safe: at each txn commit, the data is flushed to the disk (happens every block)
fast: the OS is left to make the decision when to flush to disk
fastest: the OS is left to make the decision, not sure the difference with fast. I think it also avoids explicitely flushing medadata, but I'm not 100% sure.
sync vs async: sync will wait for a flush to be done before continuing, while async will continue right after issuing the sync instruction, not waiting until the OS reports the operation is finished.
nblocks_per_sync is how often to request a flush. The higher the number, the longer monero will work before asking for a flush. This speeds things up, unless you start having so much queued up that you start swapping. However, it increases the danger of corruption if your database is on a flaky OS/hardware.
On a spinning HDD, actual hardware writes are very slow, so you want to keep the data cached as much as you can before hitting the disk.