2 つの mysql テーブルがあります。
tbl_jobs (
id INTEGER PRIMARY KEY,
status ENUM("runnable", "running", "finished"),
server_id INTEGER
)
tbl_servers (
is INTEGER PRIMARY KEY,
name VARCHAR(50)
)
接続するクライアントがたくさんあり、特定のサーバーに対して1つのジョブを取り、runnable
それをに設定しrunning
ます。
これが私が現在行っている方法です(ストアドプロシージャで):
DELIMITER $$
CREATE PROCEDURE GET_JOB(serverName VARCHAR(50))
BEGIN
DECLARE jobId INTEGER DEFAULT NULL;
SELECT id FROM tbl_jobs j INNER JOIN tbl_servers s on j.server_id=s.id
WHERE j.state='runnable' AND s.server_name=serverName
ORDER BY job_id ASC LIMIT 1
INTO jobId FOR UPDATE;
IF IFNULL(jobId, 0) = 0 THEN
SELECT 0;
END IF;
UPDATE tbl_jobs SET state='running' WHERE job_id=jobId;
SELECT jobId;
END $$
それは問題なく動作しますが、同時クライアントの数が多く (数百) になると、大きなロックの輻輳が発生することがわかりtbl_servers
ます。ステートメントがすべてのテーブルをロックすることは理解していFOR UPDATE
ますが、実際にはtbl_servers
読み取り専用テーブルとして使用しています。
質問: でのロックの輻輳 (つまり、まったくロック) を回避するにはどうすればよいtbl_servers
ですか?
私が考えることができる 1 つのことは、クエリを 2 つに分割することです。最初にサーバー名を id に変換してからクエリのみを実行しますtbl_jobs
が、実際のアプリケーションでは、1 つのサーバー名に多くの ID を含めることができます (奇妙に聞こえることはわかっていますが、ここでは説明のために私のアプリケーションを簡略化しています)。したがって、2 番目のクエリにtbl_jobs
は準備済みステートメントが必要です。
もっとエレガントな解決策があると確信しています。
システム:
- Amazon RDS 上の MySQL 5.1.67
- すべてのテーブルは InnoDb です
- インデックスを持っています
tbl_jobs.server_id