MariaDB 避免死锁

Posted

技术标签:

【中文标题】MariaDB 避免死锁【英文标题】:MariaDB Avoid Deadlocks 【发布时间】:2017-12-17 04:22:20 【问题描述】:

我原来的错误是

错误号:1213 - 尝试获取锁时发现死锁;尝试 重启交易

好的,所以我写了一个循环,最大重试次数,中间有一个等待来尝试解决死锁。

$Try = 0;
while (!$Result = $dbs->query($mysql)) 
    $Try++;
    if ($Try === MYSQL_MAX_RETRIES)
        HandleMySQLError($dbs->error, $MySQL, false, $Test, $Trace);
    else 
        sleep(MYSQL_RETRY_WAIT);

但现在我仍然不断收到一些原始错误,以及一个新错误

在提交期间出现错误 35“避免了资源死锁”

但我似乎真的不知道这意味着什么或如何解决它?


编辑

我第一次写这篇文章时遗漏了大量信息,但服务器是 Galera 和 MariaDB 集群中的 RedHat 7 AWS EC2(好吧,其中 3 个)。

我正在运行的查询是对存储过程的调用

call`getchatmessages`('<ChatID>','<UserID>',from_unixtime('<Some Timestamp>'));

存储过程如下

CREATE DEFINER=`root`@`%` PROCEDURE `getchatmessages`(IN `__ChatID` CHAR(36), IN `__UserID` CHAR(36), IN `__Timestamp` TIMESTAMP(6))
BEGIN

DECLARE `__NewChatMessages` TINYINT(1) DEFAULT 0;
DECLARE `__i` INT(11) DEFAULT 0;

DECLARE `__Interval` INT(11) DEFAULT 100; -- ms
DECLARE `__Timeout` INT(11) DEFAULT 15000; -- ms

while `__NewChatMessages`=0 and `__i`<`__Timeout`/`__Interval` do
    select 1 into `__NewChatMessages` from `chatmessages` where `ChatID`=`__ChatID` and `DateTimeAdded`>ifnull(`__Timestamp`,0) limit 1;
    update `chatusers` set `DateTimeRead`=now(6) where `ChatID`=`__ChatID` and `UserID`=`__UserID`;
    do sleep(`__Interval`/1000);
    set `__i`=`__i`+1;
end while;

select `chatmessages`.`Body`, `chatmessages`.`ChatID`, `chatmessages`.`UserID`, 
`chatmessages`.`ChatMessageID`, `chatmessages`.`DateTimeAdded`, UNIX_TIMESTAMP(`chatmessages`.`DateTimeAdded`) `Timestamp`, `users`.`FirstName`,
`users`.`LastName`
from `chatmessages` 
join `users` using (`UserID`) 
where `chatmessages`.`ChatID`=`__ChatID` 
and `chatmessages`.`DateTimeAdded`>ifnull(`__Timestamp`,0) 
order by `chatmessages`.`DateTimeAdded` desc
limit 100;

END

【问题讨论】:

查询内容是什么? @ethrbunny 是一个存储过程,我已经把调用和过程加到帖子末尾了 .. 这些表上没有任何触发器或 FK? @ethrbunny 表上没有任何正在更新的触发器,但几乎每个涉及的表上肯定有多个外键,外键会导致集群问题吗? 【参考方案1】:

Galera Cluster 中的死锁(MariaDB Galera Cluster,3 个节点)不是典型的死锁,而是多主冲突的一种沟通方式:

http://galeracluster.com/documentation-webpages/dealingwithmultimasterconflicts.html

避免死锁的最简单方法是一次写入 1 个节点,即将 HA 代理配置为仅写入 1 个节点。在您的情况下,您将在 Node1 上运行 sp(不管哪个节点,但总是在 1 个节点上,有点“粘性会话”)。

更多信息在这里:https://severalnines.com/blog/avoiding-deadlocks-galera-set-haproxy-single-node-writes-and-multi-node-reads

【讨论】:

看来你可能是对的,我目前正在使用 AWS ELB,所以我认为它不能像 HA 代理那样只保留一个用于写入,如果只启用我的一个实例但在 ELB 中它确实有效。看起来我必须设置一个或两个 HA 代理节点。我会回来看看我的结果 您可以尝试(仅用于测试)直接连接到 1 个节点并运行您的测试 我已经设置了这个并且可以与 HA 代理一起正常工作,我修改了我的应用程序以将不以“select”开头的所有内容发送到第一个节点,然后循环其余的,我没有自从遇到问题 除了一些选择查询在插入数据后立即选择数据(例如获取最后一个自动递增的数字,例如发票编号,使用发票 ID 选择)和选择命中数据尚未传播到的节点,我可以选择发送到写入连接的节点【参考方案2】:

这个 Proc 是否在事务中被调用?如果是这样,我强烈反对它的设计。你有一个循环,睡眠挂在事务上。

相反,让UPDATE 本身成为一个事务。

这实际上可以消除死锁。但是,正如其他答案所讨论的那样,您仍然应该处理死锁。

编辑由于没有BEGINsautocommit=ON,OP 已经在遵循这个建议。唉。

【讨论】:

除非在创建连接时隐含事务并存在于 php 脚本的其余部分,否则我的印象是循环中的调用是它自己的事务,因为我从未明确启动或结束任何事务交易,尽管我的假设可能是错误的 你没有BEGIN 并且autocommit = 1? 不,我不应该吗? 我认为BEGIN 的存在/不存在以及autocommit 的值对于找出死锁至关重要。 SHOW VARIABLES LIKE 'autocommit';。 PHP 脚本是你的,还是第三方软件? 哦,我的错,是的,自动提交是开启的,而且我没有任何开始,这完全是我的脚本(这是一个非常大的脚本,>~8,000 行)

以上是关于MariaDB 避免死锁的主要内容,如果未能解决你的问题,请参考以下文章

如何避免死锁

java如何避免死锁

如何避免死锁

避免死锁的方法

进程死锁与避免

mysql 开发进阶篇系列 14 锁问题(避免死锁,死锁查看分析)