Eddie780
Eddie780
2021-07-20 20:41
采纳率: 100%
浏览 183

pymysql中select……for update不锁行问题


conn = pymysql.connect(...)
lsc = conn.cursor()
conn.begin()
lsc.execute("select * from user where uid=93442 for update;")
lsonerow = lsc.fetchone()
print("现有金钱:", lsonerow[0])
time.sleep(10)
conn.commit()
lsc.close()
print("完整执行完成")

引擎是InnoDB,uid是user表主键。
以上代码理应查到数据后锁行,十秒后放开锁,锁期间其他增删改行为被阻塞。
但测试发现,在等待十秒期间,其他的终端依然可以update!
像是conn.begin()根本没起作用?

数据库应该没问题,因为尝试过开2个终端,终端A执行
begin;
select * from user where uid=93442 for update;
之后再使用终端B尝试update,能够进入阻塞,直至终端A执行commit后才解除阻塞。

img

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

6条回答 默认 最新

  • weixin_42433970
    Jason Ho 2021-07-21 09:48
    已采纳

    SQL语句:

    SELECT ... FOR UPDATE [OF column_list][WAIT n|NOWAIT][SKIP LOCKED];
    详解:

    OF 子句用于指定即将更新的列,即锁定行上的特定列。 
    WAIT 子句指定等待其他用户释放锁的秒数,防止无限期的等待。
    

    示例:

    SELECT * FROM 'account' FOR UPDATE
    会等待行锁释放之后,返回查询结果。

    SELECT * FROM 'account' FOR UPDATE NOWAIT
    不等待行锁释放,提示锁冲突,不返回结果

    SELECT * FROM 'account' FOR UPDATE WAIT 5
    等待5秒,若行锁仍未释放,则提示锁冲突,不返回结果

    SELECT * FROM 'account' FOR UPDATE SKIP LOCKED
    查询返回查询结果,但忽略有行锁的记录

    “使用FOR UPDATE WAIT”子句的优点如下:
      1防止无限期地等待被锁定的行;
      2允许应用程序中对锁的等待时间进行更多的控制。
      3对于交互式应用程序非常有用,因为这些用户不能等待不确定
      4 若使用了skip locked,则可以越过锁定的行,不会报告由wait n 引发的‘资源忙’异常报告

    简单场景:

    【钱包扣钱】事务中:

    //事务
    SELECT balance FROM 'account' WHERE id = 1 FROM UPDATE
    //判断钱包是否大于需要扣除的金额,例如100
    UPDATE 'account' SET balance = balance - 100 WHERE id =1
    //提交事务
    在未提交事务之前,其他人使用for update语句查询这个时候会出现被锁住,无法被读取。保证准确性

    SELECT ... FOR UPDATE 的Row Lock 与Table Lock

    只有「明确」的指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。

    示例:

    表account 其中主键为id

    SELECT * FROM 'account' WHERE id='3' FOR UPDATE;
    有主键,并且有此数据,row lock

    SELECT * FROM 'account' WHERE id='-1' FOR UPDATE;
    主键,若查无此数据,无lock

    SELECT * FROM 'account' WHERE name='小树' FOR UPDATE;
    无主键,table lock

    SELECT * FROM 'account' WHERE id<>'3' FOR UPDATE;
    SELECT * FROM 'account' WHERE id>'3' FOR UPDATE;
    SELECT * FROM 'account' WHERE id LIKE '3' FOR UPDATE;

    点赞 评论
  • Eddie780
    Eddie780 2021-07-20 20:42

    https://ask.csdn.net/questions/7476977?expend=true
    两帖子一起结,谢谢大佬们

    点赞 评论
  • java2000_net
    老紫竹 2021-07-21 15:10

    set autocommit=0
    或者
    start transation
    自己主动打开事务,确认事务可用。
    至于是否py的问题,和我们用户有啥关系。

    点赞 评论
  • m0_58562487
    $$鸡蛋酱$$ 2021-07-21 21:22

    你看一下这个帖子,我感觉应该有用
    https://blog.csdn.net/zhwbqd/article/details/17056447

    点赞 评论
  • qq_35501764
    曦陽惜夏 2021-07-22 13:27

    先执行打开事务
    conn.begin()
    lsc.execute(“start transation“)
    lsc.execute("select * from user where uid=93442 for update;")

    点赞 评论
  • renminzdb
    renminzdb 2021-07-22 14:36

    这个锁,是数据库提供的,跟应用代码是没有关系的。

    点赞 评论

相关推荐