MySQL 全局锁、表级锁、行级锁,你搞清楚了吗?( 三 )

  • 然后 , 线程 B 也执行了同样的 select 语句 , 此时并不会阻塞 , 因为「读读」并不冲突;
  • 接着 , 线程 C 修改了表字段 , 此时由于线程 A 的事务并没有提交 , 也就是 MDL 读锁还在占用着 , 这时线程 C 就无法申请到 MDL 写锁 , 就会被阻塞 , 
  • 那么在线程 C 阻塞后 , 后续有对该表的 select 语句 , 就都会被阻塞 , 如果此时有大量该表的 select 语句的请求到来 , 就会有大量的线程被阻塞住 , 这时数据库的线程很快就会爆满了 。
    为什么线程 C 因为申请不到 MDL 写锁 , 而导致后续的申请读锁的查询操作也会被阻塞?
    这是因为申请 MDL 锁的操作会形成一个队列 , 队列中写锁获取优先级高于读锁 , 一旦出现 MDL 写锁等待 , 会阻塞后续该表的所有 CRUD 操作 。
    所以为了能安全的对表结构进行变更 , 在对表结构变更前 , 先要看看数据库中的长事务 , 是否有事务已经对表加上了 MDL 读锁 , 如果可以考虑 kill 掉这个长事务 , 然后再做表结构的变更 。
    意向锁接着 , 说说意向锁 。
    • 在使用 InnoDB 引擎的表里对某些记录加上「共享锁」之前 , 需要先在表级别加上一个「意向共享锁」;
    • 在使用 InnoDB 引擎的表里对某些纪录加上「独占锁」之前 , 需要先在表级别加上一个「意向独占锁」;
    也就是 , 当执行插入、更新、删除操作 , 需要先对表加上「意向独占锁」 , 然后对该记录加独占锁 。
    而普通的 select 是不会加行级锁的 , 普通的 select 语句是利用 MVCC 实现一致性读 , 是无锁的 。
    不过 , select 也是可以对记录加共享锁和独占锁的 , 具体方式如下:
    //先在表上加上意向共享锁 , 然后对读取的记录加共享锁select ... lock in share mode;//先表上加上意向独占锁 , 然后对读取的记录加独占锁select ... for update;意向共享锁和意向独占锁是表级锁 , 不会和行级的共享锁和独占锁发生冲突 , 而且意向锁之间也不会发生冲突 , 只会和共享表锁(

    经验总结扩展阅读