SQLAlchemy多线程下事务隔离机制详解
1. 起因
通过开启多线程,并发查询订单详情信息,通过将不同订单对象发送给不同线程,执行完所需要的业务逻辑之后,对订单对象的数据进行修改,然后执行 commit,查询数据库发现数据没有更新,且后台日志没有任何的报错
**错误代码:**
from app.ext import db from flask import current_app from concurrent.futures import ThreadPoolExecutor def do_something(order, app): with app.app_context(): order.status = ‘running‘ db.session.commit() def main(): orders = Order.query.fitery_by(status=‘created‘).all() app = current_app._get_current_object() with ThreadPoolExecutor(max_workers=10) as executor: for order in orders: executor(do_something, order, app)
2. 排查
2.1 开启SQLAlchemy 中打印SQL参数
app.config["SQLALCHEMY_ECHO"] = True
2.2 查看日志
通过日志发现在主线程中查询 orders 的时候,可以打印SQL,但在新开启的线程执行到 db.session.commit() 时,没有对应SQL打印。
**初步定位是线程传递 order 的问题,SQLAlchemy 事务应该不允许跨线程。**
3. 解决方案
**在开启的线程中,通过传递的订单ID获取订单对象**
from app.ext import db from flask import current_app from concurrent.futures import ThreadPoolExecutor def do_something(order_id, app): with app.app_context(): order = Order.query.get(order_id) order.status = ‘running‘ db.session.commit() def main(): orders = Order.query.fitery_by(status=‘created‘).all() app = current_app._get_current_object() with ThreadPoolExecutor(max_workers=10) as executor: for order in orders: executor(do_something, order.id, app)
4. 总结
当生成 Session 对象的时,这个对象并不是线程安全的,是一个本地线程对象(thread local storage),因此当跨线程时,就不在此 session 范围内了,从而导致对象无法提交。这样也避免了多线程、多进程情况下,污染其他线程或进程数据。
5. 参考
相关推荐
liuweiq 2020-06-14
emmm00 2020-06-14
朱建伟 2020-05-14
猛禽的编程艺术 2020-05-06
苦咖啡flask 2020-05-04
dreamhua 2020-04-30
dreamhua 2020-04-30
tanrong 2020-04-30
xuanlvhaoshao 2020-04-26
tanrong 2020-04-18
Danialzhou 2020-04-11
happinessaflower 2020-03-01
xiaoxiangyu 2020-02-23
喝绿茶的猫 2020-01-18
疯狂老司机 2020-01-10
liuyang000 2020-01-08
Lingforme 2020-01-08
阿亮 2019-12-26