事务: 指作为单个逻辑工作单元(Service方法)执行的一系列操作(数据库操作),要么全部执行,要么全部不执行。事务可以看做是一组任务,通常对应了一个业务方法,这些任务要么全部成功,要么全部失败。
本地事务有这么几个特征:
一次事务只连接一个支持事务的数据库(一般来说都是关系型数据库)
事务的执行结果保证[ACID]
会用到数据库锁
事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)四个特性,简称 ACID,缺一不可。
官方解释
Atomicity requires that each transaction be “all or nothing”: if one part of the transaction fails, then the entire transaction fails, and the database state is left unchanged. An atomic system must guarantee atomicity in each and every situation, including power failures, errors and crashes. To the outside world, a committed transaction appears (by its effects on the database) to be indivisible (“atomic”), and an aborted transaction does not happen.
关键词在于:
all or nothing,它的意思是数据库要么被修改了,要么保持原来的状态。所谓保持原来的状态不是我先insert再delete,而是压根就没有发生过任何操作。因为insert然后再delete实际上还是修改了数据库状态的,至少在数据库日志层面是这样。
indivisible,不可分割,一个事务就是一个最小的无法分割的独立单元,不允许部分成功部分失败。
利用Innodb的undo log,undo log名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的sql语句,他需要记录你要回滚的相应日志信息。 例如:
当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据;
当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作;
当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作
undo log记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
官方解释
The consistency property ensures that any transaction will bring the database from one valid state to another. Any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This does not guarantee correctness of the transaction in all ways the application programmer might have wanted (that is the responsibility of application-level code), but merely that any programming errors cannot result in the violation of any defined rules.
一致性要求任何写到数据库的数据都必须满足于预先定义的规则(比如余额不能小于0、外键约束等),简单来说就是在任何时间点都不能出现违反一致性要求的状态。几个并行执行的事务,其执行结果必须与按某一顺序 串行执行的结果相一致,Mysql通过排他锁来保证一致性.
官方解释
The durability property ensures that once a transaction has been committed, it will remain so, even in the event of power loss, crashes, or errors. In a relational database, for instance, once a group of SQL statements execute, the results need to be stored permanently (even if the database crashes immediately thereafter). To defend against power loss, transactions (or their effects) must be recorded in a non-volatile memory.
持久性的关键在于一旦“完成提交”(committed),那么数据就不会丢失。undolog实现事务原子性,redolog实现事务的持久性,**Redo Log记录的是新数据的备份。**在事务提交前,只要将Redo Log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是Redo Log已经持久化。系统可以根据Redo Log的内容,将所有数据恢复到最新的状态。
1.A.事务开始.
2.B.记录A=1到undo log.
3.C.修改A=3.
4.D.记录A=3到redo log.
5.E.记录B=2到undo log.
6.F.修改B=4.
7.G.记录B=4到redo log.
8.H.将redo log写入磁盘。
I.事务提交
通过undolog(记录旧数据用来反悔)保证事务的原子性,redolog(记录新数据用来保存到磁盘)保证持久性。
redo log进行刷盘比对数据页刷盘效率高,具体表现如下
redo log体积小,毕竟只记录了哪一页修改了啥,因此体积小,刷盘快。
redo log是一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。
隔离性要求如果两个事务修改同一个数据,则必须按顺序执行,并且前一个事务如果未完成,那么未完成的中间状态对另一个事务不可见。
SQL 标准定义了四种隔离级别,MySQL 全都支持。这四种隔离级别分别是:
读未提交(READ UNCOMMITTED)
读提交 (READ COMMITTED)
可重复读 (REPEATABLE READ)
串行化 (SERIALIZABLE)
以上几个概念是事务隔离级别要实际解决的问题:脏读,不可重复读,幻读
脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了不一定最终存在的数据,这就是脏读。读未提交会造成脏读;读提交可以解决脏读问题。
可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的,那不可重复读就是一个事务多次读取到的数据是不一致的,通常针对数据更新(UPDATE)操作。即:同一个事务中多次使用相同条件读取到的数据是不一样的。
set global transaction isolation level repeatable read;
可重复读,这也是Mysql
默认的事务隔离策略,但是该事务隔离级别没办法解决幻读问题。
事务A 按照一定条件进行数据读取, 期间事务B 插入了相同搜索条件的新数据,事务A再次按照原先条件进行读取时,发现了事务B 新插入的数据 称为幻读 , 即:事物A两次读取相同条件的数据读到的条数不一样。
MySQL 的可重复读隔离级别其实解决了幻读问题,它使用到了“间隙锁”。
串行化是4种事务隔离级别中隔离效果最好的,解决了脏读、可重复读、幻读的问题,但是效果最差,它将事务的执行变为顺序执行,与其他三个隔离级别相比,它就相当于单线程,后一个事务的执行必须等待前一个事务结束。
事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(Read uncommitted) | 可能 | 可能 | 可能 |
读提交(Read committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable reads) | 不可能 | 不可能 | 可能 |
串行化(Serializable) | 不可能 | 不可能 | 不可能 |
MySQL 事务隔离其实是依靠锁来实现的,加锁自然会带来性能的损失。而读未提交隔离级别是不加锁的,所以它的性能是最好的,没有加锁、解锁带来的性能开销。但有利就有弊,这基本上就相当于裸奔啊,所以它连脏读的问题都没办法解决。
MVCC的全称是**“多版本并发控制”(Multi Version Concurrency Control),。这项技术使得InnoDB的事务隔离级别下执行一致性读操作有了保证,换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值,一个行记录数据有多个版本对快照数据,这些快照数据在undo log中**。 如果一个事务读取的行正在做DELELE或者UPDATE操作,读取操作不会等行上的锁释放,而是读取该行的快照版本。
这是一个可以用来增强并发性的强大的技术,因为这样的一来的话查询就不用等待另一个事务释放锁。这项技术在数据库领域并不是普遍使用的。一些其它的数据库产品,以及mysql
其它的存储引擎并不支持它。
可重复读是在事务开始的时候生成一个当前事务全局性的快照,而读提交则是每次执行语句的时候都重新生成一次快照。
脏读,幻读,不可重复读,都是发生在一个事务在写,一个事务在读的时候出现的问题,而丢失更新发生在两个事务都在做写操作的时候出现的,丢失更新分为:回滚丢失更新(一类)和覆盖丢失更新(二类)