zoukankan      html  css  js  c++  java
  • 【错误记录】flask mysql 死锁

    最近使用flask-sqlalchemy时,进行测试的时候发现日志中打印出了MySql死锁错误,查看Mysql日志发现是因为有俩条sql出现了死锁:

    Deadlock found when trying to get lock; try restarting transaction

    查看方法是执行:show engine innodb statusG

    具体原因分析:

     1. 涉及的俩个sql:

    update ks_saei_instances set status = :status where last_worktime < :now - :timeout ;
    update ks_saei_instances set request_num = request_num+1 where instance_id = :instanceid ;

    2. 这俩个sql死锁的很奇怪,因为第二sql只会锁住一条记录,且只加一次锁(记录X锁) , 所以,应该只是会锁等待而不会死锁

    后面换了一种实现,当然就不会有死锁了。

    3.其实本人对上下文的所有sql都做了分析,里面没有相关的sql了(就是说没有操作这个表的sql),所以这个问题先这样了。。。。

    附mysql加锁分析

    首先,mysql不是对记录加锁,而是对索引加锁

    其次,针对不同索引和隔离级别加不同的锁,参考这里:http://hedengcheng.com/?p=771

    1. 如果字段为主键索引,隔离级别为RC(Read Committed)

      只会在主键所在的记录的索引项加一个X锁

    2. 如果字段为唯一索引,隔离级别为RC

      X锁住字段对应的唯一索引项,然后X锁住该记录对应的主键索引项(聚簇索引,如果没有主键,会自动生成隐含的聚簇索引)

    3. 如果字段为非唯一索引,隔离级别为RC

      X锁住字段值对应的所有索引项,然后X锁住这些记录上对应的主键索引项

    4. 如果字段没有索引,隔离级别为RC

      所有的记录都被加上一个X锁

    5. 如果字段为主键,隔离级别为RR(Repeatable Read)

      只会在主键所在的记录的索引项加一个X锁

    6. 如果字段为唯一索引,隔离级别为RR

      X锁住字段对应的唯一索引项,然后X锁住该记录对应的主键索引项(聚簇索引,如果没有主键,会自动生成隐含的聚簇索引)

    7. 如果字段为非唯一索引,隔离级别为RR

      对每一个符合字段值的索引添加索引项的X锁,然后在GAP上加GAP锁,最后在聚簇索引项加一个X锁,下一个也是如此做

    8. 如果字段没有索引,隔离级别为RR

      所有的记录都被加上一个X锁,所有的GAP(记录数+1)都会加一个GAP锁。这对并发有很大影响(杜绝所有的并发 更新/删除/插入 操作)

    我们根据什么来判断哪些字段会加锁?以下出现“?”的地方的字段都需要加锁

    快照读:简单的select操作,属于快照读,不加锁。(当然,也有例外,下面会分析)
      select * from table where ?;

    当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。
      select * from table where ? lock in share mode;
      select * from table where ? for update;
      insert into table values (…);
      update table set ? where ?;  -- 这里set后面的?不会加锁,如果理解错误请指正
      delete from table where ?;
    所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。

    注意:

    一个Update操作的具体流程。当Update SQL被发给MySQL后,MySQL Server会根据where条件,读取第一条满足条件的记录,然后InnoDB引擎会将第一条记录返回,并加锁 (current read)。待MySQL Server收到这条加锁的记录之后,会再发起一个Update请求,更新这条记录。一条记录操作完成,再读取下一条记录,直至没有满足条件的记录为止。因此,Update操作内部,就包含了一个当前读。同理,Delete操作也一样。Insert操作会稍微有些不同,简单来说,就是Insert操作可能会触发Unique Key的冲突检查,也会进行一个当前读。

    注:针对一条当前读的SQL语句,InnoDB与MySQL Server的交互,是一条一条进行的,因此,加锁也是一条一条进行的。先对一条满足条件的记录加锁,返回给MySQL Server,做一些DML操作;然后在读取下一条加锁,直至读取完毕。

    索引创建基本原则:

    1. 频繁作为查询条件的字段适合创建索引

    2. 唯一性太差的字段不适合建立索引(例如状态字段,它的值可能只有几个,因此不适合做索引)

    3. 不要为频繁更新的字段创建索引,因为当你这样做了之后,后续的更新都会重建这个字段的索引

    4. 不会出现在where条件中的字段不该建立索引

    5. 如果经常同时搜索两列或多列或按两列或多列排序时,索引也很有帮助。例如,如果经常在同一查询中为姓和名两列设置判据,那么在这两列上创建多列索引将很有意义

    注意自己的SQL是否使用了索引:MySQL何时使用索引、何时不使用索引。《待续》

  • 相关阅读:
    Java SE
    Java SE
    Java SE
    Java SE
    Vocabulary: dockyard
    Vocabulary: Intercept
    Java SE
    MVG
    相关学习
    相机开发
  • 原文地址:https://www.cnblogs.com/pengyusong/p/5813153.html
Copyright © 2011-2022 走看看