Learnitweb

How to avoid deadlocks in database transaction?

What is a Deadlock?

A deadlock occurs when two or more transactions are waiting for each other to release locks, and neither can proceed. Oracle automatically detects deadlocks and kills one transaction, but that’s a last resort.

Example of a Deadlock:

  • Tx1: locks Row A, then tries to lock Row B (but it’s already locked by Tx2)
  • Tx2: locks Row B, then tries to lock Row A

They’re both stuck — deadlock.

Best Practices to Avoid Deadlocks in Oracle

1. Always Access Tables and Rows in the Same Order

If your transactions touch multiple tables or rows, ensure that all processes follow the same locking order.

Transaction 1: LOCK TABLE departments IN EXCLUSIVE MODE; UPDATE departments ...; LOCK TABLE employees IN EXCLUSIVE MODE; UPDATE employees ...; COMMIT;

Transaction 2: LOCK TABLE departments IN EXCLUSIVE MODE; UPDATE departments ...; LOCK TABLE employees IN EXCLUSIVE MODE; UPDATE employees ...; COMMIT;

2. Keep Transactions Short and Focused

Keep your BEGIN...COMMIT blocks tight:

  • Do not include user input delays or long loops inside transactions
  • Avoid heavy computation within a transaction
@Transactional
public void updateAccounts() {
    // do DB updates here
    // avoid sleep() or long business logic
}

3. Use Appropriate Isolation Level

Oracle uses read-consistent architecture, but too high an isolation level (like SERIALIZABLE) increases the risk of deadlocks.

Use READ COMMITTED (default in Oracle) unless your logic truly needs more strict isolation.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

4. Avoid Select For Update Unless Needed

SELECT ... FOR UPDATE is useful but dangerous if misused — it locks rows even during reads.

-- Use only when truly needed
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;

If two sessions do this on different rows, and then try to update each other’s rows — deadlock.

5. Retry Logic in Application Code

Implement retry logic for ORA-00060: deadlock detected errors.

In Spring Boot:

@Retryable(
    value = {DeadlockLoserDataAccessException.class},
    maxAttempts = 3,
    backoff = @Backoff(delay = 500)
)
@Transactional
public void updateCriticalData() {
    // update logic
}

You can also catch and handle SQLException with errorCode == 60.

6. Index Foreign Keys

Unindexed foreign keys are a common cause of deadlocks. When you delete from a parent table, Oracle may lock the child table if the FK isn’t indexed.

Always index foreign key columns!

CREATE INDEX idx_fk_customer_id ON orders(customer_id);

7. Avoid Manual Locks (if possible)

Don’t use DBMS_LOCK, LOCK TABLE, or custom locking unless absolutely necessary. These can force serialization and increase chances of deadlock.

8. Monitor with Deadlock Trace File

Oracle generates trace files for each deadlock. Check:

SHOW PARAMETER user_dump_dest;
-- Check the trace file for "deadlock detected"

Use it to analyze and fix the locking sequence.