一、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”
# 视图里甚至没开事务
from django.db import connection
def stmt_timeout_demo(request):
with connection.cursor() as c:
c.execute("SET SESSION ob_query_timeout = 1_000_000") # 1 s
c.execute("SELECT SLEEP(3)") # 单条 SQL 执行 3 s → 4012
return HttpResponse("ok")
现象:SLEEP(3) 还没跑完,OceanBase 直接返回 4012,Django 抛 OperationalError,事务不存在,照样杀。
2. 事务超时——掐“begin 到 commit”的总时长
from django.db import transaction
import time
@transaction.atomic
def trx_timeout_demo(request):
from app.models import Order
Order.objects.filter(id=1).update(status=1) # 0.1 s
time.sleep(11) # 业务睡了 11 s
Order.objects.filter(id=2).update(status=1) # 事务已存活 > 10 s → 4012
现象:第 2 条 update 刚发出去就收到 4012,Django 自动回滚整个事务,再抛 OperationalError。
四、本地复现完整脚本
把下面代码保存为 test_4012.py,python manage.py shell 里直接跑:
import os, django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings')
django.setup()
from django.db import connection
def trigger_4012():
with connection.cursor() as cur:
# 1. 把两条阈值都改成 1 s(单位 µs)
cur.execute("SET SESSION ob_query_timeout = 1_000_000")
cur.execute("SET SESSION ob_trx_timeout = 1_000_000")
print("阈值已设为 1 s")
# 2. 开启事务
cur.execute("BEGIN")
# 3. 故意跑 3 s,触发语句超时
try:
cur.execute("SELECT SLEEP(3)")
except Exception as e:
print("语句超时 →", e)
# 4. 再试一次,触发事务超时
try:
cur.execute("SELECT 1")
except Exception as e:
print("事务超时 →", e)
finally:
cur.execute("ROLLBACK")
if __name__ == '__main__':
trigger_4012()
运行结果示例
阈值已设为 1 s
语句超时 → (4012, 'Timeout')
事务超时 → (4012, 'Timeout')
两条红线一次体验齐活。
五、如何调大阈值
-- 全局生效(需 OB 超管)
SET GLOBAL ob_query_timeout = 60_000_000; -- 60 s
SET GLOBAL ob_trx_timeout = 86400_000_000; -- 24 h
或者只在 Django 当前连接生效:
'OPTIONS': {
'init_command': "SET ob_query_timeout=60_000_000, ob_trx_timeout=86400_000_000",
}
六、一句话总结
4012 不是 Django 的锅,而是 OceanBase 的两条“红线”:
- 语句超时掐单条 SQL 执行时长;
- 事务超时掐 begin→commit 总时长。
搞清谁先撞线,定位改阈值还是拆事务,下次再见到 4012 就能秒解。