💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 事务 ### 什么是事务? 事务是逻辑上的一组操作,要么都执行,要么都不执行 ### 事务的四大特性 原子性(Atomicity)、一致性(Consistent)、隔离性(Isalotion)、持久性(Durable)**,简称为`ACID` 1. **原子性**:是指事务的原子性操作,**对数据的修改要么全部执行成功,要么全部失败,实现事务的原子性,undo log保证** 2. **一致性**:是指**执行事务前后的状态要一致**,可以理解为数据一致性。**undo log+redo log保证** 3. **隔离性**:侧重指**事务之间相互隔离,不受影响**,这个与事务设置的隔离级别有密切的关系。**锁(共享、排他)+mvcc保证** 4. **持久性**:则是指在**一个事务提交后,这个事务的状态会被持久化到数据库中,也就是事务提交,对数据的新增、更新将会持久化到数据库中。redo log保证** Mysql默认的隔离级别:**可重复读**。 REPEATABLE-READ(可重读)**事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是**REPEATABLE-READ(可重读)**已经可以完全保证事务的隔离性要求,即达到了 SQL标准的**SERIALIZABLE(可串行化) ### 并发事务处理带来的问题 * 更新丢失(Lost Update):事务A和事务B选择同一行,然后基于最初选定的值更新该行时,由于两个事务都不知道彼此的存在,就会发生丢失更新问题 * 脏读(Dirty Reads):事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据 * 不可重复读(Non-Repeatable Reads):事务 A 多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。 * 幻读(Phantom Reads):幻读与不可重复读类似。它发生在一个事务A读取了几行数据,接着另一个并发事务B插入了一些数据时。在随后的查询中,事务A就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。 ### 并发事务处理带来的问题的解决办法 * “更新丢失”通常是应该完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此,防止更新丢失应该是应用的责任。 * “脏读” 、 “不可重复读”和“幻读” ,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决: * 一种是加锁:在读取数据前,对其加锁,阻止其他事务对数据进行修改。 * 另一种是数据多版本并发控制(`MultiVersion Concurrency Control`,简称`MVCC`或`MCC`),也称为多版本数据库:不用加任何锁, 通过一定机制生成一个数据请求时间点的一致性数据快照 (`Snapshot`),并用这个快照来提供一定级别 (语句级或事务级) 的一致性读取。从用户的角度来看,好象是数据库可以提供同一数据的多个版本。 ### 隔离级别 **读未提交-读提交-可重复读-串行化**,**级别越来越高,隔离也就越来越严实,到最后的串行化,当出现读写锁冲突的时候,后面的事务只能等前面的事务完成后才能继续访问** 1. **读未提交:一个事务还没提交时,它做的变更就能被其他事务看到,从而产生了脏读**。 2. **读提交:一个事务提交之后,它做的变更才能被其他事务看到,从而产生不可重复读。** 3. **可重复读:一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,(产生幻读,在Mysql的中通过MVCC多版本控制的一致性视图解决了不可重复读问题以及通过间隙锁解决了幻读问题。)** 4. **串行化:对于同一行记录,若是读写锁发生冲突,后面访问的事务只能等前面的事务执行完才能继续访问**。 * 脏读:读到其他事务未提交的数据; * 不可重复读:前后读取的数据不一致; * 幻读:前后读取的记录数量不一致。 **幻读和不可重复读的区别:** >不可重复读的重点是修改,幻读的重点在于新增或者删除 **不可重复读的重点是修改**:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为中间有其他事务提交了修改) **幻读的重点在于新增或者删除**:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(因为中间有其他事务提交了插入/删除) ## MVCC ### 什么是MVCC MVCC是一种多版本并发控制机制 MVCC中规定**每一行数据都有多个不同的版本,一个事务更新操作完后就生成一个新的版本** ### MVCC是为了解决什么问题 * 大多数的MYSQL事务型存储引擎,如,InnoDB,Falcon以及PBXT都不使用一种简单的行锁机制.事实上,他们都和MVCC–多版本并发控制来一起使用. * 大家都应该知道,锁机制可以控制并发操作,但是其系统开销较大,而MVCC可以在大多数情况下代替行级锁,使用MVCC,能降低其系统开销. ### MVCC实现 MVCC是通过保存数据在某个时间点的快照来实现的,不同存储引擎的MVCC实现是不同的,典型的有乐观并发控制和悲观并发控制. ### MVCC 具体实现分析(原理)   InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,分别保存了这个行的创建时间,一个保存的是行的删除时间。这里存储的并不是实际的时间值,而是系统版本号(可以理解为事务的ID),每开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的ID. ### REPEATABLE READ(可重读)隔离级别下MVCC如何工作: * SELECT:InnoDB会根据以下两个条件检查每行记录: * InnoDB只查找版本早于当前事务版本的数据行,这样可以确保事务读取的行,要么是在开始事务之前已经存在要么是事务自身插入或者修改过的 * 行的删除版本号要么未定义,要么大于当前事务版本号,这样可以确保事务读取到的行在事务开始之前未被删除 * 只有符合上述两个条件的才会被查询出来 * INSERT:InnoDB为新插入的每一行保存当前系统版本号作为行版本号 * DELETE:InnoDB为删除的每一行保存当前系统版本号作为行删除标识 * UPDATE:InnoDB为插入的一行新纪录保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为删除标识 保存这两个额外系统版本号,使大多数操作都不用加锁。使数据操作简单,性能很好,并且也能保证只会读取到符合要求的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作和一些额外的维护工作。 **MVCC 只在 COMMITTED READ(读提交)和REPEATABLE READ(可重复读)两种隔离级别下工作**。 ## 数据库日志 ### 事务日志可以帮助提高事务效率 * 使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。 * 事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。 * 事务日志持久以后,内存中被修改的数据在后台可以慢慢刷回到磁盘。 * 如果数据的修改已经记录到事务日志并持久化,但数据本身没有写回到磁盘,此时系统崩溃,存储引擎在重启时能够自动恢复这一部分修改的数据。 ## 事务的实现 事务的实现是基于数据库的存储引擎。不同的存储引擎对事务的支持程度不一样。MySQL 中支持事务的存储引擎有 InnoDB 和 NDB。 ### 事务的实现就是如何实现ACID特性。 事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过 事务日志实现 。 ### 事务是如何通过日志来实现的 事务日志包括:**重做日志redo和回滚日志undo** * redo log(重做日志)实现了持久化 在innoDB的存储引擎中,事务日志通过重做(redo)日志和innoDB存储引擎的日志缓冲(InnoDB Log Buffer)实现。事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是DBA们口中常说的“日志先行”(Write-Ahead Logging)。 当事务提交之后,在Buffer Pool中映射的数据文件才会慢慢刷新到磁盘。此时如果数据库崩溃或者宕机,那么当系统重启进行恢复时,就可以根据redo log中记录的日志,把数据库恢复到崩溃前的一个状态。未完成的事务,可以继续提交,也可以选择回滚,这基于恢复的策略而定。 在系统启动的时候,就已经为redo log分配了一块连续的存储空间,以顺序追加的方式记录Redo Log,通过顺序IO来改善性能。所有的事务共享redo log的存储空间,它们的Redo Log按语句的执行顺序,依次交替的记录在一起。 * undo log(回滚日志)  实现一致性 undo log 主要为事务的回滚服务。在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。单个事务的回滚,只会回滚当前事务做的操作,并不会影响到其他的事务做的操作。 Undo记录的是已部分完成并且写入硬盘的未完成的事务,默认情况下回滚日志是记录下表空间中的(共享表空间或者独享表空间) 二种日志均可以视为一种恢复操作,redo_log是恢复提交事务修改的页操作,而undo_log是回滚行记录到特定版本。二者记录的内容也不同,redo_log是物理日志,记录页的物理修改操作,而undo\_log是逻辑日志,根据每行记录进行记录。 ### mysql中的日志文件都有哪些 ①重做日志 redo log ②回滚日志 undo log ③二进制日志 bin log ④错误日志 error log ⑤慢查询日志 slow query log ⑥一般查询日志 general log ⑦中继日志 relay log ### Mysql中redolog 和 binlog的作用和区别 ①redolog属于物理日志,记录改数据页的更新状态内容 ②binlog属于逻辑日志,记录更新的操作语句的原始逻辑 ③redolog是循环写,日志的空间大小是固定的;binlog是追加写入,写完一个写下一个,不会进行数据的覆盖 ### 作为主从复制和数据恢复使用哪一个? redo log作为服务器异常宕机后事务数据自动恢复使用,binlog可以作为主从复制和数据恢复使用,binlog没有自动crash-safe的能力 ### bin log 作用:用于主从复制,实现主从同步 ### redo log 作用:确保日志的持久性,防止在发生故障,脏页未写入磁盘。重启数据库会进行redo log执行重做,达到事务一致性 ### undo log 作用:保证数据的原子性,记录事务发生之前的一个版本,用于回滚,innodb事务可重复读和读取已提交 隔离级别就是通过mvcc+undo实现 ### relay log 作用:用于数据库主从同步,将主库发来的bin log保存在本地,然后从库进行回放 ## Innodb 事务为什么要两阶段提交? * 先写 redolog 后写binlog。假设在 redolog 写完,binlog 还没有写完的时候,MySQL 进程异常重启,这时候 binlog 里面就没有记录这个语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的**「binlog 丢失」**,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。 * 先写 binlog 后写 redolog。如果在 binlog 写完之后 crash,由于 redolog 还没写,崩溃恢复以后这个事务无效,所以这一行c的值是0。但是 binlog 里面已经记录了“把c从0改成1”这个日志。所以,在之后用 binlog 来恢复的时候就**「多了一个事务出来」**,恢复出来的这一行 c 的值就是 1,与原库的值不同。 ## 分布式事务 https://mp.weixin.qq.com/s/x0y0_NmA8rrWxhK-m7duVA