Deadlock — взаимная блокировка, когда две транзакции ждут друг друга и ни одна не может продолжить. Транзакция A заблокировала строку 1 и ждёт строку 2, а транзакция B заблокировала строку 2 и ждёт строку 1.
Tx A: UPDATE accounts SET balance=0 WHERE id=1 -- lock row 1
Tx B: UPDATE accounts SET balance=0 WHERE id=2 -- lock row 2
Tx A: UPDATE accounts SET balance=0 WHERE id=2 -- wait for B...
Tx B: UPDATE accounts SET balance=0 WHERE id=1 -- wait for A... DEADLOCK!
PostgreSQL автоматически определяет дедлоки и убивает одну из транзакций. Как избежать: всегда блокируй строки в одном порядке (например, по ID), используй короткие транзакции, применяй FOR UPDATE SKIP LOCKED для очередей.