数据库锁
# 1 事务一定会锁表吗?
1、事务隔离级别是可重复读时,如果没有索引,更新数据时会锁住整张表。
2、事务隔离级别为读未提交时,写数据只会锁住相应的行。
3、事务隔离级别为串行化时,读写数据都会锁住整张表。
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁。
# 2 锁分类
1.从性能上分为乐观锁(用版本对比来实现)和悲观锁
2.从对数据操作的粒度分,分为表锁和行锁
3.从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)
读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响
写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁
读锁和写锁都是行级锁,InnoDB的行锁是通过给索引上的索引项加锁来实现的,如果没有索引,InnoDB将通过隐藏的聚簇索引来对记录加锁,InnoDB行锁分为3中情形:
1. Record Lock:对索引项加锁。
2. Gap Lock(间隙锁):对索引项之间的“间隙”、第一条记录前的“间隙”或最后一条记录后的“间隙”加锁。
3. Next-key Lock(临键锁):前两种的结合,对记录及其前面的间隙加锁。
InnoDB这种行锁的实现特点意味着,如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,实际效果跟锁表一样。
# 3 锁优化建议
1.尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
2.合理设计索引,尽量缩小锁的范围
3.尽可能减少检索条件范围,避免间隙锁
4.尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行
5.尽可能低级别事务隔离
# 4 MVCC
所谓的MVCC(Multi-Version Concurrency Control ,多版本并发控制)指的就是在使用READ COMMITTD、REPEATABLE READ这两种隔离级别的事务在执行普通的SEELCT操作时访问记录的版本链的过程,这样子可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。
READ COMMITTD、REPEATABLE READ这两个隔离级别的一个很大不同就是生成ReadView的时机不同,
READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView,
而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复这个ReadView就好了。
MVCC,对一行数据的读和写两个操作默认是不会通过加锁互斥来保证隔离性,避免了频繁加锁互斥,而在串行化隔离级别为了保证较高的隔离性是通过将所有操作加锁互斥来实现的。Mysql在读已提交和可重复读隔离级别下都实现了MVCC机制。
# 5 undo日志版本链与read view机制详解
undo日志版本链是指一行数据被多个事务依次修改过后,在每个事务修改完后,Mysql会保留修改前的数据undo回滚日志,并且用两个隐藏字段trx_id和roll_pointer把这些undo日志串联起来形成一个历史记录版本链。
在可重复读隔离级别,当事务开启,执行任何查询sql时会生成当前事务的一致性视图read-view,该视图在事务结束之前都不会变化。
如果是读已提交隔离级别在每次执行查询sql时都会重新生成。
read-view视图由执行查询时所有未提交事务id数组(数组里最小的id为min_id)和已创建的最大事务id(max_id)组成,事务里的任何sql查询结果需要从对应版本链里的最新数据开始逐条跟read-view做比对从而得到最终的快照结果。
总结: MVCC机制的实现就是通过read-view机制与undo版本链比对机制,使得不同的事务会根据数据版本链对比规则读取同一条数据在版本链上的不同版本数据。
提示
begin / start transaction
命令并不是一个事务的起点,在执行到它们之后的 第一个修改操作
InnoDB表的语句,事务才真正启动,才会向mysql申请 事务id
,mysql内部是严格按照事务的启动顺序来分配事务id的。