Mysql
索引
事务
锁机制
MVVC
什么是MVCC
MVCC,多版本并发控制,就是说同一时刻统同一记录在数据库欧中可以存在多个版本
在MySQL中,MVCC的存在主要是为了提高数据库系统的并发性,提倡不要使用锁来保证数据的读写冲突
比如我有这样一条数据:
3
YxYL
500
2
0x0001
可以看到后面多了两列:
事务id
回滚指针
这两个是数据库表每条数据后隐藏的两个字段门后面细锁
如果同时有多个事务来操作这一条数据,为了避免多事务并发场景下出现数据不一致的问题,InnoDB存储引擎会通过一条"链表"吧事务的"数据快照"串联起来:

每个事务启动的时候都会给自己分配一个事务id,并生成一个当前所有活跃事务的id列表——ReadView
如果一个事务操作一条数据,那么会顺便在这条数据后面填加所谓的隐藏字段;如果有下一个事务来操作这条数据的话,就会拿自己的事务id和这条数据上的隐藏字段上的事务id进行比较,如果发现不可用直接读取记录的数据的话,就会沿着undo_log找到自己能使用的数据版本——这就是MVCC
隐藏字段
上面提到了,在事务操作数据的时候,会比较自己的事务id和隐藏字段上的事务id。
这里提到的隐藏字段有:
事务id:
trx_id,每个事务进行数据操作的时候会顺便吧自己的事务id添加到数据的后面回滚指针:
roll_pointer,指向了某个事务的undo_log,是一个地址指针,可以通过他找到索引记录历史修改信息
ReadView
那么什么是ReadView呢,上面只是说了,每个事务启动的时候都会给自己分配一个事务id,并生成一个当前所有活跃事务的id列表——ReadView
说摆了,ReadView就是事务进行快照读操作的时候产生的读视图 ,在当前事务执行的快照读的时刻,会生成数据库系当前的一个快照,来维持数据和当前活跃事务的id列表(启动了还没有提交的事务)
上面又说了快照读,那么什么是快照读?什么又是当前读呢?
当前读:使用到当前读的场景有:共享锁,排他锁等,这些操作都是一种当前读的操作,因为他需要读取数据的最新版本进行操作,而且操作的时候还会进行加锁来保证其他事务不能操作当前记录
快照读:一般指的是不加锁的
select操作,如果MySQL的事务隔离级别是可串行化的话,那么快照读会退化为当前读、
一般ReadView包含下面四个字段:

在可重复读的隔离界别下,ReadView是在事务begin之后,执行一条sql之后,创建ReadView的同时也就生成一个新的事务id,直到commit
可重复读
指的是,在这个事务隔离机制下,整个事务过程中,本事务看到的所有记录,从事务
begin到commit都是一样的
之所有有这个效果,是因为事务A一开始就创建好了ReadView,一直到commit都是采用的同一个ReadView
基于MVCC实现可重复读
上面说了隐藏字段、ReadView的概念的之后,要理解MVCC的实现原理就要简单多了
假设现在YxYL的账户有于余额100
3
YxYL
100
2
0x0001
现在MySQL只有两个活跃事务,在同时操作这行数据。假设为事务A事务B。
事务A启动过后,在执行第一条sql之前,事务B启动
所以,基于以上操作的ReadView如下:
creator_trx_id
11
12
m_ids
[11,12]
[11,12]
min_trx_id
11
11
max_trx_id
13
13
内部事务流程如下:
①
开始
②
开始
③
查询YxYL余额 100
④
更新YxYL余额 100->200
⑤
查询YxYL余额 还是100
⑥
提交事务
⑦
提交事务
整个过程是这样的:
在③的时候,事务A查询余额,这个时候,记录行的事务id是在查询之前就被提交过的,假设
trx_id为10,且不在ReadView中。事务A的事务id为11,比10大。所以这条记录是对事务A可见的,查询出来是100在④的时候,事务B对记录执行修改,
trx_id->事务B的事务id12。并在undo_log中记录本次操作,以被pol_pointer指向在⑤的时候,事务A再次查询记录,发现这个时候的
trx_id为12,不仅>事务A的事务id11,并且在ReadView中。对于事务A来说不会读取这条数据,而是通过记录的poll_pointer去undo_log中去寻找旧版本的数据。通过每个版本的roll_pointer一直找下去,直到trx_id<=自己的事务id && trx_id不在自己的ReadView的m_ids的第一条记录
总结
总结一下吧,其实通过MVCC实现可重复读隔离级别,就是在事务内查询记录的时候,会先通过记录的trx_id判断2个点:
trx_id是否大于本事务id
trx_id是否在本事务的ReadView的m_ids的第一条记录
如果上述条件为true,那么就说明在本事务过程中,有其他事务对记录进行了操作,我不能取得这个被操作过后的数据。那么我该怎么办呢,因为事务对记录进行操作的时候会记录相对应的undo_log,并且数据行的roll_pointer字段指向它,所以我可以通过这个指针去寻找就记录,知道上述条件为false,就是我想要的记录。
这样就实现了可重复读的隔离级别
日志
怎么解决慢查询的
Last updated