mysql 锁
InnoDB中,一般我们会做的就是两种操作,即DDL和DML。
DML中。我们日常的对数据库表结构的SELECT、INSERT、UPDATE以及DELETE都不会添加表级别的共享锁及排他锁。而是使用默认的并发控制方式——行级锁。
那除了增删改查以外,还有一些其他的操作,比如ALTER、DROP等对表机构改变的动作,他们加锁的过程添加的是MDL锁,即字典锁。
所以,InnoDB中的表级锁并不是没用,而是因为他划分的太细了,意向锁、AUTO-INC锁、字典锁等。而剩下的普通的排他锁和共享锁,确认很少才能用得上。我找了很多资料,也没有明确的看到具体是啥时候,在《MySQL是怎样运行的》这本书中提到过一句:比如在崩溃恢复时。
当然,我们可以自己通过SQL语句来添加表级锁。可以使用LOCK TABLES 手动添加表级锁,但这会阻塞其他所有访问该表的操作,直到执行 UNLOCK TABLES。
LOCK TABLES还可以分为排他和共享:
LOCK TABLES table READ:这就是添加表级别的共享锁
LOCK TABLES table WRITE:这就是添加表级别的排他锁
还有就是,Innodb会在倾向于选择行级锁来进行并发控制,但是如果在一些极端情况下, 比如说UPDATE操作需要扫描整个表且对表中许多行进行更新,InnoDB可能会评估行级锁的成本过高,而采用更粗粒度的锁定策略,比如表级锁。然而,这种情况在InnoDB中是非常罕见的,因为InnoDB设计上是倾向于尽可能地使用行级锁。
相信大家看到过的很多资料中都有过类似的描述“innodb 的 update语句中,如果where条件中没有索引,就不是行级锁了,而是锁表了,就是表级锁”。
我一直也都有这个印象,最开始是从哪看来的,也无从考究了,确实很长一段时间都是这么认为的。但是我发现并不对。
确实,mysql的行级锁锁的是索引,但是当update语句的where条件中没有用到索引的话,他会做全表扫描,但是也不是全部都锁定。而是把符合条件的记录锁住。
锁啥呢?锁主键索引。没有主键呢?会自动创建隐式主键锁住。
Record Lock表示记录锁,锁的是索引记录。
Gap Lock是间隙锁,锁的是索引记录之间的间隙。
Next-Key Lock是Record Lock和Gap Lock的组合,同时锁索引记录和间隙。他的范围是左开右闭的。
InnoDB的RR级别中,加锁的基本单位是 next-key lock,只要扫描到的数据都会加锁。唯一索引上的范围查询会访问到不满足条件的第一个值为止。
同时,为了提升性能和并发度,也有两个优化点:
- 索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
- 索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获得共享锁的事务只能读数据,不能修改数据。
SELECT ... LOCK IN SHARE MODE;
在查询语句后面增加LOCK IN SHARE MODE,MySQL会对查询结果中的每行都加共享锁,被加了共享锁的记录还可以被其他事务成功申请共享锁,但是不能被申请排他锁。
排他锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的锁。获得排他锁的事务既能读数据,又能修改数据。
SELECT ... FOR UPDATE;
在查询语句后面增加FOR UPDATE,MySQL会对查询命中的每条记录都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
总结:
当这一行数据获取了排他锁,那么其他事务就不能在对这一行数据添加共享锁或者排他锁。
当这一行数据获取了共享锁,那么其他事务依然可以对这一行数据添加共享锁,但不能添加排他锁
InnoDB是不支持锁升级的!默认使用行级锁进行并发控制。
使用乐观锁在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。(CAS)
