目录
(1)事物的概述使用
(2)事物的基本特性
(3)事物的隔离级别
(4)事物实现机制
(5)分布式事务
一、事物的概述使
经典例子:举一个转账的例子,假设你朋友向你借10000元,你打开APP,乐呵呵的把钱转了,你的卡里已经少了10000元,但是你打电话给朋友时,你朋友说没有收到啊,你这时候肯定卖银行怎么不靠谱,没到账怎么把我卡里的钱给扣了 .
这个例子应该很好理解。只要稍微复杂的业务,要保证数据一致性,事务就必不可少。
目前MySQL主要使用的存储引擎就是:InnoDB ,其次MyISAM。
事物简单使用,例如laravel,需要事务的时候就只需要(php laravel):
DB::beginTrasaction();
DB::rollback() or DB::commit复制
savepoint identifier 可以创建事务的一个保存点,执行回滚操作时可以回滚到指定保存点,不需要回滚整个事务。
打个比例,假设你去旅游到目标地需要三个行程,第一程 深圳到广州高铁,第二程 从广州飞到雅加达,第三程 雅加达飞到某岛。如果再第三程 飞机取消行程,事务要回滚,如果要你再会深圳,你肯定会心理一万个草泥马。因为再进入事务,第一步和第二步是不变的,所以不需要回滚,直接回滚第三步即可。
二、事物的基本特性
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
(以上概念抄自菜鸟教程,这些都容易理解的)
三、事物的隔离级别
事务特性拥有隔离性,有四种级别:读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。MySQL系统默认的隔离级别是:可重复读(repeatable read)。
MySQL隔离级别的不同会导致的常见异常:
脏读:所谓脏读是指一个事务中访问到了另外一个事务未提交的数据
如果会话 2 更新 age 为 10,但是在 commit 之前,会话 1 希望得到 age,那么会获得的值就是更新前的值。或者如果会话 2 更新了值但是执行了 rollback,而会话 1 拿到的仍是 10。这就是脏读。
幻读:一个事务读取2次,得到的记录条数不一致(注意:幻读指行数区别,另外一个事务增加或者删除符合调教的数据)
不可重复读:一个事务读取同一条记录2次,得到的结果不一致 。在事务过程中,由于另外一个事务修改提交了当前条件的数据,导致读取的数据前后不一致
不同的事务隔离级别会导致不同的问题:
3.1 READ-UNCOMMITTED
3.1-3.4 来自于掘金( https://juejin.im/post/5cbc049de51d456e7b372089 ) 侵删
READ-UNCOMMITTED 中文叫未提交读,即一个事务读到了另一个未提交事务修改过的数据,整个过程如下图:

如上图,SessionA和SessionB分别开启一个事务,SessionB中的事务先将id为1的记录的name列更新为'lisi',然后Session 中的事务再去查询这条id为1的记录,那么在未提交读的隔离级别下,查询结果由'zhangsan'变成了'lisi',也就是说某个事务读到了另一个未提交事务修改过的记录。但是如果SessionB中的事务稍后进行了回滚,那么SessionA中的事务相当于读到了一个不存在的数据,这种现象也称为脏读。
可见READ-UNCOMMITTED是非常不安全。
3.2 READ COMMITTED
READ COMMITTED 中文叫已提交读,或者叫不可重复读。即一个事务能读到另一个已经提交事务修改后的数据,如果其他事务均对该数据进行修改并提交,该事务也能查询到最新值。如下图:

在第4步 SessionB 修改后,如果未提交,SessionA是读不到,但SessionB一旦提交后,SessionA即可读到SessionB修改的内容。
从某种程度上已提交读是违反事务的隔离性的。
3.3 REPEATABLE READ
REPEATABLE READ 中文叫可重复读,即事务能读到另一个已经提交的事务修改过的数据,但是第一次读过某条记录后,即使后面其他事务修改了该记录的值并且提交,该事务之后再读该条记录时,读到的仍是第一次读到的值,而不是每次都读到不同的数据。如下图:

InnoDB默认是这种隔离级别,SessionB无论怎么修改id=1的值,SessionA读到依然是自己开启事务第一次读到的内容。
3.4 SERIALIZABLE
SERIALIZABLE 叫串行化, 上面三种隔离级别可以进行 读-读 或者 读-写、写-读三种并发操作,而SERIALIZABLE不允许读-写,写-读的并发操作。如下图:

SessionB 对 id=1 进行修改的时候,Sessio。【nA 读取id=1则需要等待 SessionB 提交事务。可以理解SessionB在更新的时候加了X锁。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制
四、事物实现机制
redo_log 实现持久化和原子性,而undo_log实现一致性,二种日志均可以视为一种恢复操作,redo_log是恢复提交事务修改的页操作,而undo_log是回滚行记录到特定版本。前者用于对事务的影响进行撤销,后者在错误处理时对已经提交的事务进行重做 。二者记录的内容也不同,redo_log是物理日志,记录页的物理修改操作,而undo_log是逻辑日志,根据每行记录进行记录。由于锁的内容比较多,记录在另外一篇中。
在事务写入过程
当我们在一个事务中尝试对数据进行写时,它会先将数据从磁盘读入内存,并更新内存中缓存的数据,然后生成一条重做日志并写入重做日志缓存,当事务真正提交时,MySQL 会将重做日志缓存中的内容刷新到重做日志文件,再将内存中的数据更新到磁盘上,图中的第 4、5 步就是在事务提交时执行的。
4.1 redo log的恢复机制
checkpoint,即检查点。在undolog中写入检查点,表示在checkpoint前的事务都已经完成commit或者rollback了,也就是检查点前面的事务已经不存在数据一致性的问题了(此处暂时不会深入解释)
MySQL的checkpoint https://www.cnblogs.com/lintong/p/4381578.html 想要详细了解
Innodb的事务日志是指Redo log,简称Log,保存在日志文件ib_logfile里面(去mysql数据目录下看下)。Innodb还有另外一个日志Undo log,但Undo log是存放在共享表空间里面的(ibdata*文件,存储的是check point日志序列号)。
LSN(Log Sequence Number) 日志序列号,Innodb里,LSN占8个字节,且是单调递增的,代表的含义有: 重做日志写入的总量、checkpoint的位置、页的版本。
假设在LSN=10000的时候数据库出现故障,磁盘中checkpoint为10000,表示磁盘已经刷新到10000这个序列号,当前redolog的checkpoint是13000,则需要恢复10000-13000的数据
innodb_flush_log_at_trx_commit 参数解析
set @@global.innodb_flush_log_at_trx_commit = 0; -- 0,1,2
show variables like 'innodb_flush_log_at_trx_commit';复制0:log buffer将每秒一次地写入log file中,并且log file的flush(刷到磁盘)操作同时进行。该模式下在事务提交的时候,不会主动触发写入磁盘的操作。
1:每次事务提交时MySQL都会把log buffer的数据写入log file,并且flush(刷到磁盘)中去,该模式为系统默认。
2:每次事务提交时MySQL都会把log buffer的数据写入log file,但是flush(刷到磁盘)操作并不会同时进行。该模式下,MySQL会每秒执行一次 flush(刷到磁盘)操作。
当设置为0,该模式速度最快,但不太安全,mysqld进程的崩溃会导致上一秒钟所有事务数据的丢失。
当设置为1,该模式是最安全的,但也是最慢的一种方式。在mysqld 服务崩溃或者服务器主机crash的情况下,binary log 只有可能丢失最多一个语句或者一个事务。
当设置为2,该模式速度较快,也比0安全,只有在操作系统崩溃或者系统断电的情况下,上一秒钟所有事务数据才可能丢失。
4.2 undo log
redo log一旦提交意味着持久化了,但是有时候需要对其进行rollback操作,那就需要undo log。
undo log是逻辑日志,只是将数据库逻辑的恢复到原来的样子。并不能将数据库物理地恢复到执行语句或者事务之前的样子。虽然所有的逻辑修改均被取消了,但是数据结构和页本身在回滚前后可能不一样了。
既然是逻辑日志,可以理解为它存储的是SQL, 在事务中使用的每一条 INSERT 都对应了一条 DELETE,每一条 UPDATE 也都对应一条相反的 UPDATE 语句。
undo log 存放在数据库内部的一个特殊段(segment)中,也叫undo段,存在于共享表空间中。
undo log实现了事务的一致性,可以通过undo log恢复到事务之前的逻辑状态,保证一致性。
undo log 还可以实现MVCC(Multi-Version Concurrency Control ,多版本并发控制),多版本并发控制其实可以通过 undo log 形成一个事务执行过程中的版本链,每一个写操作会产生一个版本,数据库发生读的并发访问时,读操作访问版本链,返回最合适的结果直接返回。从而读写操作之间没有冲突,提高了性能。
五、分布式事务
分布式事务大多数情况不会用到,暂不多述
资源管理器:管理事务的提交和回滚,向事务提供资源。
事务管理器:和资源管理器通信,协调完成事务的处理。
用于执行分布式事务的过程使用两个阶段;
第一阶段:所有的分支被预备。他们被事务管理器告知要准备提交,每个分支资源管理器记录分支的行动并指示认为的可行性。
第二阶段:事务管理器告知资源管理器是否要提交或者回滚。如果预备分子时,所有的分支指示他们将能够提交,那么所有的分支被告知提交。如果有一个分支出错,那么就全部都要回滚。特殊情况下,只有一个分支的时候,第二阶段则被省略。
分布式事务主要作用在于确保事务的一致性和完整性。他利用分布式的计算环境,将多个事务性的活动合并成一个事务单元,这些事务组合在一起构成原子操作,这些事务的活动要么一起执行并提交事务,要么回滚所有的操作,从而保证了多个活动之间的一致性和完整性。
注意事项:
事物在遇到错误时并不会主动回滚,需要主动执行rollback,所以在执行事务时通常必会使用try catch