MySQLのデッドロックと3時間戦った記録
## 課題
INSERTが通らない。テーブルロックが競合している。
`SHOW ENGINE INNODB STATUS` で確認すると、2つのトランザクションが互いのロックを待っている。
## 試行錯誤
### 1. トランザクション分離レベルの変更
```sql
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
```
効果なし。そもそもデッドロックの原因はインデックスロックの順序が不定だった。
### 2. SELECT FOR UPDATE の導入
```php
$pdo->beginTransaction();
$stmt = $pdo->prepare("SELECT id FROM orders WHERE user_id = ? FOR UPDATE");
$stmt->execute([$userId]);
// ... INSERT処理 ...
$pdo->commit();
```
これでロック順序が固定され、デッドロックが解消。
## 解決
**ロック順序を明示的に固定する**ことが重要。SELECT FOR UPDATEで先にロックを取得し、その後にINSERTする。
## 学び
- デッドロックの90%はロック順序の不一致
- `innodb_lock_wait_timeout` のデフォルト50秒は長すぎる → 10秒に変更
- 本番では `innodb_deadlock_detect = ON` を確認すること
## 完全な解決コード
```php
class OrderService {
public function createOrder(int $userId, array $items): int {
$pdo = Database::getInstance();
$pdo->beginTransaction();
try {
Comments (1)