如何在CodeIgniter 3.x中处理MySQL死锁错误?常规try-catch无效
CodeIgniter 3.x 处理MySQL死锁错误的可行方案
常规try-catch失效的核心原因是:CodeIgniter 3.x的数据库类默认不会抛出异常,而是将错误信息存储在$this->db->error()中,直接用try-catch无法捕获死锁错误。以下是具体解决办法:
1. 临时开启错误检查+死锁重试
先关闭数据库自动调试,手动检查错误码(MySQL死锁错误码为1213),遇到死锁时重试操作:
// 启动事务 $this->db->trans_start(); // 临时关闭自动调试,避免CI直接输出错误 $this->db->db_debug = FALSE; $retryCount = 3; $success = FALSE; while ($retryCount > 0 && !$success) { // 执行你的数据库操作(示例:更新数据) $this->db->update('your_table', $updateData, $whereCondition); $dbError = $this->db->error(); if (empty($dbError['code'])) { // 操作成功 $success = TRUE; } elseif ($dbError['code'] != 1213) { // 非死锁错误,直接抛出异常 throw new Exception($dbError['message'], $dbError['code']); } $retryCount--; if (!$success) { // 等待随机时长(100-500ms),避免重复冲突 usleep(rand(100000, 500000)); } } if ($success) { $this->db->trans_complete(); } else { $this->db->trans_rollback(); throw new Exception('多次重试后仍未解决死锁'); } // 恢复默认调试设置(根据你的配置调整) $this->db->db_debug = TRUE;
2. 封装通用重试函数
把死锁重试逻辑封装成可复用函数,减少重复代码:
/** * 带死锁重试的数据库事务执行函数 * @param callable $callback 要执行的数据库操作闭包 * @param int $retryTimes 最大重试次数 * @throws Exception */ function run_with_deadlock_retry($callback, $retryTimes = 3) { $this->db->trans_start(); $this->db->db_debug = FALSE; $success = FALSE; $attempts = 0; while ($attempts < $retryTimes && !$success) { try { // 执行传入的数据库操作 call_user_func($callback); $dbError = $this->db->error(); if (empty($dbError['code'])) { $success = TRUE; } elseif ($dbError['code'] != 1213) { throw new Exception($dbError['message'], $dbError['code']); } } catch (Exception $e) { $this->db->trans_rollback(); throw $e; } $attempts++; if (!$success) { usleep(rand(100000, 500000)); } } if ($success) { $this->db->trans_complete(); } else { $this->db->trans_rollback(); throw new Exception('死锁重试失败,已回滚事务'); } $this->db->db_debug = TRUE; } // 使用示例 $this->run_with_deadlock_retry(function() { $this->db->update('order', ['status' => 'paid'], ['id' => $orderId]); $this->db->insert('payment_log', ['order_id' => $orderId, 'amount' => $amount]); });
3. 从根源减少死锁发生
除了重试,优化业务逻辑和数据库操作能降低死锁概率:
- 缩短事务执行时间:不要在事务中执行非数据库操作(如API调用、复杂计算)
- 统一表访问顺序:所有事务都按相同顺序操作表(比如先操作订单表,再操作商品表),避免交叉锁
- 使用行级锁:尽量用带主键/唯一索引的WHERE条件(如
UPDATE ... WHERE id = ?),减少锁范围 - 避免长时间持有锁:事务中不要等待用户输入或外部响应
内容的提问来源于stack exchange,提问作者Ras4U
相关产品推荐
相关产品推荐

