让它全部失败呢?这个时候我们必须要回滚。
原子性,在 InnoDB 里面是通过 undo log 来实现的,它记录了数据修改之前的值(逻
辑日志),一旦发生异常,就可以用 undo log 来实现回滚操作。
第二个,一致性,consistent,指的是数据库的完整性约束没有被破坏,事务执行的
前后都是合法的数据状态。比如主键必须是唯一的,字段长度符合要求。
除了数据库自身的完整性约束,还有一个是用户自定义的完整性。
比如说转账的这个场景,A 账户余额减少 1000,B 账户余额只增加了 500,这个时
候因为两个操作都成功了,按照我们对原子性的定义,它是满足原子性的, 但是它没有
满足一致性,因为它导致了会计科目的不平衡。
还有一种情况,A 账户余额为 0,如果这个时候转账成功了,A 账户的余额会变成
-1000,虽然它满足了原子性的,但是我们知道,借记卡的余额是不能够小于 0 的,所以
也违反了一致性。用户自定义的完整性通常要在代码中控制。
第三个,隔离性,Isolation,我们有了事务的定义以后,在数据库里面会有很多的
事务同时去操作我们的同一张表或者同一行数据,必然会产生一些并发或者干扰的操作,
那么我们对隔离性的定义,就是这些很多个的事务,对表或者行的并发操作,应该是透
明的,互相不干扰的。通过这种方式,我们最终也是保证业务数据的一致性。
最后一个叫做持久性,Durable,事务的持久性是什么意思呢?我们对数据库的任意
的操作,增删改,只要事务提交成功,那么结果就是永久性的,不可能因为我们系统宕
机或者重启了数据库的服务器,它又恢复到原来的状态了。这个就是事务的持久性。
持久性怎么实现呢?数据库崩溃恢复(crash-safe)是通过什么实现的?
持久性是通过 redo log 和 double write 双写缓冲来实现的,我们操作数据的时候,
会先写到内存的 buffer pool 里面,同时记录 redo log,如果在刷盘之前出现异常,在
重启后就可以读取 redo log 的内容,写入到磁盘,保证数据的持久性。
当然,恢复成功的前提是数据页本身没有被破坏,是完整的,这个通过双写缓冲
(double write)保证。
原子性,隔离性,持久性,最后都是为了实现一致性。
1.5 数据库什么时候会出现事务
无论是我们在 Navicat 的这种工具里面去操作,还是在我们的 Java 代码里面通过
API 去操作,还是加上@Transactional 的注解或者 AOP 配置,其实最终都是发送一个
指令到数据库去执行,Java 的 JDBC 只不过是把这些命令封装起来了。
我们先来看一下我们的操作环境。版本(5.7),存储引擎(InnnoDB),事务隔离
级别(RR)。
selectversion();
show variables like’%engine%’;
showglobal variables like “tx_isolation”;
执行这样一条更新语句的时候,它有事务吗?
update student set sname = ‘猫老公 111’ where id=1;
实际上,它自动开启了一个事务,并且提交了,所以最终写入了磁盘。
这个是开启事务的第一种方式,自动开启和自动提交。
InnoDB 里面有一个 autocommit 的参数(分成两个级别, session 级别和 global
级别)。