MySQL中的事务
什么是事务
一组不可分割的操作就是事务。目前支持事务的MySQL存储引擎只有InnoDB和NDB
事务的特性
我们知道现在的程序基本都是都是并发运行的,数据库当然也不例外,往往数据库要同时处理多个操作数据库的请求,然后同时执行多个数据库操作,所以在这种并发情况下,事务应当具备如下几个特性:
原子性
事务是一组操作的集合,在执行事务的过程中,事务不能中断,要么全部执行,要么全不执行。
隔离性
一个事务的执行不会影响另一个事务的执行,包括事务中用到的数据。
一致性
一致性就是指数据是满足某些约束条件的。比如说表中的字段不能为空,表中的字段的值要具有唯一性,不能重复,而满足这些约束条件的数据我们就说它具有一致性。数据库是为了存储现实生活中的数据的,而现实生活中的很多约束是数据库不具备的,所以我们就需要在业务代码中来对存入数据库中的数据做约束。可以说原子性和隔离性的保证,都是为了保证数据的一致性。
持久性
一个事务一旦提交,那么它对数据库中数据的改变就是永久性的。也就是说改变要立即写到磁盘中,即使关机了,即使电脑被砸了,只要硬盘还在,事务对数据的操作就会一直存在。
事务的状态
事务在不同的状态下有不同的状态:
- active(存活的)
- partially committed(部分提交)
- failed(失败)
- aborted(终止的)
- committed(提交的)
事务在并发执行时的一致性问题
脏写
一个事务修改了另一个未提交事务的数据。
脏读
一个事务读到了另一个未提交事务修改过的数据。
不可重复读
一个事务修改了另一个未提交事务读取的数据。
幻读
一个事务根据某些条件查询出数据,另一个事务写入了和条件相符的数据。
问题产生的原因?
在并发环境中,我们没法完全保证隔离性,因为一旦完全保证隔离性,那就是串行了,但是串行的性能比较差,所以为了性能就难以避免的出现的一致性的问题。
如何保证一致性?
为了避免某些一致性问题,隔离级别应运而生。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 不可能 | 可能 | 可能 |
| REPEATABLE READ | 不可能 | 不可能 | 可能 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 |
从上到下隔离级别逐渐提高,对应的性能也就逐渐下降。之所以没有脏写,是因为脏写对数据一致性的影响巨大,所以不论哪种隔离级别都不允许脏写。
如何保证持久性?
事务的执行过程:
- 开启事务
- 执行事务中的操作
- 提交
这是数据库使用者感觉的操作过程,但实际上InnoDB并不是如此执行的,而是先把操作的结果缓存到内存中(部分提交状态),等到时机合适,再刷新到磁盘中去,这样就可以避免频繁随机I/O,造成性能浪费。试想一下,你执行完一个事务,然后突然断电了,你以为数据已经保存在了磁盘上,结果来电后发现数据库里什么也没有。。。
redo日志
redo日志就是为了保证持久性而生,它只记录每一次事务执行后数据有哪些改变,因为只记录有哪些改变,所以redo日志体积小,每个事务执行后产生的日志为一组,redo日志也是先存在内存中,等日志攒够了,再写入磁盘。即使出现了意外也可以通过redo日志来恢复数据,不过话又说回来了,如果redo日志在还没写入磁盘时断电了怎么办呢?
如何保证原子性?
事务的原子性就是一个事务要么全部执行,要么全不执行,但是事务执行到一半的时候如果发生点意外事务被迫中断就需要进行回滚,或者是执行到一半手动回滚。但这时已经执行了很多操作,数据产生了很大的改变,这是undo日志就应运而生,他记录了事务执行前的数据,一旦要回滚的时候就可以快速回到原来的样子。