事务
是数据库(编程语言)用户执行的一个语句,从开始开始到结束为止,这就造成了事务要么是执行成功结束,要么是执行失败而结束。
而事务是控制事件的基本单位,既然是用来控制事件,就会针对各种事件产生了不同特征,也就是我们常说的ACID,原子性、一致性、隔离性、持久性。
- 原子性:当前事务操作非失败即成功,只有这两钟可能,原子性是由undo log日志保证的。undo log记载着数据修改前的信息,例如我插入一条数据,undo log会记载我创建表的信息或者删除该条数据的记录,一旦事务执行过程中出现异常,就会执行回滚,InnoDB执行引擎利用undo log记录下的数据,将数据恢复到事务开始之前。
- 隔离性:在事务并发执行时,保证相互之间不干扰,避免多个操作对同一个数据产生影响,于是定义了四种隔离级别:read uncommit(读未提交)、read commit (读已提交)、repeatable read (可重复复读)、serializable (串行),隔离级别越高效率也是越低。
- 持久性:将数据持久化到磁盘上,也就是说一旦事务提交对数据库的改变就是永久性的。持久性是由redo log日志保证的,当我们修改数据时,mysql会先把这条记录的持久‘页’找到,然后把该‘页’加载到内存中,同时写一份redo log,日志记载着对该页的修改。
- 一致性:事物或者说行为的目的,以上三种特性均是为了保证一致性,出现异常就需要回滚事务,需要应用程序代码保证。
隔离
四种隔离保证需要锁的相关知识,先大致了解一下锁:可以简单的分为行锁和表锁,行锁是作用在索引之上的,如果没有命中索引,那么锁就对应为整表,即表锁。索引是为了更快的查询,部分优化查询也是增加索引操作的,创建索引就是创建一个B+树。
行锁可分为读锁和写锁,行锁是共享,多个事务可以同时读取一份资源,但是不允许其他事务修改,写锁则是排队执行。
有了锁就可以对其他事务进行隔离操作了,
- 读未提交:事务B读取到了事务A还未操作完的数据,也是脏读。从锁的角度看是写锁没办法排除没有锁的读操作(不是读锁)。
- 读已提交、可重复读:默认隔离级别,为了解决读操作的脏读问题,但是又不能给读操作加锁导致数据库性能降低,就来了一个多版本并发控制(MVCC),其实就是生成数据快照,读操作读取该快照的数据,快照的生成又可以分为语句级快照、事务级快照,语句级快照对应的就是读已提交,事务级快照则是可重复读。
- 串行:可重复读会存在幻读的问题,在一个事务内读取到了别的事务插入的数据,导致前后读取不一致,而串行不允许事务的并发,从而最安全。
MVCC基本原理
它是通过undo log和版本信息来实现的,undo log记录数据之前的信息和版本,版本信息就是在查询时生成的read view,其中有几个重要字段
- trx_ids: 当前系统活跃(未提交)事务版本号集合
- low_limit_id: 下次要创建的版本号
- up_limit_id: 创建当前read view 时“系统正处于活跃事务最小版本号”
- creator_trx_id: 创建当前read view的事务版本号
数据库每行之中还有几个不显示的字段 - db_trx_id:记录该记录的事务Id
- db_roll_pointer:这条记录的上一个版本
- db_row_id:隐藏主键
最后就是根据不同的隔离条件选择不同的版本信息进行展示