Mysql 如何避免MariaDB/Galera上的队列表出现死锁?
我有一个基本上是先进先出队列的数据库表。行被系统的其他部分简单地插入到表中,然后被遗忘。每5分钟运行一次作业以处理队列中的项目。要处理的每一行的状态字段都已从挂起值更改为处理值。队列中的后续重复项将被匹配并标记为正在处理的先前排队项的重复项。除了系统中盲目插入行的部分之外,队列处理器作业是对表执行任何操作的唯一对象 这正是处理器对队列执行的操作:Mysql 如何避免MariaDB/Galera上的队列表出现死锁?,mysql,deadlock,mariadb,galera,Mysql,Deadlock,Mariadb,Galera,我有一个基本上是先进先出队列的数据库表。行被系统的其他部分简单地插入到表中,然后被遗忘。每5分钟运行一次作业以处理队列中的项目。要处理的每一行的状态字段都已从挂起值更改为处理值。队列中的后续重复项将被匹配并标记为正在处理的先前排队项的重复项。除了系统中盲目插入行的部分之外,队列处理器作业是对表执行任何操作的唯一对象 这正是处理器对队列执行的操作: START TRANSACTION; SELECT id FROM api_queue WHERE status=:status_processin
START TRANSACTION;
SELECT id
FROM api_queue
WHERE status=:status_processing
-- Application checks this result set is empty, then...
UPDATE api_queue qs
INNER JOIN api_queue qdupes ON qdupes.products_id=qs.products_id AND qdupes.action=qs.action
SET qdupes.status = IF(qs.id=qdupes.id, :status_processing, :status_processing_duplicate)
WHERE qs.id IN (:queue_ids) ;
COMMIT;
-- Each queue item is processed
-- Once processing is complete, we purge the queue
START TRANSACTION;
SELECT COUNT(*) AS total FROM api_queue WHERE status = :status_processing ;
-- Application sanity checks the number of processing items it's about to delete against how many it's processed, and then...
DELETE FROM api_queue WHERE status IN (:status_processing, :status_processing_duplicate) ;
COMMIT;
在一个典型的5分钟内,队列将建立大约100个项目的待办事项,尽管如果目录中发生了大量更改,有时可能会有数千个项目
当第一个事务没有遇到死锁(0.1-0.2秒完成)时,它通常非常快,但它似乎在大约10%的时间里遇到死锁
为什么它经常遇到死锁?即使事务锁定了表中当前的所有行,当向表中添加新行时,我是否应该认为这会导致争用?若然,原因为何
我还注意到,有时上面的第一个事务(包含UPDATE
查询)看起来根本不适用——尽管我认为这可能是一个无关的bug
我的队列表如下所示:
CREATE TABLE IF NOT EXISTS `api_queue` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`products_id` int(11) NOT NULL,
`action` tinyint(3) NOT NULL,
`triggered_by` tinyint(3) NOT NULL,
`status` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
我的咒语是:“不要排队,只管去做。”。我之所以这样说,是因为我看到MySQL中实现了太多的队列,这些队列由于某种原因失败了。一个常见的原因是,插入/检查/删除项目的开销可能与“仅仅完成任务”一样昂贵。那么,为什么要加倍成本呢?而且,很明显,排队造成了额外的死锁
根据您提供的信息,系统应能每5分钟处理1500-3000次。这应该可以处理“100”到“数千”的问题
您的排队机制似乎过于复杂,因为它涉及到连接
和其他不只是1-in,1-out的东西
假设到目前为止您拒绝我的评论,我将继续评论代码
SELECT ... FOR UPDATE
这两个选项可能都需要
DELETE
旁边的SELECT
可能与DELETE
合并为多表DELETE
。或者,也可以将其以及相关代码从事务中取出。(更快的事务不太可能死锁。)
您在提交后检查错误(死锁等),是吗?那是Galera被击中的时候
在(…)
中使用时,对元素进行排序。底层代码可能是按照
元素中的顺序锁定行。这可能会将死锁转化为高达innodb\u lock\u wait\u timeout
秒的延迟。(这种延迟没有死锁那么“糟糕”。)
当事务出现死锁时,重复该事务,对吗?(这是处理死锁的简单方法。)
编辑(在中)
如果一个线程正在执行更新。。。其中id位于(11,22)
中,另一个正在执行更新。。。其中id位于(22,11)
,并且每一行都锁定了一行,然后试图锁定另一行是一个死锁——必须回滚
。相反,如果两者都说(11,22),那么(最坏的情况下)一方将不得不等待(但不会陷入僵局)。我假设,在没有证据的情况下,InnoDB代码不足以在某种程度上避免死锁中的——通过对数字排序、原子锁定或其他方式。(我认为cleaver=速度较慢,因此不值得为这种罕见的情况做任何事情。)我的格言是:“不要排队,做就行了”。我之所以这样说,是因为我看到MySQL中实现了太多的队列,这些队列由于某种原因失败了。一个常见的原因是,插入/检查/删除项目的开销可能与“仅仅完成任务”一样昂贵。那么,为什么要加倍成本呢?而且,很明显,排队造成了额外的死锁
根据您提供的信息,系统应能每5分钟处理1500-3000次。这应该可以处理“100”到“数千”的问题
您的排队机制似乎过于复杂,因为它涉及到连接
和其他不只是1-in,1-out的东西
假设到目前为止您拒绝我的评论,我将继续评论代码
SELECT ... FOR UPDATE
这两个选项可能都需要
DELETE
旁边的SELECT
可能与DELETE
合并为多表DELETE
。或者,也可以将其以及相关代码从事务中取出。(更快的事务不太可能死锁。)
您在提交后检查错误(死锁等),是吗?那是Galera被击中的时候
在(…)
中使用时,对元素进行排序。底层代码可能是按照
元素中的顺序锁定行。这可能会将死锁转化为高达innodb\u lock\u wait\u timeout
秒的延迟。(这种延迟没有死锁那么“糟糕”。)
当事务出现死锁时,重复该事务,对吗?(这是处理死锁的简单方法。)
编辑(在中)
如果一个线程正在执行更新。。。其中id位于(11,22)
中,另一个正在执行更新。。。其中id位于(22,11)
,并且每一行都锁定了一行,然后试图锁定另一行是一个死锁——必须回滚
。相反,如果两者都说(11,22),那么(最坏的情况下)一方将不得不等待(但不会陷入僵局)。我假设,在没有证据的情况下,InnoDB代码不足以在某种程度上避免死锁中的这种死锁