MySQL学习笔记Ⅳ-锁
MySQL InnoDB锁的基本类型
共享锁
Shared Locks(共享锁),在获取了一行数据的读锁以后,可以用来读取数据,所以它也叫做读锁。用select ...... lock in share mode;的方式手动加上一把锁。使用begin开启事务,而不关闭事务,这样做是用来测试,因为提交事务或回滚事务就会释放锁。
共享锁的行不可以被修改;共享锁的行可以不加任何锁读取;共享锁的行可以加读锁读取。
排他锁
Exclusive Locks(排他锁),他是用来操作数据的,所以又叫做写锁。只要一个事务获取了一行数据的排它锁,其他的事务就不能再获取这一行数据的共享锁和排它锁。释放锁的方式和共享锁一样。手工加锁,用FOR UPDATE给一行数据加上一个排它锁,这个在代码或者操作数据的工具里都比较常用。
增删改,都会默认加上一个排他锁。select语句默认不会加任何锁类型,如果加排它锁可以使用select ... for update。加过排他锁的数据行在其他事务中是不能修改数据也不能通过for update和lock in share mode锁的方式查询数据。但可以直接通过select ... from ...查询数据,因为普通查询没有任何锁机制。
排他锁的行不可以再加排他锁;排他锁的行可以不加读锁;1+2=排他锁的行不可以再加任何锁;排他锁的行可以普通读取。
不用锁的查询可以读取加锁(共享锁、排他锁)的同一行数据
意向锁
在我们给一行数据加上共享/排他锁之前,数据库会自动在这张表上加一个意向共享/排他锁。
反过来说,如果一张表上至少有一个意向共享/排他锁,说明有其他事务给其中的某些数据行加上了共享/排他锁。
行锁的原理
InnoDB的行锁,就是通过锁住索引记录来实现的(?)索引为什么可以被锁住?从information_schema.innodb_locks可以看到锁住的是索引。那么:
为什么表里面没有索引的时候,锁住一行数据会导致锁表?or如果锁住的是索引,一张表没有索引怎么办?
那么一张表有没有可能没有索引?【不会的】
- 如果我们定义了主键那么InnoDB会选择主键作为聚集索引
- 如果没有显式定义主键,则InnoDB会选择第一个不包含NULL值的唯一索引作为主键索引。
- 如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐藏的聚集索引,它会随着行记录的写入而主键递增。
为什么通过唯一索引给数据行加锁,主键索引也会被锁住?
在InnoDB里面,当我们使用辅助索引的时候,辅助索引的叶子结点存储的是二级索引和主键的值,比如name=4,存储的是name的索引和主键id的值4.
而主键索引里面除了索引之外,还存储了完整的数据。所以我们通过辅助索引锁定一行数据的时候,它跟我们检索数据的步骤是一样的,会通过主键值找到主键索引,然后也锁定。

锁的算法

间隙(Gap)连同它左边的记录(Record)被称之为临键的区间。他是一个左开右闭的区间。
记录锁
当对于唯一性的索引(包括唯一索引和主键索引)使用等值查询,精准匹配到一条记录的时候,使用的就是记录锁。比如where id = 1/4/7/10
间隙锁
当我们查询的记录不存在时,没有命中任何一个record,无论使用等值查询还是范围查询,使用的都是间隙锁。比如where id>4 and id<7, where id = 6
间隙锁主要是阻塞插入insert。相同的间隙锁之间不冲突。
Gap Lock只在RR(可重复读)中存在,如果要关闭间隙锁,就是把事务隔离级别设置成RC(已提交读),并且把innodb_locks_unsafe_for_binlog设置成ON。这种情况下除了外键约束和唯一性检查会加间隙锁,其他情况都不会用间隙锁。
临键锁
当使用了范围查询,不仅命中Record记录,而且包含了gap间隙,在这种情况下使用的就是临键锁,他是MySQL里面默认的行锁算法,相当于记录锁加上间隙锁。临键锁,锁住最后一个key的下一个左开右闭的区间。