问题一:在多线程情况下,多个线程访问同一个变量,需要保证数据安全问题;在mysql数据库中同样存在多线程访问同一条数据导致的数据问题;很多人第一时间想到加锁,但是对数据库来说,加锁相对比较消耗性能,并发量很难上去;
MVCC是如何解决并发场景下,不加锁,解决多个事务访问同一条数据产生的数据安全的三种问题(脏读、不可重复读、幻读)?
1、什么是MVCC?
Multi-Version-Concurrenty-Control:多版本并发控制,多版本指什么?
2、三个基础概念:
(1)undo log:当执行任何一条insert、update、delete语句时,mysql会记录每条sql的回滚日志;
a、undo log回滚日志的字段包含:
row_id: 就是数据库的隐藏字段row_id;
db_trx_id:当前事务的ID;
db_roll_ptr: roll_pointer,回滚指针,每条数据的回滚日志中的回滚指针都会指向上一条回滚日志;
b、作用:
回滚日志用来实现事务的回滚操作
事务的原子性,也是基于undo log实现的
当然,mysql还有其他日志,redo log和bing log,后面在学习;
(2)版本链:
一条数据的多个回滚日志,通过回滚指针链接在一起,形成了这条数据的版本链;
所有的针对该条数据的查找,都是基于一定规则在这条数据对应的版本链中进行判断并查找;
MVCC中多版本并发控制中的多版本应该就是指某条数据在undo log形成的版本链中有多条记录
(3)ReadView:查询视图,mysql在执行select的时候,都会生成ReadView查询视图,通过查询视图的关键字段去判断,该从版本链的哪些记录提取数据,并返回结果;
a、ReadView的几个关键字段:
m_ids:表示在生成ReadView视图的时候,活跃的事务ID集合,所谓活跃,就是开启了事务,没提交的那些事务的id集合,一条数据,可以被多个会话开启多个事务,就会有多个ReadView;
m_min_trx_id:在m_ids集合中最小的值;
m_max_trx_id: m_max_trx_id并不是m_ids中的最大值,而是在m_ids的最大值再递增一位,
比如某条数据有3个事务同时对它进行修改,事务A的id是1,事务B的id是2,事务3的id是4,则m_ids=[1,2,3],而m_max_trx_id则等于4;
creator_trx_id: 当前事务的id;
b、根据ReadView这几个关键字段,判断数据是否可被访问,主要遵循以下几点原则:
db_trx_id == creator_trx_id:当前事务就是查询的ReadView事务中的数据,那么这条数据肯定是可以被查询的;
db_trx_id < m_min_id:说明要查询的事务是m_min_id之前提交的那些事务,因此数据也是可以被查看的;
db_trx_id > m_max_id:说明当前事务要查询的数据在最大的活跃事务后,是不能被访问的;
m_min_id < db_trx_id < m_ids:这里分为2种情况:
3、mysql怎样通过undo log、版本链、ReadView去进行数据筛选,从而达到不加锁,也能实现事务的隔离级别所达到的那个隔离效果的?
(1)READ UNCOMMITTED和SERIALIZABLE,
a、由于READ UNCOMMITTED隔离级别低,每次读取时只需要从版本链中读取最新版本的数据返回即可,
b、SERIALIZABLE是通过串行的方式执行,直接从最近不活跃的版本链中进行数据提取即可,因此值得讨论的只有READ COMMITTED和REPEATABLE READ 这两个隔离级别;
(2)READ COMMITTED的执行流程:
每个select新建一个ReadView视图,
(3)REPEATABLE READ的执行流程:
在一个事务中,里面都沿用同一个ReadView
(4)总结二者的区别: