Django 1.11.49中原生SQL与模型查询的单事务读取问题
问题解决:并发场景下跨表查询的数据一致性问题
问题原因
你的核心问题出在数据库事务隔离级别和查询的一致性快照上:
- 默认数据库隔离级别为「读已提交」,即使在同一个事务内,后续查询会读取到其他已提交事务的最新数据。
- API先通过原生SQL读取Table A的旧数据,此时后台事务完成了A、B表的删改并提交,后续用旧外键查询Table B时,旧的B记录已被删除,自然无结果。
- 之前仅用
transaction.atomic()包裹查询无效,是因为没有调整隔离级别,事务内的查询仍会看到外部提交的修改。
解决方案
要让两次查询共享同一数据快照,不受后台更新影响,需要将事务隔离级别设置为「可重复读」,并确保原生SQL与Django ORM使用同一个事务连接。
方案1:原生SQL+ORM结合(保留原生查询)
from django.db import transaction, connection with transaction.atomic(): # 设置事务隔离级别为可重复读(适配PostgreSQL/MySQL等主流数据库) with connection.cursor() as cursor: cursor.execute("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;") # 原生SQL查询Table A query = "SELECT * FROM A WHERE status = 1;" cursor.execute(query) results = cursor.fetchall() list_of_values = get_values_for_table_B(results) # ORM查询Table B,自动复用当前事务的连接和数据快照 b_records = list(B.objects.filter(values__in=list_of_values))
方案2:全ORM查询(更简洁,推荐)
如果可以替换原生SQL为Django ORM,代码会更简洁,且事务一致性更易维护:
from django.db import transaction with transaction.atomic(): # 设置可重复读隔离级别 transaction.set_autocommit(False) try: # ORM查询Table A的活跃数据 a_records = A.objects.filter(status=1) list_of_values = get_values_for_table_B(a_records.values()) # 基于同一快照查询Table B b_records = list(B.objects.filter(values__in=list_of_values)) finally: transaction.set_autocommit(True)
关键说明
- 「可重复读」隔离级别会让事务内的所有查询都基于事务启动时的数据快照,不会看到其他事务在事务期间提交的修改,完美解决你的并发一致性问题。
- 后台进程的
transaction.atomic()用法是正确的,原子性删改A、B表不会破坏数据关联。
内容的提问来源于stack exchange,提问作者Avijit Saxena
相关产品推荐
相关产品推荐

