一、4012 是谁抛的?
Django 本身没有 4012 错误码,它是 OceanBase 的“杀手”信号:
当前 SQL 或 当前事务累计执行时间 ≥ 系统阈值,直接返回 4012。
二、两条红线长啥样?
| 变量名 | 默认阈值 | 计时对象 | 触发后果 |
|---|---|---|---|
| ob_query_timeout | 10 000 000 µs = 10 s | 单条 SQL 执行时长 | 这条语句被杀,事务可继续 |
| ob_trx_timeout | 100 000 000 µs = 100 s | 事务 begin→commit 总时长 | 下一条语句必 4012,事务必须回滚 |
谁先撞线谁动手,互不插队。
三、Django 视角看区别
1. 语句超时——只掐“这一条 SQL”
1# 视图里甚至没开事务 2from django.db import connection 3 4def stmt_timeout_demo(request): 5 with connection.cursor() as c: 6 c.execute("SET SESSION ob_query_timeout = 1_000_000") # 1 s 7 c.execute("SELECT SLEEP(3)") # 单条 SQL 执行 3 s → 4012 8 return HttpResponse("ok") 9
现象:SLEEP(3) 还没跑完,OceanBase 直接返回 4012,Django 抛 OperationalError,事务不存在,照样杀。
2. 事务超时——掐“begin 到 commit”的总时长
1from django.db import transaction 2import time 3 4@transaction.atomic 5def trx_timeout_demo(request): 6 from app.models import Order 7 Order.objects.filter(id=1).update(status=1) # 0.1 s 8 time.sleep(11) # 业务睡了 11 s 9 Order.objects.filter(id=2).update(status=1) # 事务已存活 > 10 s → 4012 10
现象:第 2 条 update 刚发出去就收到 4012,Django 自动回滚整个事务,再抛 OperationalError。
四、本地复现完整脚本
把下面代码保存为 test_4012.py,python manage.py shell 里直接跑:
1import os, django 2os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings') 3django.setup() 4 5from django.db import connection 6 7def trigger_4012(): 8 with connection.cursor() as cur: 9 # 1. 把两条阈值都改成 1 s(单位 µs) 10 cur.execute("SET SESSION ob_query_timeout = 1_000_000") 11 cur.execute("SET SESSION ob_trx_timeout = 1_000_000") 12 print("阈值已设为 1 s") 13 14 # 2. 开启事务 15 cur.execute("BEGIN") 16 17 # 3. 故意跑 3 s,触发语句超时 18 try: 19 cur.execute("SELECT SLEEP(3)") 20 except Exception as e: 21 print("语句超时 →", e) 22 23 # 4. 再试一次,触发事务超时 24 try: 25 cur.execute("SELECT 1") 26 except Exception as e: 27 print("事务超时 →", e) 28 finally: 29 cur.execute("ROLLBACK") 30 31if __name__ == '__main__': 32 trigger_4012() 33
运行结果示例
1阈值已设为 1 s 2语句超时 → (4012, 'Timeout') 3事务超时 → (4012, 'Timeout') 4
两条红线一次体验齐活。
五、如何调大阈值
1-- 全局生效(需 OB 超管) 2SET GLOBAL ob_query_timeout = 60_000_000; -- 60 s 3SET GLOBAL ob_trx_timeout = 86400_000_000; -- 24 h 4
或者只在 Django 当前连接生效:
1'OPTIONS': { 2 'init_command': "SET ob_query_timeout=60_000_000, ob_trx_timeout=86400_000_000", 3} 4
六、一句话总结
4012 不是 Django 的锅,而是 OceanBase 的两条“红线”:
- 语句超时掐单条 SQL 执行时长;
- 事务超时掐 begin→commit 总时长。
搞清谁先撞线,定位改阈值还是拆事务,下次再见到 4012 就能秒解。
《Django 踩坑记:OceanBase 4012 Timeout 两条红线,语句超时 vs 事务超时一次讲透》 是转载文章,点击查看原文。