Mysql InnoDB 锁总结

锁类型

  • 排他锁(写锁、X锁)

    会阻塞其他事物读和写

  • 共享锁(读锁、S锁)

    会阻塞其他事物修改表数据

  • 意向锁

    意向锁与行锁互不排斥,意向锁之间互不排斥,意向锁与表锁互斥!

锁粒度

  • 行锁

    开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高

  • 表锁

    开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低

  • 页锁

    开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

活锁与死锁

  • 活锁

    如果事务T1封锁了数据对象R后,事务T2也请求封锁R,于是T2等待,接着T3也请求封锁R。当T1释放了加载R上的锁后,系统首先批准T3的请求,T2只能继续等待。接着T4也请求封锁R,T3释放R上的锁后,系统又批转了T4的请求。这样的一直循环下去,事务T2就只能永远等待了,这样情况叫活锁

  • 死锁

    当两个事务分别锁定了两个单独的对象,这时每一个事务都要求在另一个事务锁定的对象上获得一个锁,因此每一个事务都必须等待另一个事务释放占有的锁,这种情况叫死锁

InnoDB行锁总结

  • 行锁是针对索引的

    故某索引相同的记录都会被加锁,会造成索引竞争,这就需要我们严格设计业务sql,尽可能的使用主键或唯一索引对记录加锁。很多时候,扫描一个表,由于无索引,往往会导致整个表被锁住(不是表锁),建立合适的索引可以防止扫描整个表

  • 记录锁

    它是建立在索引记录上的锁

  • 间隙锁

    锁定一个范围,但不包括记录本身

  • next-key锁

    记录锁加间隙锁的组合。也就是说next-key锁技术包含了记录锁和间隙锁
    有时在开发过程中我们会发现,在INSERT的时候会锁定相邻的键,其实这是一个next-key锁技术,MySQL使用这个技术来避免幻读

死锁总结

  • 产生死锁的必要条件

    • 禁止抢占
    • 持有和等待
    • 互斥
    • 循环等待
    • 预防死锁就是至少破坏这4个条件中的一项
  • InnoDB处理死锁的机制

    发现有循环等待的现象,立即rollback开销更小的事务,也就是插入、修改、删除了更少记录的事务

  • 死锁预防

    • 经常提交你的事务。小事务更少地倾向于冲突。
    • 以固定的顺序访问你的表和行。这样事务就会形成定义良好的查询并且没有死锁。
    • 将精心选定的索引添加到你的表中。这样你的查询就只需要扫描更少的索引记录,并且因此也可以设置更少的锁定。
    • 不要把无关的操作放到事务里面。
    • 在并发比较高的系统中,不要显式加锁,特别是在事务里显式加锁。如SELECT…FOR UPDATE语句,如果是在事务里(运行了START TRANSACTION或设置了autocommit等于0),那么就会锁定所查找到的记录。
    • 尽量按照主键/索引去查找记录,范围查找增加了锁冲突的可能性,也不要利用数据库做一些额外的计算工作。比如有些人会用到“SELECT…WHERE…ORDER BY RAND();”这样的语句,由于类似这样的语句用不到索引,因此将导致整个表的数据都被锁住。
    • 优化SQL和表设计,减少同时占用太多资源的情况。比如说,减少连接的表,将复杂SQL分解为多个简单的SQL。