摘要:
1、基础知识
2、MVCC实现原理以及视图化理解(包含些测试以便理解)
3、深MVCC实现机制
一、基础知识
事务:
事务是一组原子性sql查询语句,被当作一个工作单元。若mysql对改事务单元内的所有sql语句都正常的执行完,则事务操作视为成功,所有的sql语句才对数据生效,若sql中任意不能执行或出错则事务操作失败,所有对数据的操作则无效(通过回滚恢复数据)。事务有四个属性:
1、原子性:事务被认为不可分的一个工作单元,要么全部正常执行,要么全部不执行。
2、一致性:事务操作对数据库总是从一种一致性的状态转换成另外一种一致性状态。
3、隔离性:一个事务的操作结果在内部一致,可见,而对除自己以外的事务是不可见的。
4、永久性:事务在未提交前数据一般情况下可以回滚恢复数据,一旦提交(commit)数据的改变则变成永久(当然用update肯定还能修改)。
ps:MYSAM 引擎的数据库不支持事务,所以事务最好不要对混合引擎(如INNODB 、MYISAM)操作,若能正常运行且是你想要的最好,否则事务中对非支持事务表的操作是不能回滚恢复的。
读锁:
也叫共享锁、S锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S 锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
写锁:
又称排他锁、X锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。
表锁:操作对象是数据表。Mysql大多数锁策略都支持(常见mysql innodb),是系统开销最低但并发性最低的一个锁策略。事务t对整个表加读锁,则其他事务可读不可写,若加写锁,则其他事务增删改都不行。
行级锁:操作对象是数据表中的一行。是MVCC技术用的比较多的,但在MYISAM用不了,行级锁用mysql的储存引擎实现而不是mysql服务器。但行级锁对系统开销较大,处理高并发较好。
MVCC:多版本并发控制(MVCC,Multiversion Currency Control)。一般情况下,事务性储存引擎不是只使用表锁,行加锁的处理数据,而是结合了MVCC机制,以处理更多的并发问题。Mvcc处理高并发能力最强,但系统开销比最大(较表锁、行级锁),这是最求高并发付出的代价。
Autocommit:是mysql一个系统变量,默认情况下autocommit=1表示mysql把没一条sql语句自动的提交,而不用commit语句。所以,当要开启事务操作时,要把autocommit设为0,可以通过“set session autocommit=0; ”来设置
二、MVCC实现原理以及例化理解(包含些测试以便理解)
第一:先看看网络上几乎全部一样的理解,包括《高性能mysql第二版(中文版)》也如此说明,这样是很容易理解。但笔者觉得2个地方不妥,先看内容,在后面笔者会给出不妥地方用(1、2…)加粗标志出来,且给出测试证明。
Ps:这些只是外部看来的理解层面,深层次在第三点讲解
------------------------------------------
InnoDB实现MVCC的方法是,它存储了每一行的两个(1)额外的隐藏字段,这两个隐藏字段分别记录了行的创建的时间和删除的时间。在每个事件发生的时候,每行存储版本号,而不是存储事件实际发生的时间。每次事物的开始这个版本号都会增加。自记录时间开始,每个事物都会保存记录的系统版本号。依照事物的 版本来检查每行的版本号。在事物隔离级别为可重复读的情况下,来看看怎样应用它。
SELECT
Innodb检查没行数据,确保他们符合两个标准:
1、InnoDB只查找版本早于当前事务版本的数据行(也就是数据行的版本必须小于等于事务的版本),这确保当前事务读取的行都是事务之前已经存在的,或者是由当前事务创建或修改的行
2、行的删除操作的版本一定是未定义的或者大于当前事务的版本号。确定了当前事务开始之前,行没有被删除(2)
符合了以上两点则返回查询结果。
INSERT
InnoDB为每个新增行记录当前系统版本号作为创建ID。
DELETE
InnoDB为每个删除行的记录当前系统版本号作为行的删除ID。
UPDATE
InnoDB复制了一行。这个新行的版本号使用了系统版本号。它也把系统版本号作为了删除行的版本。
----------------------------------------------
(1) 不是两个,是三个。
1DB_TRX_ID:一个6byte的标识,每处理一个事务,其值自动+1,上述说到的“创建时间”和“删除时间”记录的就是这个DB_TRX_ID的值,如insert、update、delete操作时,删除操作用1个bit表示。 DB_TRX_ID是最重要的一个,可以通过语句“show engine innodb status”来查找,如下:
-----------------------------------------
……
TRANSACTIONS
------------
Trx id counter 0 430621
Purge done for trx\'s n:o < 0 430136 undo n:o < 0 0
History list length 7
……
------------------------------------------
2DB_ROLL_PTR: 大小是7byte,指向写到rollback segment(回滚段)的一条undo log记录(update操作的话,记录update前的ROW值)
3DB_ROW_ID: 大小是6byte,该值随新行插入单调增加,当由innodb自动产生聚集索引时,聚集索引包括这个DB_ROW_ID的值,不然的话聚集索引中不包括这个值. 这个用于索引当中
(2) 这里的不是真正的删除数据,而是标志出来的删除。真正意义的删除是在commit的时候。网上的说法很容易让读者误解
(3) 这点上面没有标注,在insert操作时 “创建时间”=DB_ROW_ID,这时,“删除时间 ”是未定义的;在update时,复制新增行的“创建时间”=DB_ROW_ID,删除时间未定义,旧数据行“创建时间”不变,删除时间=该事务的DB_ROW_ID;delete操作,相应数据行的“创建时间”不变,删除时间=该事务的DB_ROW_ID;select操作对两者都不修改,只读相应的数据
第二、下面用图形化形式表示MVCC如何处理select、insert、delete、update
有两个事务A、B
假设开始时间顺序ABCD,且DB_TRX_ID满足以下情况
A. DB_TRX_ID = 2010
B. DB_TRX_ID = 2011
C. DB_TRX_ID = 2012
D. DB_TRX_ID = 2013
注意:
1、B. DB_TRX_ID> A. DB_TRX_ID是因为DB_TRX_ID的值是系统版本号的值,系统版本号是自动增加的,所以DB_TRX_ID也是自动增加。但是会出现这种情况,假如A事务开始后B事务开始前有一个insert操作插入一行数据(没有bengin、comint),则B. DB_TRX_ID= A. DB_TRX_ID+1+1 ,并不符合不是说系统版本号增量为1
GMT+8, 2024-12-22 17:31