当前位置: 首页 > 科技观察

基于MVCC,自己用C++自己搞了一个MySQL!

时间:2023-03-14 12:24:49 科技观察

没错,如题所示,我基于MVCC算法(这里姑且称之为算法吧,毕竟实际写代码的时候是用算法实现的)用C++写了一个简单版的MySQL。简单版本的CRUD操作。其实今天我并不打算向朋友们演示我写的简易版MySQL。这个项目在我优化后会开源。到时候大家可以一起学习,一起进步,一起维护。今天想和大家聊一聊MVCC。网上关于MVCC的文章很多,大部分都是基于版本链来介绍的。其实初学者要用版本链来介绍MVCC是相当困难的。今天,我就和大家谈谈我是如何理解MVCC的。MVCC其实很简单,不用版本链也能完全看懂。MVCC技术MVCC是一种通过记录数据的历史版本来提高并发事务处理能力的技术。可以大大提高并发事务下的数据处理性能。目前大部分关系型数据库都实现了MVCC机制。.MVCC主要解决多事务的并发控制问题,即保证事务的隔离性。MVCC存储方式MVCC大致可以分为三种存储方式,分别是Append-Only方式、Delta方式和Time-Travle方式,如下图。(1)Append-Only模式:将数据的历史版本直接存储在数据表中,代表数据库为PostgreSQL。(2)Delta模式:将数据的增量历史版本存储在一个独立的表空间中,代表数据库有MySQL和Oracle。(3)Time-Travle方法:完整存储每个版本的数据,将数据库表示为HANA。MVCC的工作原理MVCC主要用于保证事务的隔离性。这里,我们就以读提交和可重复读这两个隔离级别为例,来谈谈MVCC是如何工作的。readcommittedMVCC的工作原理在readcommitted隔离级别下,当前事务只能看到两类数据,如下图。(1)当前交易本身产生的数据。(2)当前交易开启前其他提交交易产生的数据。为了方便小伙伴们的理解,这里我画了一个简单的交易执行图,如下图。事务A到事务E是在数据库中执行的五个事务。它们是顺序执行的,分别对数据表中data1~data5的5条记录进行操作。在t1时刻,启动事务E,事务E需要读取事务A到事务D的4条记录。在t1时刻,事务E启动后,会向系统申请一个活跃事务列表。所谓活跃事务就是已经启动但是还没有提交或者回滚的事务。因此,在应用程序的活动事务列表中会看到事务D。当事务E查询data4的数据记录时,对应的事务D刚好在活跃事务列表中,事务E会读取data4的上一个版本。但是事务A、事务B、事务C在事务E启动时已经提交,最新版本的事务id小于活跃事务D对应的事务id,所以事务E可以看到data1,事务对应事务A、事务B、事务C。最新版本的data2和data3记录。可重复读MVCC的工作原理MVCC在重复读隔离级别下是如何工作的呢?我们先看图。如果在readcommitted隔离级别下,在时间t1,当事务E开始时,事务A、事务B和事务C已经提交,那么事务E可以读取事务A、事务B和事务C对应的data1和data2以及最新的data3记录的版本。事务D是活跃事务,所以事务E可以读取到data4的上一个版本。当事务E执行到t2时刻,事务D也已经提交。根据前面的分析,在t2时刻,事务E可以读取到事务A、事务B、事务C、事务D对应的数据data1、data2、data3和最新版本的data4。在可重复读隔离级别下,这显然是不合规的。在可重复读隔离级别下,MVCC机制是如何解决这个问题的?其实解决方法很简单。记录系统中事务E在时间t1启动时的活跃事务列表。在事务E的执行过程中,可以一直使用t1时刻记录的活跃事务列表,这个一直被使用的活跃事务列表称为“快照”。显然,在t2时刻使用t1时刻保存的活跃事务列表,事务E在t1时刻和t2时刻读取的数据是一致的。readcommitted和repeatablereadMVCC的区别readcommitted隔离级别下的每条SQL语句都会有自己的快照,它们看到的数据库中的数据是不同的。在可重复读隔离级别下,所有SQL语句使用同一个快照,可以看到数据库中相同的数据。快照优化实现MVCC时,并不是简单的存储一个事务id列表,而是统计最小活跃事务id和最大已提交事务id。这样做的好处是通过比较这些边界值可以快速识别出大部分交易id是读取最新版本还是之前的版本,如果交易id落在这些边界值范围内,只需要进一步检查是否是当前交易ID与活动交易的ID匹配。如果它们匹配,则当前事务处于活动状态,您可以看到当前数据。好了,关于MVCC,小伙伴们了解了吗?深入了解后,学习MySQL的底层原理。可能的话,读读MySQL的源码,然后用冰河手写MySQL。