Difference between isolation levels in locktable implementation
In just MySQL, one would do this:

START TRANSACTION;
SELECT @bal := balance FROM acct WHERE id=123  FOR UPDATE;
SET @bal = @bal - @amount;
UPDATE acct SET balance = @bal WHERE id = 123;
COMMIT;


The FOR UPDATE declares the intention to change the row(s) found.  This keeps other threads away -- not via deadlock, but via a delay.

I don't know what is added by lockRecord().  Maybe it is a replacement for FOR UPDATE.  If so, it is over-kill since it grabs the row for longer than the MySQL code does.

I don't think transaction_isolation_level matter, but I am not sure.

A deadlock can occur when you grab multiple rows in a transaction while another transaction is grabbing the same rows in a different order.  In some situations, the "gap" next to a row counts, and can participate in a deadlock.