数据库锁的分类:
(1)按锁的粒度划分:分为表级锁、行级锁、页级锁
(2)按照锁级别划分:分为共享锁、排它锁
(3)按照加锁方式划分:分为隐式锁(内部自动加锁)、显示锁(显示的添加锁)
(4)按照使用方式划分:分为乐观锁悲观锁
数据库中不同的存储引擎支持不同的锁机制
根据表粒度的锁
1、表级锁:
开销小,加锁快;不会出现死锁,发生锁冲突的概率最高;并发度最低
表锁锁定整张表,一个用户在对表进行写操作前(插入、删除和更新等),需要先获得写锁,这会阻塞其他用户在对该表的所有读操作。只有没有写锁时,其他用户才能获得读锁,读锁之间是不相互阻塞的。
写锁比读锁的优先级更高,所以一个写锁请求可能会被插入到读锁的前面。反之读锁不能插入到写锁前面。
1、在MySQL中,表锁不会有死锁发生,因为在加表锁前,要先解锁已有的表锁
2、表锁时,会锁定表以及表中的所有数据,对该表的任何请求几乎都可能会被阻塞,所以说冲突最多
3、锁冲突是一个事务持有某条数据的写锁,两一个事务也要请求同一条数据的写锁,就会被阻塞。
2、行级锁:
开销大,加锁慢;会出现死锁;锁粒度最小,发生锁冲突的概率最低,并发度也最高。
3、页面锁:
开销和加锁时间介于表锁和行锁之间;会出现死锁;锁粒度介于表锁和行锁之间,并发度一般。
下面我们主要来看InnoDB行级锁分类:共享锁和排他锁
共享锁、排他锁
共享锁(s)
共享锁又称读锁,多个事务对同一个数据行共享着同一把锁;一个事务在对一个数据进行读操作的时候,其他事务也可以访问到数据。
排他锁(x)
排他锁又称为写锁,一个事务拥有了一个数据的写锁可以对该数据进行读写,其他事务就不能获得该数据的写锁。
1、排他锁使用于锁定杭机路后需要进行更新操作的应用。
2、MySQL InnoDB引擎中update、delete、insert自动加排他锁
3、普通的select语句会自动加共享锁
此外InnoDB内部还自动添加意向共享锁(ls)和意向排他锁(lx)
意向共享锁(ls):事务打算给数据行共享锁;事务在给一个数据行加共享锁前必须先取得该表的意向共享锁。相当于要加排他锁先要获取意愿。
意向排他锁(lx):事务打算给数据行排他锁;事务在给一个数据行加排他锁前必须先取得该表的意向排他锁。
对于上表:意向锁之间都是兼容的;共享锁与共享锁之间兼容;共享锁与意向共享锁之间兼容
兼容指的是如果当前锁模式和要请求的锁模式兼容的话,请求的锁就会加在该事务上,如果不兼容就只能等待当前的锁释放。
意向锁存在的意义:
提高效率
如果没有意向锁,事务就需要一行一行的区检查每个数据航上是否有共享锁或者排他锁
有了意向锁就只需要检查该表上是否有意向锁而不必一行一行的去检查。
例如:
1、事务A获取了一个表中的某一行数据的排他锁;
2、事务B想获取该表的共享锁,由于共享锁和排他锁互斥,所以事务B必须知道当前是否有其他事务持有表中任意一条记录的排他锁。
3、当事务B检测到A已经持有该表的意向排他锁的时候就会知道A肯定持有该表中某条记录的排他锁,就不需要一行一行区检测了。
死锁
《高性能MySQL》中说:死锁是指两个或者多个事务在同一个资源上相互占用,并请求锁定对方占有的资源,从而导致恶性循环的现象。如果没有外力的作用,都无法推进下去。
例如:仿照《高性能mysql》中的例子
student表
事务1:
start transaction;
update student set score=85 where stu_id = 4;
update student set score=90 where stu_id = 3;
commit;
事务2:
start transaction;
update student set score=85 where stu_id = 3;
update student set score=90 where stu_id = 4;
commit;
1、如果两个事物都执行了第一条语句,更新了一行数据,同时也锁定了该行数据
2、接着每个事务都去尝试执行第二条更新语句,就会发现改行已经被对方锁定
3、然后两个事物都等待对方释放锁,但又持有对方锁需要的锁,这样就会进入死循环。
4、只有外力的作用才能解除死锁。
死锁的关键就在于:两个或多个事务加锁的顺序不一致
乐观锁和悲观锁
乐观锁
乐观锁顾名思义持有乐观的态度,每次对数据进行操作都认为别人不会修改数据,所以一开始不会上锁,但是会在更新的时候回判断在此期间是否有人修改了该数据。
利用版本号机制和CAS算法实现判断是否有人修改了数据
悲观锁
先获取锁再进行操作。
悲观锁顾名思义就是一种悲观的态度,每次进行操作的时候都会认为别人会修改数据,所以每一进行操作的时候都会加上锁。
乐观锁和悲观锁的使用场景:
乐观锁:适用于写操作较少,读操作多的场景。这样就可以省去锁的开销。
悲观锁:适用于写操作多的场景。