0

我有两个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
4

1 回答 1

1

这应该避免锁定表tbl_servers

DELIMITER $$
CREATE PROCEDURE GET_JOB(serverName VARCHAR(50))
BEGIN
    DECLARE jobId INTEGER DEFAULT NULL;

    -- get the server id's
    CREATE TEMPORARY TABLE tmp_srvID 
    SELECT id
    INTO srvID 
    FROM tbl_servers
    WHERE name = serverName;

    SELECT id 
    INTO jobId 
    FROM tbl_jobs 
    WHERE state='runnable'

      AND server_id IN ( SELECT id FROM tmp_srvID ) 

    ORDER BY job_id ASC 
    LIMIT 1
    FOR UPDATE;

    DROP TABLE tmp_srvID;

    IF IFNULL(jobId, 0) = 0 THEN
        SELECT 0;
    ELSE
        UPDATE tbl_jobs SET state='running' WHERE job_id=jobId;
        SELECT jobId;
    END IF;
END $$
于 2013-01-10T10:51:07.987 回答