V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
推荐学习书目
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 · Jul 3, 2018 · 5559 views
    This topic created in 2858 days ago, the information mentioned may be changed or developed.
     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 replies    2018-07-04 14:37:40 +08:00
    gotounix
        1
    gotounix  
       Jul 3, 2018
    你弄混了,execute 用的是 connection 而不是 session。
    lolizeppelin
        2
    lolizeppelin  
       Jul 3, 2018 via Android
    with session begin
    lcqtdwj
        3
    lcqtdwj  
    OP
       Jul 3, 2018
    @gotounix session 有 execute
    lcqtdwj
        4
    lcqtdwj  
    OP
       Jul 3, 2018
    @lolizeppelin 获取 session 时候里面有 begin
    gotounix
        5
    gotounix  
       Jul 3, 2018
    @lcqtdwj
    看看文档里的第一段话:
    http://docs.sqlalchemy.org/en/rel_1_0/core/connections.html
    session 里的 execute 也是调用 connection 去执行的。
    lcqtdwj
        6
    lcqtdwj  
    OP
       Jul 3, 2018
    @gotounix 关键是没有注释的写法报错,不是 execute 问题
    zeq
        7
    zeq  
       Jul 4, 2018 via Android
    大胆猜测一下, 是不是其他地方调用了 os.fork() ?
    lcqtdwj
        8
    lcqtdwj  
    OP
       Jul 4, 2018   ❤️ 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 的调用>
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   807 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 20:34 · PVG 04:34 · LAX 13:34 · JFK 16:34
    ♥ Do have faith in what you're doing.