💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 事务 事务语句: ``` start transaction select ... update ... update ... commit; ``` ## 事务特点 ACID 1. 原子性 Atomicity 2. 一致性 Consistency 3. 隔离性 Isolation 4. 持久性 Durability **原子性** 一个事务必须被视为一个不可分割的最小工作单元,事务当中的操作要么全部提交成功,要么全都不能提交成功。这里的原子性更多的指的是对数据库的修改操作(DML语言)。 实现原理:undo log,可以将没有commit的新值恢复到旧值。 **持久性** 一旦事务提交了,其操作所做的修改会永久的保存到数据库中。 实现原理:redo log。 **隔离性** 一般来说,一个事务所做的修改在最终提交之前,对于其他事务是不可见,但是这是根据不同的隔离级别而确定的,对于不同的隔离级别可能会有不同的影响。隔离性用在并发的场景才起作用。 &nbsp; ### 隔离级别 :-: ![](https://img.kancloud.cn/b3/03/b3039c93f6b15d02cb64da134eb13e8a_824x258.png) 1. READ UNCOMMITTED 读取未提交 在该隔离级别中,一个事务中可以看到另外一个事务未commit的修改内容。发生这种情况就叫做`脏读:`即事务可以读取到其他事务未提交的操作。 ~~~  tips:所以这种级别一般很少使用 ~~~ 2. READ COMMITTED 提交读 可以读取别的事务已经提交的内容,满足事务隔离性的定义,但是有可能会出现不可重复读。 `不可重复读:`在同个事务中两次读取数据库的内容前后不一致。 3. REPEAABLE READ 可重复读 该级别解决了脏读的问题,同时解决了在同个事务中两次读取数据库的内容不一致的不可重复读的问题。但是可能会出现幻读(phantom Read)的问题。 `幻读:`当某个事务在读取某个范围的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,就会产生幻读。 `该隔离级别是MYSQL中默认的级别。一般都会设置成RR。` 4. SERIALIZABLE 可串行化 Serializables是数据库最高的隔离级别,使用该级别可以强制事务串行执行。强制事务串行执行了之后也就不会出现脏读、不可重复读、幻读问题了。 设置不同隔离级别: ``` set session transaction isolation level read committed; # 设置为读已提交隔离级别。 当前会话生效 ``` &nbsp; **一致性** 数据库总是从一个一致性的状态转化到另外一个一致性的状态。AID最终是为了保证一致性。 &nbsp; ## 多版本并发控制MVCC Multi Version Concurrency Control,属于一种乐观锁,不用在读写冲突的时候加上锁,属于一种`快照读`的方式,提高并发性。而在每次读写冲突都加锁是属于一种悲观锁,属于`当前读`的方式。InnoDB采取乐观锁、快照读的方式来提供并发性。 - 当前读:读取当前最新的版本号。 - 快照读:读取历史的版本号,这样即是在同一行进行读写操作也没有关系。 MVCC的实现方式: 通过在每行记录后面保存两个隐藏的列,一个列保存了行的创建版本号,一个列保存了行的删除版本号。每次开启一个新的事务,系统版本号就会自动递增。同时会将该系统版本号作为事务的版本号。读操作时只会读取该事务开始前的数据库快照数据。 读写冲突种类,MVCC就是用来解决读写冲突问题,提供并发性。 - 读写 - 写写 **MVCC的实现原理:** 1. 版本链 2. undo log 3. Read view ### 版本链 数据库的每行数据中都会有隐藏的几个字段,分别是`db_trx_id`、`db_roll_pointer`、`db_row_id`。 - db_trx_id:6byte,最近修改或者插入的事务id。 - db_roll_pointer:版本链的关键,7byte,**回滚指针**,指向这条记录的上一个版本,这些版本数据存储与一个特定的区域。**主要用于配合undo日志。** - db_row_id:隐含自增的ID,没有创建聚簇索引的时候会添加上。 - 是否删除的字段,数据库删除的时候并不会真正的删除,而是修改flag的标志位。 版本链的是实现主要通过db_roll_pointer指针和undo日志配合实现,行中的db_roll_pointer指向undo log。每次记录更新的时候,就会将旧值放到undo log中,undo log中也有一个db_roll_pointer,用于指向上一个版本的undo log段的内容,最终形成了一条记录了历史版本内容的链。也就是快照!**同时也会在undo log中存放事务的id,可用于Read View中判断版本的可见性。** :-: ![](https://img.kancloud.cn/54/fb/54fb4cccc1a379c5deb2ea460941c9d2_1080x565.png) 图片来源:https://www.php.cn/mysql-tutorials-460111.html &nbsp; ### undo log 主要用于记录数据被修改之前的日志,在表修改之前会先把数据拷贝到undo log里,当事务进行回滚的时候可以用undo log日志的内容进行恢复。有如下的两个主要用途 - 保证事务的原子性和一致性。 - 用于MVCC快照读,因为在undo log中保留了历史的版本数据。 两种主要的undo log: 1. insert undo log 事务失败时用于回滚,事务提交时可以直接丢弃。 2. undate undo log 事务在进行命令delete或者update时产生的undo log,在事务回滚和快照读的时候都需要。 &nbsp; ### Read View 读视图,**解决哪些数据可以被当前事务读取的问题。** 用于解决事务可见性的问题,每当开启一个事务的时候,就会生成一个当前数据系统的一个视图,该视图包含如下的几个内容: - trx_ids:当前系统未提交的事务。 - low_limit_id:创建read view时当前系统的**最大事务版本号+1**。 - up_limit_id:创建read view时当前系统的未提交事务的**最小版本号**。 - creator_trx_id:创建当前read view的事务版本号。 可见性的判断: 1. db_trx_id < up_limit_id 在当前事务创建前数据已经存在,可读。 2. db_trx_id == creator_trx_id 当前事务id和数据id一样,证明数据在当前数据创建,可以读取。 3. db_trx_id >= low_limit_id 数据在当前read view创建之后创建,不可读取。 4. db_trx_id是否在trx_ids中? - 在:不可读。 - 不在,可以显示。 &nbsp; ### MVCC在RR和RC两种隔离级别所起的作用 对于RR隔离级别,在第一个读的时候会创建一个Read View,此后每次读用的都是同一个Read View,如果有其他的事务id在该Read View的trx_ids中,则其对行的数据的修改对当前事务是不可见,也就实现了可重复读。 对于RC隔离级别,在每次读取的时候都会创建一个Read View,这样有可能读取到其他事务的内容。 &nbsp; 总之:MVCC就是在使用RR、RC隔离级别下,调用select语句的时候可以查询历史版本数据的内容,从而避免了在高并发场景下的大量读写冲突问题。其中undo log用于解决历史版本数据保存在哪的问题,read view用于解决哪些历史版本的数据应该被当前事务读取到的问题,版本链则用于串联所有的历史版本数据,方便找到所需要的历史版本数据。 &nbsp;