Please note that InnoDB has 2 types of exclusive locks: one for updating and deleting, and the other for inserting. Thus, in order to execute a SELECT FOR UPDATE transaction, InnoDB must first take a lock for updating in one transaction, then the second transaction will try to make the same lock and block waiting for the first transaction (it could not succeed, as you stated in the question), then when the first transaction tries to execute INSERT, it will have to change its lock from lock to update to lock to insert. The only way InnoDB can do this is to first lower the lock to general, and then update it to lock for insertion. And he cannot lower the lock when there is another transaction awaiting the acquisition of an exclusive lock. Therefore, in this situation, you get a deadlock error.
The only way to do this correctly is to have a unique index in col, try to insert a row with col = 4 (you can put dummy data if you do not want to generate it before INSERT), then in case of duplicate rollback of a key error, and if INSERT is successful You can UPDATE a row with the correct data. Please note that if you do not want to incur the cost of creating data unnecessarily, this probably means that the generation takes a lot of time, and all this time you will keep an open transaction that inserted a row with col = 4, which will hold all other processes trying to insert the same row. I'm not sure if this will be much better than generating data first and then pasting it.
source share