diff --git a/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index ed08ca8..a0b85f2 100644 --- a/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -1661,17 +1661,49 @@ class Adapter implements WeChatServiceInterface $offset = 0; - $limit = 2000; + // 降低单批规模,减少长事务导致的锁冲突窗口 + $limit = 500; $usleepTime = 50000; + $maxRetry = 5; + // 在会话级别降级隔离级别,避免 SELECT 阶段持有共享锁导致与业务更新互斥 + Db::execute("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED"); do { - $affected = Db::execute($sql, [$offset, $limit]); - $offset += $limit; + $retry = 0; + while (true) { + try { + $affected = Db::execute($sql, [$offset, $limit]); + break; + } catch (\Throwable $e) { + $retry++; + if (!$this->isDeadlockOrLockWaitTimeout($e) || $retry >= $maxRetry) { + throw $e; + } + // 指数退避,给冲突事务让路 + usleep(100000 * $retry); + Log::warning("syncTrafficSourceGroup 批处理发生锁冲突,正在重试: offset={$offset}, retry={$retry}, error=" . $e->getMessage()); + } + } + if ($affected > 0) { + $offset += $limit; usleep($usleepTime); } } while ($affected > 0); } + /** + * 判断是否为死锁或锁等待超时 + */ + protected function isDeadlockOrLockWaitTimeout(\Throwable $e): bool + { + $msg = strtolower($e->getMessage()); + return strpos($msg, 'deadlock found') !== false + || strpos($msg, 'lock wait timeout exceeded') !== false + || strpos($msg, '1213') !== false + || strpos($msg, '1205') !== false + || strpos($msg, '40001') !== false; + } + // ============================================================================ // V2 流量池数据同步方法 // ============================================================================