MySQL 锁

锁是一种使各种共享资源在被并发访问时变得有序,从而保证数据一致性的机制。

MySQL 中不同存储引擎支持不同的锁机制,InnoDB 支持行锁、表锁,MyISAM 只支持表锁。

表锁开销小、加锁快,不会出现死锁,但锁冲突概率大,并发度低;而行锁开销大、加锁慢,会出现死锁,但锁冲突概率小,并发度高。

InnoDB 锁类型

InnoDB 的锁类型包括读锁(共享锁)、写锁(排他锁)、MDL 锁和意向锁。

读锁

读锁,简称 S 锁,一个事务获取了一个数据行的读写,其他事务能获取该行对应的读锁,但不能获得写锁,即其他事务可以读取数据行,但不能对其进行修改。

通过 select 查询时,如果在尾部添加 lock in share mode ,会在读取的行记录或行记录的范围上加读锁,其他事务可以读,但修改操作会阻塞。不加 lock in share mode 则不会加读锁。

写锁

写锁,简称 X 锁,优先级最高,一个事务获取了一个数据行的写锁,其他事务就不能再获取该行的其他锁。

DML 语句会对行记录或行记录范围加写锁。

select ... for update 可以对读取的行记录加写锁,其他事务尝试对锁定的行加锁时会被阻塞。

MDL 锁

MDL 锁(meta data lock)在 MySQL 5.5 被引入,用于保证表中元数据的一致性。

一个会话开启事务后,会自动获取 MDL 锁,期间其他会话不可以执行任何 DDL 语句的操作。

意向锁

InnoDB 中意向锁是表级锁,分为意向共享锁和意向排他锁,作用于 MDL 锁类似,都是防止在事务进行过程中执行 DDL 语句而导致数据不一致。

意向共享锁(IS)即给数据行加共享锁前必须先取得该表的 IS 锁,意向排他锁(IX)即给数据行加排他锁前必须先取得该表的 IX 锁。

InnoDB 行锁种类

InnoDB 在默认事务隔离级别 RR 的模式下,行锁分为三种:单个行记录的锁 record lock 、间隙锁 gap lock 和 记录锁与间隙锁的组合 next-key lock。

单个行记录的锁

InnoDB 中对单个行记录的锁加在索引项上。

表 t 包含 id、name、age 三列,在事务中执行 update t set age = 10 where name = 'AAA' 。事务执行过程中,如果 name 上有索引,则会在 name = ‘AAA’ 的行上加锁,如果 name 上无索引,则会在所有行记录上加锁。

间隙锁

可重复读事务隔离级别 RR 为了避免幻读现象,引入了间隙锁 gap lock,锁定行记录数据的范围,不包含记录本身,即不允许在此范围内插入任何数据。

如在事务中查询表 t 中 age 大于 10 的数据并加共享锁(age 上有索引),其他事务插入 age 大于 10 的数据会被阻塞。

间隙锁只针对 RR 可重复读事务隔离级别有效,RC 读已提交隔离级别允许幻读现象。

Next-key Locks

Next-key Locks 是记录锁与间隙锁的组合,InnoDB 会首先对选中的索引记录加锁,再对间隙加锁。

如在事务中查询表 t 中 age 大于 10 的数据并加共享锁(age 上有索引),其他事务不但插入 age 大于 10 的数据会被阻塞,插入 age 等于 10 的数据也会被阻塞。

锁等待与死锁

锁等待指一个事务过程中获取锁,其他事务需要等待锁释放才能占用该资源。如果获取锁的事务一直不释放,其他事务会一直等待,直到超过锁等待时间。锁最长等待时间通过 innodb_lock_wait_timeout 参数控制,单位秒。

死锁指两个及以上的事务过程中,因争夺资源而造成的一种相互等待的现象,常见报错为 Deadlock found when trying to get lock; try restarting transaction。

InnoDB 可以自动检测死锁,并自动回滚造成死锁的事务。

出现锁问题时,可以通过 show full processlistshow engine innodb status 命令来判断事务中锁的情况,也可以通过 information_schema 库下的 INNODB_TRX、INNODB_LOCKS 和 INNODB_LOCK_WAITS 三张表来监控当前事务。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • © 2016-2020 姜越

谢谢老板

支付宝
微信