PostgreSQL: deadlock detected
PostgreSQLで複数トランザクションが互いにロックを待ち合うデッドロックエラーの原因と解決策
概要
複数のトランザクションが互いにロックを待ち合う状態になり、自動的に一方がロールバックされるエラーです。
エラーメッセージ
``` ERROR: deadlock detected DETAIL: Process 12345 waits for ShareLock on transaction 67890; blocked by process 54321. Process 54321 waits for ShareLock on transaction 12345; blocked by process 12345. HINT: See server log for query details. ```
原因
- トランザクション内でのロック順序不一致: 複数のテーブルを異なる順序で更新
- 長時間のトランザクション: ロック保持時間が長すぎる
- 明示的ロックの競合: SELECT FOR UPDATEの多用
解決策
1. ロック順序を統一する
```sql – 悪い例: 順序がバラバラ – トランザクション1: UPDATE users SET … WHERE id = 1; UPDATE orders SET … WHERE id = 2; – トランザクション2: UPDATE orders SET … WHERE id = 2; UPDATE users SET … WHERE id = 1;
– 良い例: 常に同じ順序 BEGIN; UPDATE orders SET status = ‘completed’ WHERE id = 2; UPDATE users SET last_order = NOW() WHERE id = 1; COMMIT; ```
2. トランザクションを短くする
```python
悪い例
with db.transaction(): process_order(order_id) # 重い処理 update_user_stats(user_id) send_notification(user_id) # 外部API呼び出し
良い例
process_order(order_id) send_notification(user_id) with db.transaction(): update_user_stats(user_id) ```
3. 行ロックを最小化
```sql – 悪い例: 広範囲ロック SELECT * FROM orders WHERE status = ‘pending’ FOR UPDATE;
– 良い例: SKIP LOCKEDで競合回避 SELECT * FROM orders WHERE status = ‘pending’ FOR UPDATE SKIP LOCKED LIMIT 10; ```
よくある間違い
- リトライロジックを実装しない
- トランザクション内で外部API呼び出し
- 必要以上に大きなトランザクション
関連エラー
関連エラー
PostgreSQL の他のエラー
この記事は役に立ちましたか?