Ano*_*don 29 mysql deadlock database-deadlocks
我有一个大约有5,000,000行的MySQL表,通过DBI连接的并行Perl进程以小的方式不断更新.该表有大约10列和几个索引.
一个相当常见的操作有时会产生以下错误:
DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction at Db.pm line 276.
Run Code Online (Sandbox Code Playgroud)
触发错误的SQL语句是这样的:
UPDATE file_table SET a_lock = 'process-1234' WHERE param1 = 'X' AND param2 = 'Y' AND param3 = 'Z' LIMIT 47
Run Code Online (Sandbox Code Playgroud)
该错误仅在有时触发.我估计只有1%或更少的电话.然而,它从未发生在一个小桌子上,随着数据库的增长而变得越来越普遍.
请注意,我正在使用file_table中的a_lock字段来确保我运行的四个几乎相同的进程不会尝试在同一行上工作.该限制旨在将他们的工作分解成小块.
我没有在MySQL或DBD :: mysql上做太多调整.MySQL是标准的Solaris部署,数据库连接设置如下:
my $dsn = "DBI:mysql:database=" . $DbConfig::database . ";host=${DbConfig::hostname};port=${DbConfig::port}";
my $dbh = DBI->connect($dsn, $DbConfig::username, $DbConfig::password, { RaiseError => 1, AutoCommit => 1 }) or die $DBI::errstr;
Run Code Online (Sandbox Code Playgroud)
我在网上看到其他几个人报告了类似的错误,这可能是一个真正的僵局.
我有两个问题:
究竟是什么情况导致上述错误?
有一种简单的方法来解决它或减少它的频率?例如,我究竟如何"在Db.pm第276行重新启动交易"?
提前致谢.
zom*_*bat 72
如果您正在使用InnoDB或任何行级事务RDBMS,则任何写入事务都可能导致死锁,即使在完全正常的情况下也是如此.较大的表,较大的写入和较长的事务块通常会增加发生死锁的可能性.在你的情况下,它可能是这些的组合.
真正处理死锁的唯一方法是编写代码以期望它们.如果您的数据库代码写得很好,这通常不是很困难.通常,您可以放置try/catch查询执行逻辑,并在发生错误时查找死锁.如果你抓到一个,通常要做的就是尝试再次执行失败的查询.
我强烈建议您在MySQL手册中阅读本页.它有一系列的事情要做,以帮助应对死锁并降低其频率.
Ros*_*oss 10
答案是正确的,但是关于如何处理死锁的perl文档有点稀疏,可能会与PrintError,RaiseError和HandleError选项混淆.似乎不是使用HandleError,而是使用Print和Raise,然后使用Try:Tiny之类的东西来包装代码并检查错误.下面的代码给出了一个示例,其中db代码位于while循环中,该循环将每3秒重新执行一次错误的sql语句.catch块获取$ _,这是特定的错误消息.我把它传递给一个处理函数"dbi_err_handler",它检查$ _对多个错误,如果代码应该继续(从而打破循环)则返回1,如果它是死锁则返回0并且应该重试...
$sth = $dbh->prepare($strsql);
my $db_res=0;
while($db_res==0)
{
$db_res=1;
try{$sth->execute($param1,$param2);}
catch
{
print "caught $_ in insertion to hd_item_upc for upc $upc\n";
$db_res=dbi_err_handler($_);
if($db_res==0){sleep 3;}
}
}
Run Code Online (Sandbox Code Playgroud)
dbi_err_handler应至少具有以下内容:
sub dbi_err_handler
{
my($message) = @_;
if($message=~ m/DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction/)
{
$caught=1;
$retval=0; # we'll check this value and sleep/re-execute if necessary
}
return $retval;
}
Run Code Online (Sandbox Code Playgroud)
您应该包含您希望处理的其他错误,并根据您是要重新执行还是继续来设置$ retval.
希望这有助于某人 -
小智 8
请注意,如果您SELECT FOR UPDATE在插入之前使用执行唯一性检查,则除非启用该innodb_locks_unsafe_for_binlog选项,否则每个竞争条件都会出现死锁.检查唯一性的无死锁方法是盲目地将行插入具有唯一索引的表中INSERT IGNORE,然后检查受影响的行计数.
将以下行添加到my.cnf文件中
innodb_locks_unsafe_for_binlog = 1
#1 - 开
0 - 关