mysql的锁
锁的分类
- 从性能上分为乐观锁(用版本对比来实现)和悲观锁
- 从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)。
- 读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响
- 写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁
- 从对数据操作的粒度分,分为表锁和行锁
表锁与行锁
表锁
- 每次操作锁住整张表。
- 开销小(不需要定位到某个元素,只需要定位到表),加锁快;
- 不会出现死锁;
- 锁定粒度大,发生锁冲突的概率最高,并发度最低;
- 一般用在整表数据迁移的场景。
表锁操作方式
- 手动增加表锁: lock table 表名称 read(write),表名称2 read(write);
- 查看表上加过的锁 show open tables;
- 删除表锁 unlock tables;
行锁
- 每次操作锁住一行数据。
- 开销大,加锁慢;
- 会出现死锁;
- 锁定粒度最小,发生锁冲突的概率最低,并发度高。
- InnoDB与MYISAM的最大不同点:InnoDB支持行级锁、支持事务
- MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行update、insert、delete操作会自动给涉及的表加写锁。
- InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。
行锁操作方式
- sql后增加for update来实现行锁。
- for update在不走索引的时候会锁表!但是当要修改或者查询的数据不存在的时候,不会锁表,也不会锁定行!
读锁与写锁
- 读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞。
乐观锁与悲观锁
乐观锁
- 乐观锁时会假设在极大多数情况下不会形成冲突,只有在数据提交的时候,才会对数据是否产生冲突进行检验。发生冲突后,不修改数据
实现方式
- 在数据库表中增加一列,记为version,当我们将数据读出时,将版本号一并读出,当数据进行更新时,会对这个版本号进行加1,当我们提交数据时,会判断数据库表中当前的version列值和当时读出的version是否相同,若相同说明没有进行更新的操作,不然,则取消这次的操作。
悲观锁
- MySql的悲观锁就是打开事务,当启动事务时,如果事务中的sql语句涉及到索引并用索引进行了条件判断,那么会使用行级锁锁定所要修改的行,否则使用表锁锁住整张表。
实现方式
- sql后增加for update来实现悲观锁。
- for update在不走索引的时候会锁表!
- 当要修改或者查询的数据不存在的时候,不会锁表,也不会锁定行!
当InnoDB表有多个索引的时候,不同事务使用不同的索引去锁定同一条记录是怎么处理的
- 虽然用的不同辅助索引,但是相应的聚簇索引也会加锁,也就是主键会加锁,这样就防止并发修改了
间隙锁(Gap Lock)与临键锁(Next-key Locks)
间隙锁
- 间隙锁,锁的就是两个值之间的空隙。
- 间隙锁是在可重复读隔离级别下才会生效。
- 在普通索引列上,不管是何种查询,只要加锁,都会产生间隙锁,这跟唯一索引不一样;
- 在普通索引和唯一索引中,数据间隙的分析,数据行是优先根据普通索引排序,再根据唯一索引排序。
产生的条件
- 使用普通索引锁定;
- 使用多列唯一索引;
- 使用唯一索引锁定多行记录。
间隙锁开启状态处理:innodb_locks_unsafe_for_binlog
- 默认值为OFF,即启用间隙锁。
- 此参数是只读模式,如果想要禁用间隙锁,需要修改 my.cnf(windows是my.ini) 重新启动才行。
- 查看是否禁用:show variables like 'innodb_locks_unsafe_for_binlog';
间隙锁的范围说明
|id|name|
|-|-|-|
|1|a|
|5|d|
|10|f|
|100|z|
- 唯一索引:id的间隙
SELECT * FROMtable
WHEREid
> 3 ANDid
< 7 FOR UPDATE;
产生的间隙为(1,10】 - 普通索引:name的间隙
SELECT * FROMtable
WHEREname
= '5' FOR UPDATE;
产生的间隙为(1,10】
临键锁(Next-key Locks)
- 行锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间
- 这是Innodb在可重复读提交下为了解决幻读问题时引入的锁机制,
- MySQL 默认隔离级别是RR(可重复读),在这种级别下,如果你使用 select in share mode 或者 select for update 语句,那么InnoDB会使用临键锁(记录锁 + 间隙锁),因而可以防止幻读;
- 即使你的隔离级别是 RR(可重复读),如果你这是使用普通的select语句,那么此时 InnoDB 引擎将是使用快照读,而不会使用任何锁,因而还是无法防止幻读。