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-07-03 20:40:11 +08:00
你弄混了,execute 用的是 connection 而不是 session。
|
2
lolizeppelin 2018-07-03 20:53:17 +08:00 via Android
with session begin
|
4
lcqtdwj OP @lolizeppelin 获取 session 时候里面有 begin
|
5
gotounix 2018-07-03 21:43:15 +08:00
@lcqtdwj
看看文档里的第一段话: http://docs.sqlalchemy.org/en/rel_1_0/core/connections.html session 里的 execute 也是调用 connection 去执行的。 |
7
zeq 2018-07-04 03:13:25 +08:00 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 的调用> |