大牛总结的MySQL锁优化,写得太好了( 三 )

  • 另一方面,也是为了满足恢复和复制的需要 。
  •  
    大牛总结的MySQL锁优化,写得太好了

    文章插图
     
     
    间隙锁图
    死锁
    两个事务都需要获得对方持有的排他锁才能继续完成任务,这种互相等待对方释放资源的情况就是死锁 。
     
    大牛总结的MySQL锁优化,写得太好了

    文章插图
     
     
    【大牛总结的MySQL锁优化,写得太好了】死锁图
    检测死锁:InnoDB 存储引擎能检测到死锁的循环依赖并立即返回一个错误 。
    死锁恢复:死锁发生以后,只有部分或完全回滚其中一个事务,才能打破死锁 。
    InnoDB 方法是,将持有最少行级排他锁的事务回滚 。在应用程序设计时必须考虑处理死锁,多数情况下重新执行因死锁回滚的事务即可 。
    避免死锁:
    • 在事务开始时,如果有记录要修改,先使用 SELECT... FOR UPDATE 语句获取锁,即使这些修改语句是在后面执行 。
    • 在事务中,如果要更新记录,直接申请排他锁 。而不是查询时申请共享锁、更新时再申请排他锁 。
    这样做会导致,当申请排他锁时,其他事务可能已经获得了相同记录的共享锁,从而造成锁冲突,甚至死锁 。
    简单来说,如果你要更新记录要做两步操作,第一步查询,第二步更新 。就不要第一步上共享锁,第二部上排他锁了,直接在第一步就上排他锁,抢占先机 。
    • 如果事务需要锁定多个表,那么尽量按照相同的顺序使用加锁语句,可以降低产生死锁的机会 。
    • 通过 SELECT ... LOCK INSHARE MODE(共享锁)获取行的读锁后,如果当前事务再需要对该记录进行更新操作,则很有可能造成死锁 。所以,如果要对行记录进行修改,直接上排他锁 。
    • 改变事务隔离级别(事务隔离级别在后面详细说明) 。
    MySQL 锁定情况的查询
    在实际开发中无法避免数据被锁的问题,那么我们可以通过哪些手段来查询锁呢?
    表级锁可以通过两个变量的查询:
    • Table_locks_immediate,产生表级锁的次数 。
    • Table_locks_waited,数显表级锁而等待的次数 。
    行级锁可以通过下面几个变量查询:
    • Innodb_row_lock_current_waits,当前正在等待锁定的数量 。
    • Innodb_row_lock_time(重要),从系统启动到现在锁定总时长 。
    • Innodb_row_lock_time_avg(重要),每次等待所花平均时间 。
    • Innodb_row_lock_time_max,从系统启动到现在等待最长的一次花费时间 。
    • Innodb_row_lock_waits(重要),从系统启动到现在总共等待的次数 。
    MySQL 事务隔离级别
    前面讲的死锁是因为并发访问数据库造成 。当多个事务同时访问数据库,做并发操作的时候会发生以下问题 。
    脏读(dirty read),一个事务在处理过程中,读取了另外一个事务未提交的数据 。未提交的数据称之为脏数据 。
     
    大牛总结的MySQL锁优化,写得太好了

    文章插图
     
     
    脏读例子
    不可重复读(non-repeatable read),在事务范围内,多次查询某条记录,每次得到不同的结果 。
    第一个事务中的两次读取数据之间,由于第二个事务的修改,第一个事务两次读到的数据可能不一样 。
     
    大牛总结的MySQL锁优化,写得太好了

    文章插图
     
     
    不可重复读例子
    幻读(phantom read),是事务非独立执行时发生的一种现象 。
     
    大牛总结的MySQL锁优化,写得太好了

    文章插图
     
     
    大牛总结的MySQL锁优化,写得太好了

    文章插图
     
     
    幻读的例子
    在同一时间点,数据库允许多个并发事务,同时对数据进行读写操作,会造成数据不一致性 。
     
    大牛总结的MySQL锁优化,写得太好了

    文章插图
     
     
    四种隔离级别,解决事务并发问题对照图
    隔离性就是用来防止这种数据不一致的 。事务隔离根据级别不同,从低到高包括: