事务的四大特性
谈起事务,自然是事务的四大特征
原子性
事务作为一个整体被执行,包含在其中的对数据库的操作要么全部都执行,要么都不执行。
一致性
指在事务开始之前和事务结束以后,数据不会被破坏,假如A账户给B账户转10块钱,不管成功与否,A和B的总金额是不变的。
隔离性
多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。
持久性
表示事务完成提交后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。
MySQL的3大文件
MySQL事务的处理,我们就需要知道3个文件bin_log,redo_log,undo_log。
下面我们主要以innodb为例介绍,innodb的内存结构如下
我们简单理解undo
_log和redo
_log区别:
事务提交前崩溃,通过 undo
_log
回滚事务
事务提交后崩溃,通过 redo
_log
恢复事务
redo_log
可以简单的理解他是重做日志。让事务具备持久性。他的操作步骤为:
1.事务首先会把数据页的数据放入缓冲池。
2.修改信息后,缓冲池中的数据就为脏页,将操作记录到redo_log_buffer中。
3.当commit(提交数据)时候redo_log_buffer刷到redo_log文件中,让脏页的数据具备持久化。可以在配置信息中配置刷盘频率,刷盘规则等。
4.redo_log文件在事务提交时,进行一次性刷盘,存入数据页中。
这里有个问题,为啥不直接把脏页数据刷盘到磁盘中呢?中间还要通过redo_log_buffer写到redo_log文件中,这样就绕了一圈才存储的,为什么呢?
其实这脏页数据是随机存储的,而redo_log是顺序存储的,顺序存储的执行效率要高很多。
undo_log
这文件一般是用于回滚操作的。一旦事务执行rollback操作时候,这文件就记录的信息就进行回滚。他主要保证了事务原子性。
我们事务执行时候可能会出现的现象:
当事务执行到一半时候,突然断电或数据库宕机等各种异常情况。
当事务执行过程中突然遇到ROLLBACK指令时候,进行回滚操作。
这里面存放的是事务的反向操作。例如:
事务中的insert操作,undo_log中记录的就是delete操作。
事务中的delete操作,undo_log中记录的就是insert操作。
事务中的update操作把字段值从a改成b,undo_log中记录的就是update操作把b改成a。
存储内容是按照mvcc(多版本并发控制)方式来进行存储的。
例如
数据结构
id int 主键id
name varchar(64) 名字
sex tinyint 性别
undo_log中存储的内容可能是
id | name | sex | trx_id | roll_pointer |
---|---|---|---|---|
1 | test | 1 | 1000 | 0x111 |
1 | 我的名字 | 1 | 1000 | 0x222 |
1 | 我的名字 | 0 | null | null |
trx_id:这里面存放的内容是事务id。
roll_pointer:这里存放内容是回滚指针,例如上面表格所示,第一条数据的roll_pointer指向第二条的地址,通过这样的联系把有关联的串在一起,形成一个版本链。ps:insert是不会有roll_pointer值。
如果发生回滚时候,就会根据这版本链进行恢复数据。
如果发生的是事务提交操作,那么这undo_log就没什么用了,等到后台线程中的master thread或purge thread进行回收操作。
bin_log
主要有2个作用:
1.数据恢复(文章后半部分)
2.数据库集群
bin_log这是二进制文件,里面记录的信息是每次数据库执行的insert(新增),update(修改),delete(删除)操作时候,记录的命令行,每次有对应操作向后追加信息。集群时候数据库和数据库之间数据同步通过这bin_log来进行的。
事务带来的问题
事务深度问题
从上面redo_log的操作,我们能看出来,事务一开始时候MySQL会去数据页中拿事务相关的数据放在缓冲池中,我们如果事务执行的操作太多,这样数据页大量的信息会放在缓冲池中,就会消耗大量的内存,需要等到这事务操作提交或回滚,缓冲池的数据才能被释放。
脏读(Dirty read)
当一个事务正在访问数据并且对其进行了修改,但是还没提交事务,这时另外一个事务也访问了这个数据,然后使用了这个数据,因为这个数据的修改还没提交到数据库,所以另外一个事务读取的数据就是“脏数据”,这种行为就是“脏读”,依据“脏数据”所做的操作可能是会出现问题的。
修改丢失(Lost of modify)
是指一个事务读取一个数据时,另外一个数据也访问了该数据,那么在第一个事务修改了这个数据之后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,这种情况就被称为修改丢失。例如:事务1读取表中数据A=20
,事务2也读取A=20
,事务1修改A=A-1
,事务2也修改A=A-1
,最终结果都是19
,但是事务1的修改记录丢失了。
不可重复读(Unrepeatableread)
指在一个事务内多次读取同一数据,在这个事务还没结束时,另外一个事务也访问了这个数据并对这个数据进行了修改,那么就可能造成第一个事务两次读取的数据不一致,这种情况就被称为不可重复读。
幻读(Phantom read)
幻读与不可重复读类似,幻读是指一个事务读取了几行数据,这个事务还没结束,接着另外一个事务插入了一些数据,在随后的查询中,第一个事务读取到的数据就会比原本读取到的多,就好像发生了幻觉一样,所以称为幻读。
事务隔离级别
说起MySQL的事务,自然首当其冲要先聊到他的事务隔离级别,不同的隔离级别,对事务的处理方式都不相同。
1.读未提交(READ-UNCOMMITTED)
读取还没提交的事务数据。可能造成脏读、不可重复读、幻读。
2.读已提交(READ-COMMITTED)
允许读取并发事务已经提交的数据,可以避免脏读,但是可能造成不可重复、幻读。
3.可重复读(REPEATABLE-READ)
MySQL默认事务隔离级别,对同一字段多次读取的结果都是一致的,除非本身事务修改,可以避免脏读和不可重复读,但是可能造成幻读。
会使用next lock锁进制,来防止幻读问题,但是引入锁进制后,锁的代价会比较高,比较耗费CPU资源,占用系统性能。
4.串读(SERIALIZABLE)
完全服从ACID的隔离级别,这是事务的最高级别,在每条读的数据上,加上锁,使之不可能相互冲突,可以避免脏读、不可重复读、幻读,但是事务的并发度就没有了。