在MySql中插入后触发的竞争条件

时间:2012-12-14 16:55:00

标签: mysql triggers race-condition

我有一个mysql触发器,我担心在几天前将触发器部署到我的生产站点之后在我的数据库中发现了一些“未同步”的数据。

为了在这里发布,我简化了我的代码。

共有3个表格:

得分表:ID,UserID,得分,GameID(用户每次玩游戏时输入一个分数,他们可以多次玩同一个游戏)

score_summary 表:ID,UserID,GameID,SummaryValue(此表为每个游戏保留每个用户的运行得分)

game_summary 表:ID,GameID,SummaryValue(此表保持每个游戏的总体得分)

当用户在分数表中输入游戏分数时,触发器会在表score_summary中更新用户的运行总分(SummaryValue),并更新给定GameID的game_summary表。

CREATE TRIGGER scores_insert_trigger AFTER INSERT ON scores
FOR EACH ROW BEGIN

    UPDATE scores_summary
    SET SummaryValue=SummaryValue + NEW.Score 
    WHERE UserID=NEW.UserID
    SELECT ROW_COUNT() INTO rowCount;
    IF (rowCount=0) THEN
        INSERT INTO scores_summary
        (UserID, GameID, SummaryValue)
        VALUES
        (NEW.UserID, NEW.GameID, NEW.Score);
    END IF;

    UPDATE game_summary
    SET SummaryValue=SummaryValue + NEW.Score 
    WHERE GameID=NEW.GameID
    SELECT ROW_COUNT() INTO rowCount;
    IF (rowCount=0) THEN
        INSERT INTO game_summary
        (GameID, SummaryValue)
        VALUES
        (NEW.GameID, NEW.Score);
    END IF;
END;

我担心的是,如果有多个用户同时输入分数,触发器会遇到竞争状态 - 特别是当特定游戏没有分数时更新game_summary - 如果有两个用户尝试这个同时,他们都会得到rowCount = 0然后都会插入?

我的担忧是否合理,如果有的话,我可以做些什么呢?

先谢谢你们。

2 个答案:

答案 0 :(得分:2)

只需在UNIQUE上创建game_summary.GameID索引,或将其设为主键。

UNIQUE索引将阻止第二个用户添加重复行。整个事务将失败并将被回滚,因此您的应用程序需要处理此异常,例如在失败之后再次尝试放弃之前。这种解决方案的缺点是,在非常繁忙的环境中,这将导致许多冲突和许多回滚,但您可以确定不会有任何重复的行。

您还可以使用GET_LOCK(str, timeout)函数获取给定GameID的独占锁定,更新或插入值,最后RELEASE_LOCK(str),以便下一位用户也可以更新分数。在某些情况下,这可能会更有效,但您需要检查它。

答案 1 :(得分:0)

我想为我的具体情况提供一个答案。

我的更新语句是原子更新,这意味着mysql引擎可以处理任何潜在的竞争条件。

为了避免插入时的竞争条件,我选择了ON DUPLICATE KEY UPDATE,它将再次成为原子,并且将避免两个或多个线程导致多行被创建。

感谢您的帮助。