1つのスレッドに対して1つのテーブル行を明示的に選択する方法を探しています。約50の並列プロセスで動作するクローラーを作成しました。すべてのプロセスは、テーブルから1行を取り出して、それを処理する必要があります。
CREATE TABLE `crawler_queue` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`url` text NOT NULL,
`class_id` tinyint(3) unsigned NOT NULL,
`server_id` tinyint(3) unsigned NOT NULL,
`proc_id` mediumint(8) unsigned NOT NULL,
`prio` tinyint(3) unsigned NOT NULL,
`inserted` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `proc_id` (`proc_id`),
KEY `app_id` (`app_id`),
KEY `crawler` (`class_id`,`prio`,`proc_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
今、私のプロセスは次のことを行います。
- DBトランザクションを開始します
- 次のような選択を行います
SELECT * FROM crawler_queue WHERE class_id=2 AND prio=20 AND proc_id=0 ORDER BY id LIMIT 1 FOR UPDATE
- 次に、この行を次のように更新します
UPDATE crawler_queue SET server_id=1,proc_id=1376 WHERE id=23892
- トランザクションのコミット
これは、他のプロセスがまだ処理されている行を取得できないようにするのに役立ちます。選択したショーでEXPLAINを行う
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE crawler_queue ref proc_id,crawler proc_id 3 const 617609 Using where
ただし、ログに2種類のエラー/警告が表示されることがあるため(5分ごと)、プロセスによって並列処理が高すぎるように見えます。
mysqli::query(): (HY000/1205): Lock wait timeout exceeded; try restarting transaction (in /var/www/db.php l
ine 81)
mysqli::query(): (40001/1213): Deadlock found when trying to get lock; try restarting transaction (in /var/www/db.php line 81)
私の質問は、これらのロックの問題を最小限に抑えるために、誰かが私を正しい方向に向けることができるかということです。(本番状態では、並列処理は現在の3〜4倍になるため、ロックの問題がはるかに増えると思います)
ヒントによるSELECT
インデックスを使用するように変更しました。私の問題はもうlockwaitタイムアウトです(デッドロックが消えました)。crawler
USE INDEX(crawler)
EXPLAIN
現在のUSE INDEX()
ショーで(テーブルに含まれるデータが増えたため、行数が増えました):
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE crawler_queue ref proc_id,crawler crawler 5 const,const,const 5472426 Using where