V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
lcqtdwj
V2EX  ›  Python

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

  •  
  •   lcqtdwj ·
    jiffies · 2018-07-03 20:12:35 +08:00 · 4824 次点击
    这是一个创建于 2336 天前的主题,其中的信息可能已经有所发展或是发生改变。
     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 执行就会正常。求助!

    8 条回复    2018-07-04 14:37:40 +08:00
    gotounix
        1
    gotounix  
       2018-07-03 20:40:11 +08:00
    你弄混了,execute 用的是 connection 而不是 session。
    lolizeppelin
        2
    lolizeppelin  
       2018-07-03 20:53:17 +08:00 via Android
    with session begin
    lcqtdwj
        3
    lcqtdwj  
    OP
       2018-07-03 21:26:18 +08:00
    @gotounix session 有 execute
    lcqtdwj
        4
    lcqtdwj  
    OP
       2018-07-03 21:26:36 +08:00
    @lolizeppelin 获取 session 时候里面有 begin
    gotounix
        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 去执行的。
    lcqtdwj
        6
    lcqtdwj  
    OP
       2018-07-03 21:54:19 +08:00
    @gotounix 关键是没有注释的写法报错,不是 execute 问题
    zeq
        7
    zeq  
       2018-07-04 03:13:25 +08:00 via Android
    大胆猜测一下, 是不是其他地方调用了 os.fork() ?
    lcqtdwj
        8
    lcqtdwj  
    OP
       2018-07-04 14:37:40 +08:00   ❤️ 1
    找到原因了,原来 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 的调用>
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2925 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 12:41 · PVG 20:41 · LAX 04:41 · JFK 07:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.