sqlalchemy 的事物加锁问题,求助!

2018-07-03 20:12:35 +08:00
 lcqtdwj
 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 执行就会正常。求助!

4855 次点击
所在节点    Python
8 条回复
gotounix
2018-07-03 20:40:11 +08:00
你弄混了,execute 用的是 connection 而不是 session。
lolizeppelin
2018-07-03 20:53:17 +08:00
with session begin
lcqtdwj
2018-07-03 21:26:18 +08:00
@gotounix session 有 execute
lcqtdwj
2018-07-03 21:26:36 +08:00
@lolizeppelin 获取 session 时候里面有 begin
gotounix
2018-07-03 21:43:15 +08:00
@lcqtdwj
看看文档里的第一段话:
http://docs.sqlalchemy.org/en/rel_1_0/core/connections.html
session 里的 execute 也是调用 connection 去执行的。
lcqtdwj
2018-07-03 21:54:19 +08:00
@gotounix 关键是没有注释的写法报错,不是 execute 问题
zeq
2018-07-04 03:13:25 +08:00
大胆猜测一下, 是不是其他地方调用了 os.fork() ?
lcqtdwj
2018-07-04 14:37:40 +08:00
找到原因了,原来 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 的调用>

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/467886

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX