行锁和表锁
1.解决事务一致性方案
1.LBCC 基于锁的并发控制
基于锁的来实现事务隔离,一个事务读取的时候不允许其他事务修改,意味着Lock Based Concurrency Control(LBCC)不支持并发的读写操作,而大多应用读多写少,这样会影响操作数据的效率。
2.MVCC 多版本的并发控制
多版本的并发控制Multi Version Concurrency Control (MVCC)。
核心思想:可以查到这个事务之前已经存在的数据,即使他已经被修改或删除了,在这个事务之后新增的数据,查不到。
问题:快照什么时候创建?读取数据的时候,怎么保证能读取到这个快照而不是最新的数据?这个怎么实现呢?
InnoDB每行记录都有两个隐藏字段:
DB_TRX_ID,6字节:插入或更新行的最后一个事务的事务ID,自增(可以理解为创建版本号)
DB_ROLL_PTR,7字节:回滚指针(删除版本号)
在InnoDB 中,MVCC 是通过Undo log 实现的。
2 行锁和表锁的区别
-
锁定粒度,行锁大于表锁
表锁,锁住一张表
行锁,锁住里面的一行数据 -
加锁效率,表锁大于行锁。
表锁只需锁住这张表就行
行锁需要在表里检索这一行数据 -
冲突概率表锁大于行锁
锁住整张表,其他任何事务都不能操作这张表
锁住一行,其他事务可以操作未被锁的行
3 行锁–共享锁 Shared Locks
获取共享锁和可以读取数据,也叫读锁。
多个事务可以共享一把读锁。
加读锁: select …… lock in share mode;
释放锁有两种方式:事务结束(提交事务和结束事务)
4 行锁–排它锁 Exclusive Locks
主要功能:操作数据,又称写锁
只要一个事务获取了一行数据的排他锁,其他事务就不能再获取该行的共享锁和排他锁。
加锁方式: 1.自动加排他锁
2.手工加锁 FOR UPDATE
5 表锁–意向锁
数据库自己维护的
加了共享锁钱,自动加意向共享锁
加了排他锁前,自动加意向排他锁
为什么加表锁?
- InnoDB可以支持更多粒度的锁
- 提升加锁的效率。加行锁前可以判断意向锁,如果有则返回失败,如果没有,则加锁成功。如果没有意向锁则需要扫描整张表。
总结:锁是用来解决事务对数据的并发访问的问题的
为什么表里面没有索引的时候,锁住一行数据会导致锁表?
如果锁住的是索引,一张表没有索引怎么办?
1)定义了主键,InnoDB会选主键作为聚集索引。
2)没有显示定义主键,会选第一个不包含null值的唯一索引作为主键索引
3)如果也没有2中的索引,则选择内置6字节长的RoWID作为隐藏的聚集索引,它会随着记录的写入递增。
所以,锁表是因为没有使用索引,进行全表扫描,把每一个隐藏的聚集索引都锁住了。
为什么通过唯一索引给数据行加锁,主键索引也会被锁住?
唯一索引也是辅助索引,存储的是二级索引和主键的值。主键索引存放了索引和完整的数据。通过辅助索引锁定一行数据的时候,会通过主键值找到主键索引然后也锁定。
锁的算法 --(记录锁,间隙锁,临键锁)
记录锁
对唯一性的索引(包括唯一索引和主键索引)使用等值查询,精确匹配到一条记录的时候,使用的就是记录锁。
间隙锁
当查询记录不存在,没有命中任何一个,无论用等值还是范围查询,都是间隙锁
注意,间隙锁主要是阻塞插入insert。相同的间隙锁之间不冲突。
临键锁
范围查询,命中了记录,还包含了间隙,就是临键锁。
默认的行锁算法,相当于记录锁加间隙锁。
临键锁,锁住最后一个key 的下一个左开右闭的区间。
为什么要锁住下一个左开右闭的区间?
—就是为了解决幻读的问题。
为什么有些公司要用RC,或者说网上有些文章推荐有RC?
RC 和RR 主要有几个区别:
1、RR 的间隙锁会导致锁定范围的扩大。
2、条件列未使用到索引,RR 锁表,RC 锁行。
3、RC 的“半一致性”(semi-consistent)读可以增加update 操作的并发性。
InnoDB 的行锁,就是通过锁住索引来实现的。
死锁
锁的释放与阻塞
锁什么时候释放?
事务结束(commit,rollback);客户端连接断开。
死锁发生和监测
死锁的产生条件。
因为锁本身是互斥的,(1)同一时刻只能有一个事务持有这把锁,(2)其他的事务需要在这个事务释放锁之后才能获取锁,而不可以强行剥夺,(3)当多个事务形成等待环路的时候,即发生死锁。
为什么可以直接检测到呢?
是因为死锁的发生需要满足一定的条件,所以在发生死锁时,InnoDB 一般都能通过算法(wait-for graph)自动检测到。
show status like ‘innodb_row_lock_%’;
如何避免死锁
1、在程序中,操作多张表时,尽量以相同的顺序来访问(避免形成等待环路);
2、批量操作单张表数据的时候,先对数据进行排序(避免形成等待环路);
3、申请足够级别的锁,如果要操作数据,就申请排它锁;
4、尽量使用索引访问数据,避免没有where 条件的操作,避免锁表;
5、如果可以,大事务化成小事务;
6、使用等值查询而不是范围查询查询数据,命中记录,避免间隙锁对并发的影响。