锁机制
文章目录
- 锁机制
- 一、锁的介绍
- 1.1锁的分类:
- 二、死锁现象
- 2.1 第一张死锁现象
- 2.2 第二中死锁现象
- 三、事务隔离机制中的两种数据读取方式
一、锁的介绍
所谓锁就是为了保证数据安全性而设计出来的,举个例子来说就像两人在办公室同时打印文件,为了使打印的内容不混乱,必须等一人打印完以后另一个人才能打印,如果把打印机看出数据,在一个人修改数据时就相当于上了一把锁,另一个人必须等待其修改完成也就是开锁以后才能进行修改操作,这就是锁。
1.1锁的分类:
按颗粒度分,锁可以分为行级锁、表锁和页级锁。行级锁指锁一行数据,表锁会把整个表锁起来,页级锁则是锁一页数据,它介于行级锁与表锁之间。行级锁开销很大加锁速度慢,但是发生死锁冲突的概率较小,并发度最高。在mysql中只有命中索引以后才会加行级锁,如果没命中索引加的锁会把整张表都锁住。如果命中的是主键索引,则会将主键索引锁起来,如果命中的是辅助索引,则会先锁辅助索引,再锁主键索引。innodb中行级锁使用的算法是Next-key lock,它是记录锁和间隙锁的结合,也是这种算法解决了幻读的问题。
按级别分锁可以分为互斥锁和共享锁。互斥锁就是当数据被锁上以后只有自己可以使用,在自己开锁以后其他事务才能再次锁数据。共享锁也就是多个人都可以同时给一个数据上锁,但是只有当数据没有被除自己以外的人锁上时才能对数据进行修改。
在mysql中查询数据属于读操作,增删改属于写操作,执行写操作时默认会加上互斥锁,读操作则不会加上锁(除非你手动加锁)。如果想手动加互斥锁可以使用select … for update,使用共享锁可以使用select … lock in share mode。
另外需要注意的是如果事务A对数据加了共享锁,其他事务就不能对A加互斥锁了,只能加共享锁。但如果其他事务没有加上共享锁的话,事务A仍然可以对数据再加互斥锁。
总结一下就是说:
- 一旦事务A对数据加了排它锁,那么其他事务无法对数据A加任何锁,且只有事务A可以操作数据(其他事务可以对数据进行不加锁的读操作)。
- 一旦事务A对数据加了共享锁,那么其他事务只能对数据加共享锁。如果其他事务没有对数据加共享锁,那么事务A可以在对数据加上共享锁的同时加上互斥锁。
- 一旦多个事务都会数据A了共享锁,大家都只能不加锁的读不能改。
按使用方式分,锁可以分为乐观锁和悲观锁。悲观锁是一种保守的锁定策略,它假设在任何时候,多个线程或进程都可能会尝试修改同一数据项,因此必须在数据被读取时立即锁定,以防止其他线程或进程的修改。乐观锁是一种宽松的锁定策略,它假设多个线程或进程在处理数据时发生冲突的可能性较低,因此不会立即锁定数据,而是在数据提交更新时检查在读取数据后是否有其他线程或进程修改了数据。
二、死锁现象
2.1 第一张死锁现象
#开启两个事务
#事务一
begin;
#事务二
begin;
#事务一
select * from t1 where id=6 for update;
#事务二
update t1 set age=19 where id=2;
#事务一
select * from t1 where id=2 for update;
#由于事务二将id=2的记录锁住了,因此事务一进入死锁状态
#事务二
update t1 set age=19 where id=6;
#由于事务一将id=6的记录锁住了,因此事务二也进入死锁状态
#此时mysql检测到了死锁现象会强制结束事务一,因此事务二完成update操作
commit;
2.2 第二中死锁现象
此种死锁现象发生在高并发状态下。
#假设现在有如下的数据
+----+------+------+
| id | name | age |
+----+------+------+
| 1 | a | 33 |
| 2 | b | 22 |
+----+------+------+
现在有两个事务在高并发状态下同时对name和age分别上互斥锁,我们知道innodb存储索引的方式是B+树,而B+树各个节点又是有序排列的,因此可以得到name、age和id三个索引在B+树中的排序大致如下:
name | ‘a’ | ‘b’ |
age | 22 | 33 |
naid | 1 | 2 |
由于A、B两个事务是同时加上的互斥锁,因此name和age两个辅助索引会被A和B分别锁上,然后A、B会开始锁主键索引。A先锁上’a’对应的主键索引1,此时B会锁上22对应的主键索引2,然后A开始锁’b’对应的主键索引2发现B已经上了锁,同时B开始锁33对应的主键索引1发现A已经上了锁,因此死锁现象产生。这种死锁现象非常隐蔽往往很难排查。
三、事务隔离机制中的两种数据读取方式
快照读:在事务中进行不加锁的读操作时,mysql会读取开启事务时快照中的数据。
当前读:在事务中进行任何加锁操作(增删改、加互斥锁读、加共享锁读),mysql会读取数据库中当前的数据。