首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mysql并以原子方式执行存储过程或以原子方式选择更新

Mysql并以原子方式执行存储过程或以原子方式选择更新
EN

Stack Overflow用户
提问于 2021-05-06 17:58:07
回答 1查看 49关注 0票数 1

在Mysql中,我有两个并发进程,需要读取一些行并根据条件更新标志。

我必须使用transaction编写一个存储过程,但问题是有时两个进程会更新相同的行。

我有一个表Status,我想读取其中标志Reserved为真的15行,然后更新那些将标志Reserved设置为False的行。

更新后的行必须返回给客户端。

我的存储过程是:

代码语言:javascript
复制
CREATE DEFINER=`user`@`%` PROCEDURE `get_reserved`()
BEGIN
DECLARE tmpProfilePageId bigint;
DECLARE finished INTEGER DEFAULT 0;

DECLARE curProfilePage CURSOR FOR 
    SELECT ProfilePageId 
    FROM Status
    WHERE Reserved is false and ((timestampdiff(HOUR, UpdatedTime, NOW()) >= 23) or UpdatedTime is NULL)
    ORDER BY UpdatedTime ASC
    LIMIT 15;
DECLARE CONTINUE HANDLER 
    FOR NOT FOUND SET finished = 1;
    
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;

START TRANSACTION;

DROP TEMPORARY TABLE IF EXISTS TmpAdsProfile;
CREATE TEMPORARY TABLE TmpAdsProfile(Id INT PRIMARY KEY AUTO_INCREMENT, ProfilePageId BIGINT);

OPEN curProfilePage;

getProfilePage: LOOP
    FETCH curProfilePage INTO tmpProfilePageId;
    IF finished = 1 THEN LEAVE getProfilePage;
    END IF;
    UPDATE StatusSET Reserved = true WHERE ProfilePageId = tmpProfilePageId;
    INSERT INTO TmpAdsProfile (ProfilePageId) VALUES (tmpProfilePageId);
END LOOP getProfilePage;

CLOSE curProfilePage;

SELECT ProfilePageId FROM TmpAdsProfile;

COMMIT;

END

无论如何,如果我执行两个调用此存储过程的并发进程,有时它们会更新相同的行。

如何以原子方式执行存储过程?

EN

回答 1

Stack Overflow用户

发布于 2021-05-06 20:09:16

稍微简化一下,并使用FOR UPDATE。这将锁定您要更改的行,直到您提交事务。您可以完全摆脱光标。像这样的东西,没有经过调试!

代码语言:javascript
复制
START TRANSACTION;

CREATE OR REPLACE TEMPORARY TABLE TmpAdsProfile AS
SELECT ProfilePageId 
  FROM Status
 WHERE Reserved IS false 
   AND ((timestampdiff(HOUR, UpdatedTime, NOW()) >= 23) OR UpdatedTime IS NULL)
 ORDER BY UpdatedTime ASC
 LIMIT 15 
   FOR UPDATE; 

 UPDATE Status SET Reserved = true 
  WHERE ProfilePageId IN (SELECT ProfilePageId FROM TmpAdsProfile);
 
COMMIT;

SELECT ProfilePageId FROM TmpAdsProfile;

该临时表将永远只有15行。因此,索引和PKs以及所有这些都是不必要的。因此,您可以使用CREATE ... AS SELECT ...一次性创建和填充表。

并且,考虑重新转换您的UpdatedTime过滤器,以便它可以使用索引。

代码语言:javascript
复制
AND (UpdatedTime <= NOW() - INTERVAL 23 HOUR OR UpdatedTime IS NULL)

SELECT查询的适当索引为

代码语言:javascript
复制
CREATE INDEX status_update ON Status (Reserved, UpdatedTime, ProfilePageId);

SELECT操作的速度越快,事务所用的时间就越少,因此整体性能就会越好。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67415860

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档