我从全局外观数据库移出到下一个sql查询。我希望它适用于下一个算法: 1)从表格签到中选择/锁定1个条目。 2)锁定并更新usage_flag,所以我不需要担心竞争状况。
begin;
SELECT GetDistance('newhaven', area) as distance, id = (SELECT @proxy_id := id)
from checkins
WHERE last_checkin > DATE_SUB(NOW(), INTERVAL 5 MINUTE)
AND active = 1
AND offline = 0
AND usage_flag = 0
ORDER BY distance ASC, RAND() limit 1 FOR UPDATE;
UPDATE checkins set usage_flag=1 where id=@proxy_id;
commit;
但是在生产一天之后,我看到了很多错误:
'Lock wait timeout exceeded; try restarting transaction'
我如何改进此代码,或者在我的猜想中完全错误select for update
begin-commit
如何工作。请纠正我或给我一些想法,让它更好。
答案 0 :(得分:0)
您的查询基本上会锁定整个表,因为它需要所有行来计算订单。
我假设您的查询运行一段时间(否则您可能只是更新到位),因此您的表将被锁定,直到计算完所有距离为止。因此要么改善距离计算,要么在此期间不要锁定它。
为了最大限度地缩短锁定时间,您可以分离(耗时)选择和锁定更新。如果在选择期间状态发生了变化,您可以在锁外重做,然后重试。
你可以,例如使用以下程序
delimiter $$
create procedure SetUsageflag()
begin
set @LockCounter = 10;
repeat
set @LockCounter = @LockCounter - 1;
set @proxy_id =
(SELECT id from checkins
WHERE last_checkin > DATE_SUB(NOW(), INTERVAL 5 MINUTE)
AND active = 1
AND offline = 0
AND usage_flag = 0
ORDER BY GetDistance('newhaven', area) asc, RAND()
limit 1);
start transaction;
-- check if status is still 0, and lock that row
set @checkvalue = (select coalesce(usage_flag,0)
from checkins where id = @proxy_id for update);
if @checkvalue = 0 then
update checkins set usage_flag = 1 where id = @proxy_id;
set @LockCounter = 0;
-- return result to caller
SELECT GetDistance('newhaven', area) as distance, id
from checkins where id = @proxy_id;
end if ;
commit;
-- give up after some time
if @LockCounter = 1 then
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Lock-Timeout trying to find an unused checkin!', MYSQL_ERRNO = 1205;
end if;
until (@LockCounter <= 1) end repeat;
end $$
delimiter ;
如果您经常需要重做select
,则可能需要使用临时表并添加例如如果第一个条目失败,那么10个最佳条目并一个接一个地测试它们,所以你不必重做计算(我不知道需要多长时间)。
答案 1 :(得分:0)
但是你有这种结构的方式,你需要锁定整个表格 选择订单限制1必须锁定表,因为更改可能会更改查询的输出
我怀疑这很贵
GetDistance('newhaven', area)
您可以将该输出存储在列
中你真的需要什么?你只需要知道另一个进程没有设置usage_flag = 1?
我不知道mysql但是你可以得到一个行数吗?
UPDATE checkins set usage_flag=1 where id=@proxy_id and usage_flag=0;
如果另一个进程已将其更改为1,则此行的行数为0 这样可以避免(代价高昂的)交易