在数据库(如 MySQL、PostgreSQL、Oracle 等)中使用 游标(Cursor) 进行批量处理时,确实容易引发 死锁(Deadlock),尤其在高并发环境下。下面从 原因 → 设计原则 → 具体避免手段 系统说明。
常见原因包括:
所有事务必须按相同顺序访问和更新资源
❌ 错误示例:
✅ 正确:
游标中:
✅ 推荐模式:
OPEN cur;
LOOP
FETCH cur INTO rec;
EXIT WHEN cur%NOTFOUND;
UPDATE ... WHERE id = rec.id;
END LOOP;
CLOSE cur;
无序游标容易导致:
✅ 推荐:
DECLARE cur CURSOR FOR
SELECT id FROM orders
ORDER BY id;
❌ 错误:
OPEN cur;
LOOP
UPDATE ...
END LOOP;
COMMIT; -- 事务太长
✅ 正确(每 N 条提交):
DECLARE
cnt NUMBER := 0;
BEGIN
FOR rec IN cur LOOP
UPDATE ...;
cnt := cnt + 1;
IF cnt >= 1000 THEN
COMMIT;
cnt := 0;
END IF;
END LOOP;
COMMIT;
END;
❌ 危险:
SELECT * FROM orders FOR UPDATE;
✅ 安全:
SELECT id FROM orders
WHERE status = 'NEW'
ORDER BY id
FOR UPDATE;
MySQL
SET innodb_lock_wait_timeout = 5;
Oracle
ALTER SESSION SET ddl_lock_timeout = 10;
PostgreSQL
SET lock_timeout = '5s';
死锁无法 100% 避免时,应:
示例(伪代码):
retry = 3
while retry > 0:
try:
process_cursor()
break
catch Deadlock:
sleep(random)
retry -= 1
| 反模式 | 风险 |
|---|---|
| 游标内再开游标 | 交叉锁 |
| 先 SELECT 再 UPDATE | 非原子 |
| 无 ORDER BY | 随机死锁 |
| 单事务处理百万行 | 长事务 |
| 更新非主键条件 | 锁范围大 |
如果业务允许,尽量不用游标:
游标避免死锁 = 统一锁顺序 + 有序访问 + 小事务 + 短锁时间 + 可重试
如果你能告诉我:
FOR UPDATE我可以给你针对性的游标写法示例。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。