大家帮忙看看我的这个事物有什么问题,导致无法进行更新?

Carryf 2009-04-21 05:44:53
以下存储过程是为了避免不同客户端同时读到同一条记录而写的。但存在一个bug就是:insert 操作已经成功执行了,但是有可能是update没有成功执行,导致其他人在别人取出来的几分钟后还是可以取到记录(少部分数据会这样)。

比如有条id=123的资料已经给了A用户,按道理不管如何其他人都拿不到的。
但是过了几份钟后,在B用户那里竟然可以显示到id=123这个资料。


DELIMITER $$

DROP PROCEDURE IF EXISTS `crm`.`SelectCustomerData`$$

CREATE DEFINER=`root`@`localhost` PROCEDURE `SelectCustomerData`(Workid char(20))
begin
-- ---------------------------------
declare SQLCondition varchar(100);
declare Cust_id int default -1;
set SQLCondition=concat(" ifdealing=0 and staffno='' and workid='",Workid,"'");
-- 这里条件的意思是:查询属于workid组的未处理(ifdealing=0)并且工号字段为空的数据
-- 顺序取记录

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ; --设置事务为串读
START TRANSACTION ; --开始一个事物

select id into Cust_id from hr_customerdata where callstate='3';---首先取callstate=‘3’的资料,如果没有再执行以下的SQL;
if Cust_id=-1 then
select id into Cust_id from hr_customerdata where ifdealing=0 and staffno='' and workid=Workid order by id limit 1 for update;
end if;

if (Cust_id<>-1) then --如果能够取到数据,则更新状态,并把数据取出来后由存储过程返回给客户端程序。
insert into TopHis value(Cust_id);----如果这条记录被提取过了,就在这个表中插入一条数据;
update hr_customerdata set ifdealing=1 where id= Cust_id ;
end if;
set @sql=concat("SELECT * from hr_customerdata where id=",Cust_id);
PREPARE stmt1 FROM @sql ;
EXECUTE stmt1 ;
DEALLOCATE PREPARE stmt1;
--Set@sql=NULL;

commit ;
end$$

DELIMITER $$;
...全文
84 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
trainee 2009-04-25
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 trainee 的回复:]
或者

declare not found ... set aa=1

aa=0
update hr_customerdata set ifdealing=1 where id= Cust_idand ifdealing <>1;
if aa=1 then -- 没有更新
中断程序
endif

[/Quote]
假如按以上的方法
根本就不需要前面的select for update 锁定, 甚至不用start transaction
因为update 语句有内置的锁定, 不知mysql是不是这样的.

你前面的select for update锁定没有任何作用,因为锁的是过时的数据.


Carryf 2009-04-22
  • 打赏
  • 举报
回复
哈哈,我现在只想到这种方法了。不过这个我后来检查了,只是个别的。
trainee 能否提供些资料参考下?


数据库只有hr_customerdata 为InnoDB表,其他的都是myisam。
trainee 2009-04-22
  • 打赏
  • 举报
回复
或者

declare not found ... set aa=1

aa=0
update hr_customerdata set ifdealing=1 where id= Cust_id and ifdealing<>1;
if aa=1 then -- 没有更新
中断程序
endif
trainee 2009-04-22
  • 打赏
  • 举报
回复
按某个键值锁住一行, 而后又改变 这个键值

这种写法很悬呼, 平常很少这种写法, 没有仔细验证.

建议用应用锁 或者 建一个只有一行记录的表 ,加锁, 放在事务的前头.

56,678

社区成员

发帖
与我相关
我的任务
社区描述
MySQL相关内容讨论专区
社区管理员
  • MySQL
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧