
session = DBSession() #spot = session.execute("select * from spot where id=1 for update;") #spot.fetchall() send_spot = session.query(Spot).filter( Spot.id == 1).with_for_update().one() spot.name='qwerdsdfdf' #session.execute("update spot set name='ccbbbaaaaa' where id=1;") session.commit() 如上所示,想在更新时加个排它锁,但是执行结果是(1205, u'Lock wait timeout exceeded; try restarting transaction') 通过查询事物和锁的信息,发现会产生 2 个线程 2 个事物,update 语句变成单独一个事物在等待第一条的锁。可是这明明是一个事物,为什么变成 2 个了? 如果把 update 语句用 execute 执行就会正常。求助!
1 gotounix 2018 年 7 月 3 日 你弄混了,execute 用的是 connection 而不是 session。 |
2 lolizeppelin 2018 年 7 月 3 日 via Android with session begin |
4 lcqtdwj OP @lolizeppelin 获取 session 时候里面有 begin |
5 gotounix 2018 年 7 月 3 日 @lcqtdwj 看看文档里的第一段话: http://docs.sqlalchemy.org/en/rel_1_0/core/connections.html session 里的 execute 也是调用 connection 去执行的。 |
7 zeq 2018 年 7 月 4 日 via Android 大胆猜测一下, 是不是其他地方调用了 os.fork() ? |
8 lcqtdwj OP 找到原因了,原来 sqlalchemy 会在很多地方调用 flush,比如 autoflush,或者 commit->prepare->flush 的时候,而 sqlalchemy 奇葩的地方在于 flush 会强行开启一个嵌套事物,所以如果用修改 instance 的方式更新,就会触发嵌套事物,两个事物竞争,update 语句就在等待前一条的 for update 锁。解决方案是使用 ``` session=DBSession() with session.no_autoflush: spot = session.query(Spot).filter(Spot.id == 1).with_for_update().one() session.query(Spot).filter(Spot.id == 1).update({"name": '3456'}, synchronize_session=False) session.commit() ``` 避开 flush 的调用> |