A deadlock is the failure mode of lock-based concurrency: transaction A holds row X and wants row Y; transaction B holds row Y and wants row X. Each is waiting for the other; neither can proceed; without intervention, both wait forever. The database doesn't let that happen — it detects the cycle and kills one side.
The mechanics start with two-phase locking, which is what makes the window for deadlocks at all. A transaction acquires locks as it executes and holds them until commit; locks are never released early. So over the life of a transaction, the set of rows it holds grows, and two transactions operating on overlapping rows in different orders can always interleave into a cycle.
Detection is a graph problem. The database tracks a wait-for graph: nodes are transactions, and an edge from T1 to T2 means "T1 holds something T2 wants." A deadlock is a cycle in that graph. Postgres checks the graph periodically (every deadlock_timeout, default 1 second — the same parameter that doubles as lock-wait reporting) and, on finding a cycle, aborts the transaction that's cheapest to sacrifice — the one that has done the least work — and returns error 40P01: deadlock detected. MySQL with innodb_deadlock_detect = ON (default) does the same continuously. The victim's transaction is rolled back completely, and its locks are released, which unblocks the survivor. If the database didn't detect, a lock_timeout would eventually abort one waiter anyway — but that's a timeout, not a cycle-aware choice, and it can kill the wrong transaction.
The interviewer wants to know you've been hit by this. Deadlocks are nearly always an application design smell, not a database bug, and there are standard fixes. Lock ordering: if every transaction touches rows in the same order (smallest ID first, say), cycles can't form. Short transactions: the less time locks are held, the smaller the window — a transaction that does its reads, pauses for user input, then writes is a deadlock factory. Consistent index access order for joins — the optimizer's join order determines acquisition order, and different plans can lock the same rows in different orders. Unique index conflicts are the classic surprise: two transactions inserting the same key create an insert-intent wait that can cycle with other locks.
Close with the retry story: since the victim's transaction is aborted wholesale, the application must retry the entire transaction, not the failed statement — and a small randomized backoff makes the retry far less likely to cycle again. The database resolves the deadlock; the application resolves the recurrence.