在CodeIgniter 4中处理MySQL死锁问题的技术求助
解决CodeIgniter中MySQL死锁捕获与重试问题
你的问题核心在于CodeIgniter默认不会将数据库错误抛出为可捕获的Exception,所以普通的try-catch(\Exception $e)无法抓到死锁错误。下面是具体的解决步骤:
1. 开启数据库异常模式
在CodeIgniter中,需要先配置数据库连接,让它把错误以异常形式抛出。可以选择全局配置或方法内临时配置:
全局配置(database.php)
找到你的数据库配置数组,添加或修改errorMode参数(适用于CodeIgniter 4):
public $default = [ 'DSN' => '', 'hostname' => 'localhost', 'username' => 'root', 'password' => '', 'database' => 'your_db', 'DBDriver' => 'MySQLi', 'DBPrefix' => '', 'pConnect' => false, 'DBDebug' => false, // 关闭默认调试输出 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', 'encrypt' => false, 'compress' => false, 'strictOn' => false, 'failover' => [], 'port' => 3306, 'errorMode' => \CodeIgniter\Database\BaseConnection::ERRORMODE_EXCEPTION, // 关键配置 ];
方法内临时设置
如果不想全局开启异常模式,可以在updateUserBalance方法内临时设置:
$this->db->errorMode(\CodeIgniter\Database\BaseConnection::ERRORMODE_EXCEPTION);
2. 捕获数据库异常并实现死锁重试
MySQL死锁的错误码是1213,捕获到数据库异常后,通过错误码判断是否为死锁,再实现重试逻辑(示例设置最多3次重试):
public function updateUserBalance($userid, $balance, $retryCount = 3) { try { // 临时开启异常模式(若全局未配置) $this->db->errorMode(\CodeIgniter\Database\BaseConnection::ERRORMODE_EXCEPTION); return $this->db->table($this->user) ->set('balance', "balance+{$balance}", false) ->where('id', $userid) ->update(); } catch (\CodeIgniter\Database\Exceptions\DatabaseException $e) { // 判断是否为死锁错误 if ($e->getCode() == 1213 && $retryCount > 0) { log_message('error', "Deadlock occurred, retrying... (remaining attempts: {$retryCount-1})"); // 递归重试,每次重试次数减1 return $this->updateUserBalance($userid, $balance, $retryCount - 1); } // 非死锁错误或重试耗尽,记录错误并返回 log_message('error', 'Database error: ' . $e->getMessage() . ' (code: ' . $e->getCode() . ')'); return false; } }
3. CodeIgniter 3兼容方案
如果你使用CodeIgniter 3,配置和捕获逻辑略有不同:
public function updateUserBalance($userid, $balance, $retryCount = 3) { $this->db->db_debug = false; // 关闭默认调试输出 try { $result = $this->db->table($this->user) ->set('balance', "balance+{$balance}", false) ->where('id', $userid) ->update(); if (!$result) { $error = $this->db->error(); throw new \Exception($error['message'], $error['code']); } return $result; } catch (\Exception $e) { if ($e->getCode() == 1213 && $retryCount > 0) { log_message('error', "Deadlock occurred, retrying... (remaining attempts: {$retryCount-1})"); return $this->updateUserBalance($userid, $balance, $retryCount - 1); } log_message('error', 'Database error: ' . $e->getMessage() . ' (code: ' . $e->getCode() . ')'); return false; } }
内容的提问来源于stack exchange,提问作者evan
相关产品推荐
相关产品推荐

