数据库系统原理学习笔记
一、事务
概念
事务是指满足ACID特性的一组操作。
ACID
1.原子性(Atomicity)
事务被视为不可分割的最小单元,事务包含的所有操作要么全部提交成功,要么全部失败回滚。
回滚可以用回滚日志(Undo Log)来实现,回滚日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。
2.一致性(Consistency)
数据库在事务执行前后保持一致性状态,在一致性状态下,所有事务对同一个数据的读取结果都是相同的。
3.隔离性(Isolation)
事务所做的修改在最终提交之前,对其它事务是不可见的。
4.持久性(Durability)
事务一旦被提交,其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
系统发生崩溃可以用重做日志(Redo Log)进行恢复,从而实现持久性。与回滚日志记录数据的逻辑修改不同,重做日志记录的是数据页的物理修改!
二、并发一致性问题
在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题
丢失修改
一个事务的更新操作被另外一个事务的更新操作替换,如下图所示:
读脏数据
指当前事务可以读到其他事务未提交的数据,如下图:
不可重复读
指在一个事务内多次读取同一数据集合,在这一事务还未结束前,另一事务也访问了该同一数据集合并做了修改,导致同一事务中两次读取的数据可能不一致,如下图:
幻影读
幻影读本质上也属于不可重复读的情况,其强调的是事务多次读取某个范围的数据,而另一事务在该范围内插入/删除了的数据,使得同一事务两次读取结果不同,如下图:
注:不可重复读和幻影读的区别在于,不可重复读强调的是数据被修改了,而幻影读强调的是范围内数据新增或减少了。
产生并发不一致性问题的主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过*来实现,但是*操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题。
三、四种隔离级别
未提交读
指事务中的修改,即使没有提交,对其他事务也是可见的。
提交读
事务在最终提交之前,对其他事务是不可见的,也就是说一个事务只能读取到已经提交的事务所做的修改。
可重复读
同一事务中多次读取同一数据的结果是一样的。
可串行化
所有事务串行执行,事务间互不干扰,因此不会出现并发一致性问题。
四、多版本并发控制
多版本并发控制(MVCC)是MySQL的InnoDB存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读两种隔离级别。未提交读隔离级别总是读取最新的数据行,要求很低,无需使用MVCC。可串行化隔离级别需要对所有读取的行都加锁,单纯使用MVCC无法实现。
基本思想
加锁能解决多个事务同时执行时出现的并发一致性问题,又由于实际场景中读操作往往多于写操作,因此我们引入了读写锁来避免不必要的加锁操作(读/读没有互斥关系)。但加锁方式中,读和写操作仍然是互斥的,MVCC利用多版本的思想,使得读和写操作没有了互斥关系:写操作更新最新的版本快照,而读操作去读旧版本的快照。
在MVCC中事务的修改操作(如delete、insert、update)会为数据行新增一个版本快照。
版本号
- 系统版本号 SYS_ID:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。
- 事务版本号 TRX_ID :事务开始时的系统版本号。
Undo日志
MVCC 的多版本指的是多个版本的快照,快照存储在 Undo 日志中,该日志通过回滚指针 ROLL_PTR 把一个数据行的所有快照连接起来。
例如在 MySQL 创建一个表 t,包含主键 id 和一个字段 x。我们先插入一个数据行,然后对该数据行执行两次更新操作。
INSERT INTO t(id, x) VALUES(1, "a");
UPDATE t SET x="b" WHERE id=1;
UPDATE t SET x="c" WHERE id=1;
因为没有使用 START TRANSACTION 将上面的操作当成一个事务来执行,根据 MySQL 的 AUTOCOMMIT 机制,每个操作都会被当成一个事务来执行,所以上面的操作总共涉及到三个事务。快照中除了记录事务版本号 TRX_ID 和操作之外,还记录了一个 bit 的 DEL 字段,用于标记是否被删除。
INSERT、UPDATE、DELETE 操作会创建一个日志,并将事务版本号 TRX_ID 写入。DELETE 可以看成是一个特殊的 UPDATE,还会额外将 DEL 字段设置为 1。
ReadView
MVCC 维护了一个 ReadView 结构,主要包含了当前系统未提交的事务列表 TRX_IDs {TRX_ID_1, TRX_ID_2, …},还有该列表的最小值 TRX_ID_MIN 和 TRX_ID_MAX。
在进行 SELECT 操作时,根据数据行快照的 TRX_ID 与 TRX_ID_MIN 和 TRX_ID_MAX 之间的关系,从而判断数据行快照是否可以使用:
-
TRX_ID < TRX_ID_MIN,表示该数据行快照时在当前所有未提交事务之前进行更改的,因此可以使用。
-
TRX_ID > TRX_ID_MAX,表示该数据行快照是在事务启动之后被更改的,因此不可使用。
-
TRX_ID_MIN <= TRX_ID <= TRX_ID_MAX,需要根据隔离级别再进行判断:
(1)提交读:如果 TRX_ID 在 TRX_IDs 列表中,表示该数据行快照对应的事务还未提交,则该快照不可使用。否则表示已经提交,可以使用。
(2)可重复读:不管 TRX_ID 在不在 TRX_IDs 列表中都不可以使用。因为如果可以使用的话,那么其它事务也可以读到这个数据行快照并进行修改,那么当前事务再去读这个数据行得到的值就会发生改变,也就是出现了不可重复读问题。
在数据行快照不可使用的情况下,需要沿着 Undo Log 的回滚指针 ROLL_PTR 找到下一个快照,再进行上面的判断。
上一篇: SQL Server 游标
下一篇: SQL server游标的使用
推荐阅读
-
数据库系统原理学习笔记
-
Python学习笔记之if语句(一)
-
python学习笔记------类的魔术方法与format
-
Tensorflow学习笔记-过度拟合问题
-
深度学习中Dropout原理解析
-
ROS学习笔记八:The Tranform Listener
-
深度学习与PyTorch笔记2
-
Redis学习笔记(二)【原创】 博客分类: 缓存 redissentinelcluster
-
tensorflow深度学习实战笔记(一):使用tensorflow slim自带的模型训练自己的数据
-
HTML&CSS基础学习笔记1.12—引入样式表 博客分类: HTML html引入样式表csshreflink