MySql数据库MVCC和锁机制
MySQL中的MVCC机制
什么是MVCC
MVCC,全称 Multi-Version Concurrency Control ,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。(百度百科)
简而言之,一份数据可能存在多个版本。
快照读/当前读
- 当前读:读取记录数据的最新版本(有锁)
insert/update/delete/select…for update/select…in share mode - 快照读:读取记录数据的可见版本(最新版本/历史版本 无锁)
select column/* from table
一条sql记录的结构
一条完整的记录可以被分为记录的额外信息和记录的真实数据。
-
row_id:隐含的自增 ID(隐藏主键),如果数据表没有主键,InnoDB 会自动产生一个聚簇索引
-
transaction_id:最近修改(修改/插入)事务 ID,记录创建这条记录/最后一次修改该记录的事务 ID(自增)
-
roll_pointer:回滚指针,指向这条记录的上一个版本
事务回滚是怎么回事呢?
Read View 读视图
Read View 就是事务进行快照读操作的时候生产的读视图 (Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的 ID (当每个事务开启时,都会被分配一个 ID , 这个 ID 是递增的,所以最新的事务,ID 值越大),Read View 主要是用来做可见性判断。
接下来需要记住几个重要的点:
拆解可见性算法的逻辑
- DB_TRX_ID < up_limit_id
判断为true,则说明:快照生成时,所有比up_limit_id小的事务全部提交。也就是说,所有活跃事务的快照读读取的版本是最新的,大于等于进入下一个判断。 - DB_TRX_ID >= low_limit_id
判断为true,则说明:快照生成时,所有活跃id的读取的数据版本是历史数据版本,如果小于则进入下一个判断。 - 判断 DB_TRX_ID 是否在活跃事务之中
判断为true,则说明:Read View 生成时刻,这个事务还在活跃,还没有 Commit,修改的数据,当前事务也是看不见的;
判断为false,则说明,你这个事务在 Read View 生成之前就已经 Commit 了,你修改的结果,我当前事务是能看见的
MySQL中的锁
一个数据库的基本结构
MySQL数据库中的基本锁类型
- 全局锁:全局锁就是对整个数据库实例加锁。
场景:当你需要整个数据库处于只读状态的时候,如全库逻辑备份。此过程中会阻塞所有的DDL和除了select的DML语句。
表锁的主动加锁语句:Flush tables with read lock(unlock解锁)
- 表级锁:MySQL 里面表级别的锁有两种,一种是表锁,一种是元数据锁。
表锁的主动加锁语句:lock tables … read/write(unlock解锁),但是不建议使用。
元数据锁:不需要显式使用,在访问一个表的时候会被自动加上,保证读写的正确性。
元数据锁的场景:如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做变更,删了一列,那么查询线程拿到的结果跟表结构对不上。因此,在
MySQL 5.5 版本中引入了 MDL,当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁。
- 页级锁:页级锁定是MySQL中比较独特的一种锁定级别,特点是锁定颗粒度介于行级锁定与表级锁之间。
无具体使用场景。使用页级锁定的主要是BerkeleyDB存储引擎。
- 行级锁:行锁就是针对数据表中行记录的锁。
问题1:下面的两个语句锁的类型是什么?
(1)update … where id = 1 (id为索引项)
(2)update … where name = ‘a’ (name不是索引项)问题2:下面的两个语句存在锁竞争么?(type 为索引项)
(1)update … where type = 1 and name = ‘1’
(2)update … where type = 1 and name = ‘2’问题3:(1) 与(2)可能会有锁冲突么?(type/name 均为索引项)
(1)update … where type = 1
(2)update … where name = ‘1’
·········································································································································知识点1:
InnoDB行锁是通过给索引上的索引项加锁来实现的。只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。知识点2:
MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。知识点3:
当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行。 这种情况是可能存在锁冲突的。
- 间隙锁
间隙锁是封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。
打开间隙锁:show variables like ‘innodb_locks_unsafe_for_binlog’ 修改为off则开启间隙锁。
假设间隙锁生效,锁定的区间为 (-supernum,5](5,10](10,15](15,20](20,+supernum]
-
间隙锁只有在事务隔离级别 RR 中才会产生;
-
唯一索引只有锁住多条记录或者一条不存在的记录的时候,才会产生间隙锁,指定给某条存在的记录加锁的时候,只会加记录锁,不会产生间隙锁;
-
普通索引不管是锁住单条,还是多条记录,都会产生间隙锁;
-
间隙锁会封锁该条记录相邻两个键之间的空白区域,防止其它事务在这个区域内插入、修改、删除数据,这是为了防止出现 幻读 现象;
-
普通索引的间隙,优先以普通索引排序,然后再根据主键索引排序(多普通索引情况还未研究);
-
事务级别是RC(读已提交)级别的话,间隙锁将会失效。