1. 锁分类
1.1根据性能
乐观锁
● 版本号
● 读多场景
● 第二次循环需要读取到最新的数据统计
示例
while{
// 1.调用方法获取当前版本号
getCurrentBalanceAndVersion(accountId);
// 2.Java运算
newBalance = Balance + 500;
updateAccountBalance(accountId, newBalance, version); //3.更新数据
}
//如果当前version与需要的不一致,则重新获取并运算
悲观锁
- 适合写多场景
- RR隔离级别下更新操作是加悲观锁
1.2根据粒度
表锁
每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景
行锁
每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。
InnoDB相对于MYISAM的最大不同有两点:
● InnoDB支持事务(TRANSACTION)
● InnoDB支持行级锁
注意,InnoDB的行锁实际上是针对索引加的锁(在索引对应的索引项上做标记),不是针对整个行记录加的锁。并且该索引不能失效,否则会从行锁升级为表锁。(RR级别会升级为表锁,RC级别不会升级为表锁)
RR和RC本质不同是:
RR通常都是快照读,需要关心过去时间的数据问题
RC是当前读,只需要查看最新的提交,不需要关心过去时刻的数据问题
RR隔离级别升级为表锁分析(无索引字段):
select * from account where name = 'zps' for update; --where条件里的name字段无索引
● 其它Session对该表任意一行记录做修改操作都会被阻塞住
● 对表的所有更改操作包括修改和删除都会无法操作,因此当前已经升级为表锁。支持读取不支持修改
1.3 根据数据库的类型
读锁
针对同一份数据,多个读操作可以同时进行而不会互相影响
写锁
当前写操作没有完成前,它会阻断其他写锁和读锁,数据修改操作都会加写锁,查询也可以通过for update加写锁
在RR隔离级别下默认使用共享锁
1.4 意向锁
行锁标记表被占有
又称I锁,针对表锁,主要是为了提高加表锁的效率,是mysql数据库自己加的。当有事务给表的数据行加了共享锁或排他锁,同时会给表设置一个标识,代表已经有行锁了,其他事务要想对表加表锁时,就不必逐行判断有没有行锁可能跟表锁冲突了,直接读这个标识就可以确定自己该不该加表锁。特别是表中的记录很多时,逐行判断加表锁的方式效率很低。而这个标识就是意向锁。
意向锁主要分为:
意向共享锁,IS锁,对整个表加共享锁之前,需要先获取到意向共享锁。
意向排他锁,IX锁,对整个表加排他锁之前,需要先获取到意向排他锁
2. 间隙锁
间隙锁,锁的就是两个值之间的空隙,间隙锁是在可重复读隔离级别下才会生效。Mysql默认级别是repeatable-read,有幻读问题,间隙锁是可以解决幻读问题的(被加锁的区间可以解决幻读)
假设三个间隙:id 为 (2,5),(5,10),(10,正无穷) 这三个区间,则锁住的间隙无法正常插入
注意:前提是查询的字段id是有索引的情况, 如果没有索引还是默认会锁表
3. 临建锁
行锁+间隙锁,解决边界问题