Mysql锁
介绍
- 按照锁的粒度来说,mysql主要包含三种类型的锁机制
全局锁:锁的是整个database,由mysql的sql layer层实现的
表级锁:锁的是某张表,由mysql的sql layer层实现的
行锁:锁的是某行数据,也可能锁定行之间的间隙。有某些存储引擎实现,比如INNODB
- 按照锁的功能来说分为:共享读锁和排他写锁
- 按照锁的实现方式分为:悲观锁和乐观锁(使用某一版本列或者唯一列进行逻辑控制)
- 表级锁和行级锁的区别:
表级锁:开销小,加锁快,不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低
行级锁:开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度最高
表级锁
由mysql sql layer层实现
- Mysql的 表级锁有两种
一种是表锁
一种是元数据锁(meta data lock mdl)
- 表锁有两种表现形式
表共享读锁:(table read lock)
表独占写锁:(table write lock)
- 手动加锁
lock table 表名 read/write
- 查看锁情况
show open tables
- 删除锁
unlock tables
MDL
元数据锁
MDL不需要显式使用,在访问一个表的时候会被自动加上,MDL的作用是,保证读写的正确性,你可以想象,如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做了变更,删除了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的
因此,在mysql5.5版本中引入了MDL,当对一个表做增删改查操作的时候,加MDL读锁,当要对表做结构变更操作的时候,加MDL写锁
- 读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查
- 读写锁之间,写锁之间是互斥的,用来保证变更表结构操作的安全性,因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行
行级锁
Mysql的行级锁,是由存储引擎来实现的,利用存储引擎锁住索引项来实现的
- INNODB的行级锁,按照锁定的范围来说,分为三种:
记录锁(Record Locks):锁定索引中一条记录
间隙锁(Gap Locks):要么锁住索引记录中间的值,要不锁住第一个索引记录前面的值或者最后一个索引记录后面的值
Next-key Locks:是索引记录上的记录锁和索引之间的间隙锁的组合
- INNODB的行锁,按照功能来说,分为两种:RR
共享锁:允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁
排它锁:允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁
对于UPDATE, DELETE和INSERT语句,INNODB会自动给涉及数据集加排它锁
对于普通的SELECT语句,INNODB不会加任何锁
手动加共享锁:
select * from table_name where .... LOCK IN SHARE MODE
手动加排它锁:
select * from table_name where ... FOR UPDATE
- INNODB也实现了表级锁,也就是意向锁,意向锁是mysql内部使用,不需要用户干预
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁
意向排它锁(IX):事务打算给数据行加行排它锁,事务在给一个数据行加排它锁前必须先取得该表的IX锁
- 意向锁和行锁可以共存,意向锁的主要作用是为了全表更新数据时的性能提升,否则在全表更新数据时,需要先检索该表是否某些记录上面有行锁
共享锁(s) | 排他锁(X) | 意向共享锁(IS) | 意向排它锁(IX) | |
共享锁(s) | 兼容 | 冲突 | 兼容 | 冲突 |
排他锁(X) | 冲突 | 冲突 | 冲突 | 冲突 |
意向共享锁(IS) | 兼容 | 冲突 | 兼容 | 兼容 |
意向排它锁(IX) | 冲突 | 冲突 | 兼容 | 兼容 |
- INNODB行锁是通过给索引上的索引项加锁来实现的,因此INNODB这种行锁实现特点意味着:只有通过索引条件检索的数据,INNODB才是用行级锁,否则,INNODB将使用表锁
行读锁实例
session1:
BEGIN; SELECT id,name FROM userinfo WHERE id = 1 lock in SHARE MODE;##加读锁 ROLLBACK; SHOW STATUS LIKE 'innodb_row_lock%'; ##展示锁状态
session2:
UPDATE userinfo SET name = '213123' where id = 2; ##可以执行 UPDATE userinfo SET name = '213123' where id = 1; ##等待释放锁
行读锁升级为表锁实例
session1:
BEGIN; SELECT id,name FROM userinfo WHERE name = 'wangwu' lock in SHARE MODE;##加读锁 ROLLBACK;
session2:
UPDATE userinfo SET name = '213123' where id = 2; ##等待释放锁
行写锁
session1:
BEGIN; SELECT id,name FROM userinfo WHERE id = 1 for update;##加写锁 ROLLBACK;
session2:
SELECT id,name FROM userinfo WHERE id = 1; ## 可以访问 SELECT id,name FROM userinfo WHERE id = 1 lock in SHARE MODE;##加读锁,阻塞,等待session1释放
间隙锁
表中没有id=4的数据
session1:
BEGIN; SELECT id,name FROM userinfo WHERE id > 2 AND id < 5 lock in SHARE MODE; ROLLBACK;
session2:
INSERT INTO userinfo(id,name,age) VALUES(4,'123132123',33);##阻塞,等待session1释放 UPDATE userinfo SET name = '213123123' where id = 3;;##阻塞,等待session1释放