锁
# 锁
为了保证数据的一致性 在共享数据被并发访问时变得安全的规则
按操作
- 共享锁:读锁 针对同一份数据 多个事务读取操作可以同时加锁而不互相影响 但不能修改数据
- 排他锁:写锁 当前操作没有完成前 会阻断其他操作的读取和写入
按力度
- 表级锁:会锁定整个表 开销小 加锁快 锁定力度大 发生锁冲突概率高 并发度低 不会出现死锁情况
- 行级锁:会锁定当前行 开销大 加锁慢 锁定力度小 发生锁冲突概率低 并发高 会出现死锁情况
按使用方式
- 悲观锁:每次查询数据都认为别人会修改 查询时加锁
- 乐观锁:每次查询数据都认为别人不会修改 但更新时会判断一下期间别人有没有去更新这个数据

# 属性锁
# 共享锁
共享锁 (Share Lock) 又称读锁,简称 S 锁。当一个事务对数据加上读锁之后,其他事务只能对该数据加读锁,而无法对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加写锁。 加了共享锁之后,无法再加排它锁,这也就可以避免读取数据的时候会被其它事务修改,从而导致重复读问题。
特点:数据可以被多个事务查询 但不能修改
InnoDB 默认的是加行锁 如果列没有索引则加的是表锁
如在有锁情况下修改 锁释放 修改数据失败,要想修改就必须等所有共享锁都释放完之后。
-- 创建 select语句 lock in share mode;
select * from 表名 lock in share mode;
2
# 排他锁
排他锁(eXclusive Lock)又称写锁,简称 X 锁;当一个事务对数据加上写锁之后,其他事务将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁。加了排他锁之后,其它事务就无法再对数进行读取和修改,所以也就避免了脏写和脏读的问题。
加锁后 不能被其他事务加锁查询或者修改 (即不能存在第二把锁)
普通查询可以 加锁查询不能 普通和加锁修改都无法执行 必须提交事务
-- 创建语法 select语句 for update;
select * from 表名 for update;
2
# 悲观锁和乐观锁
乐观锁和悲观锁都是针对 select 而言的,悲观锁一般都是依靠关系型数据库提供的锁机制
悲观锁和乐观锁 差别是一个执行前上锁 一个是不上锁 只是在更新时查看是否与被修改
- 添加一个版本号列 每次更新版本号 + 1 并在更新时检测版本号是否与查询时相同
update table set num=num-1 where id=10 and version=12;
# 粒度锁
# 表锁
表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;
特点: 粒度大,加锁简单,容易冲突;

# MyISAM 读锁
所有连接只能查询不能修改数据
MyISAM 不支持事务 和 行锁 当前表也不能修改
-- 加锁
lock table 表名 read;
-- 解锁
unlock tables;
2
3
4
# MyISAM 写锁
其他连接不能查询和修改数据 当前表可以查询和修改数据
-- 加锁
lock table 表名 write;
-- 解锁
unlock tables;
2
3
4
# 行锁
行锁是对所有行级别锁的一个统称,比如下面说的记录锁、间隙锁、临键锁都是属于行锁, 行锁是指加锁的时候锁住的是表的某一行或多行记录,多个事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问;
特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高;

# 记录锁 (Record Lock)
记录锁属于行锁中的一种,记录锁的范围只是表中的某一条记录,记录锁是说事务在加锁后锁住的只是表的某一条记录。

** 触发条件:** 精准条件命中,并且命中索引;
** 例如:**update user_info set name=’张三’ where id=1 , 这里的 id 是索引。
** 记录锁的作用:** 加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事务读取的脏读问题。
# 间隙锁 (Gap Lock)
间隙锁属于行锁中的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻 ID 之间出现空隙则会形成一个区间。
比如下面的表里面的数据 ID 为 1,4,5,7,10 , 那么会形成以下几个间隙区间,-n-1 区间,1-4 区间,7-10 区间,10-n 区间 (-n 代表负无穷大,n 代表正无穷大)

** 触发条件:** 范围查询,查询条件必须命中索引、间隙锁只会出现在 REPEATABLE_READ(重复读) 的事务级别中。
例如:对应上图的表执行 select * from user_info where id>1 and id<4 (这里的 id 是唯一索引) ,这个 SQL 查询不到对应的记录,那么此时会使用间隙锁。
间隙锁作用:防止幻读问题,事务并发的时候,如果没有间隙锁,就会发生如下图的问题,在同一个事务里,A 事务的两次查询出的结果会不一样。

# 临键锁 (Next-Key Lock)
临键锁也属于行锁的一种,并且它是 INNODB 的行锁默认算法,总结来说它就是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住,遵循左开右闭原则。
如
select * from user_info where id>1 and id<=13 for update ;
会锁住 ID 为 5,10 的记录;同时会锁住,1 至 5,5 至 10,10 至 15 的区间。

** 触发条件:** 范围查询,条件命中了索引。
** 临键锁的作用:** 结合记录锁和间隙锁的特性,临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之后,在范围区间内数据不允许被修改和插入。