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


另外,当一个进程请求某个 MyISAM 表的读锁,另一个进程也请求同一表的写锁 。
即使读请求先到达,写请求后到达,写请求也会插到读请求之前 。因为 MySQL 的默认设置认为,写请求比读请求重要 。
我们可以通过 low_priority_updates 来调节读写行为的优先级:

  • 数据库以读为主时,要优先保证查询性能时,可通过 low_priority_updates=1 设置读优先级高于写优先级 。
  • 数据库以写为主时,则不用设置 low_priority_updates 参数 。
InnoDB 存储引擎和表级锁
再来看看第二象限的内容:
 
大牛总结的MySQL锁优化,写得太好了

文章插图
 
 
2*2 表行锁,MyISAM,InnoDB 示意图-第二象限
InnoDB 存储引擎表锁 。当没有对数据表中的索引数据进行查询时,会执行表锁操作 。
上面是 InnoDB 实现行锁,同时它也可以实现表锁 。其方式就是意向锁(Intention Locks) 。
这里介绍两种意向锁:
  • 意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前,必须先取得该表的 IS 锁 。
  • 意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前,必须先取得该表的 IX 锁 。
注:意向共享锁和意向排他锁是数据库主动加的,不需要我们手动处理 。对于 UPDATE、DELETE 和 INSERT 语句,InnoDB 会自动给数据集加排他锁 。
InnoDB表锁的实现方式:假设有一个表 test2,有两个字段分别是 id 和 name 。
没有设置主键同时也没有设置任何索引(index)如下:
 
大牛总结的MySQL锁优化,写得太好了

文章插图
 
 
InnoDB 表锁实现方式图
InnoDB 存储引擎和行级锁
第四象限我们使用的比较多,讨论的内容也相对多些:
 
大牛总结的MySQL锁优化,写得太好了

文章插图
 
 
2*2 表行锁,MyISAM,InnoDB 示意图-第四象限
InnoDB 存储引擎行锁,当数据查询时针对索引数据进行时,会使用行级锁 。
共享锁(S):当一个事务读取一条记录的时候,不会阻塞其他事务对同一记录的读请求,但会阻塞对其的写请求 。当读锁释放后,才会执行其他事务的写操作 。
例如:select … lock in share mode
排他锁(X):当一个事务对一条记录进行写操作时,会阻塞其他事务对同一表的读写操作,当该锁释放后,才会执行其他事务的读写操作 。
例如:select … for update
行锁的实现方式:假设有一个表 test1,有两个字段分别是 id 和 name 。
id 作为主键同时也是 table 的索引(index)如下:
 
大牛总结的MySQL锁优化,写得太好了

文章插图
 
 
InnoDB 行锁实现方式图
在高并发的情况下,多个事务同时请求更新数据,由于资源被占用等待事务增多 。
如此,会造成性能问题,可以通过 innodb_lock_wait_timeout 来解决 。innodb_lock_wait_timeout 是事务等待获取资源的最长时间,单位为秒 。如果超过时间还未分配到资源,则会返回应用失败 。
四种锁的兼容情况:
 
大牛总结的MySQL锁优化,写得太好了

文章插图
 
 
共享锁,排他锁,意向共享锁,意向排他锁兼容图例
如果一个事务请求的锁模式与当前的锁兼容,InnoDB 就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放 。
间隙锁
前面谈到行锁是针对一条记录进行加锁 。当对一个范围内的记录加锁的时候,我们称之为间隙锁 。
当使用范围条件索引数据时,InnoDB 会对符合条件的数据索引项加锁 。对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB 也会对这个“间隙”加锁,这就是间隙锁 。间隙锁和行锁合称(Next-Key锁) 。
如果表中只有 11 条记录,其 id 的值分别是 1,2,...,10,11 下面的 SQL:
Select * from table_gapwhere id > 10 for update;
这是一个范围条件的检索,InnoDB 不仅会对符合条件的 id 值为 10 的记录加锁,会对 id 大于 10 的“间隙”加锁,即使大于 10 的记录不存在,例如 12,13 。
InnoDB 使用间隙锁的目的: