我有两个mysql表:
tbl_jobs (
id INTEGER PRIMARY KEY,
status ENUM("runnable", "running", "finished"),
server_id INTEGER
)
tbl_servers (
is INTEGER PRIMARY KEY,
name VARCHAR(50)
)
我有很多客户端连接,为特定服务器执行一项工作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
?
我能想到的一件事是将我的查询分成两部分 - 首先将服务器名称转换为 id 然后只查询tbl_jobs
,但在我的真实应用程序中,一个服务器名称可以有很多 id(我知道这听起来很奇怪,但我只是在这里简化了我的应用程序以进行说明)。所以第二个查询tbl_jobs
需要准备好的语句。
我确信有一个更优雅的解决方案。
系统:
- Amazon RDS 上的 MySQL 5.1.67
- 所有表都是 InnoDb
- 我确实有一个索引
tbl_jobs.server_id