一.事务的概述
如何开启事务?
- 显示事务:start transaction;//开启事务 commit;//提交事务 rollback;//回滚事务
- 隐式事务:set autocommit = true;默认开启,DDL,修改表结构(alter)等都是隐式提交。
事务的基本特征?
- "原子性"(atomicity) :要么都成功要么都失败(不可分割,必须是一起的)
- "一致性"(consistency) :是程序员的责任。要保证事物开始前的状态的一致性和事物结束后状态的一致性
- 比如汇款, 事务开始前:A:500 B:100,A给B汇款100,事务正常结束后:A:400 B:200 一致性就是要保证事务前后的两种状态分别A和B的一致(A和B的金额都正常) ,事务的一致性说的是"事物的结果",事务的其他特性是为了保证这个结果正确的(就是为了保证事物的一致性)
- "隔离性"(isolation):在并发环境中,并发的事务时相互隔离的,一个事务的执行不能不被其他事务干扰(相当于一个同步锁)
- "持久性"(durability):一旦事务提交,那么它对数据库中的对应数据的状态的变更就会永久保存到数据库中
事务的隔离级别?为了能保证数据的完整性和一致性,并且提高并发的效率
- RU (read uncommit):读未提交,可能回读到其他事务没有提交的数据,也叫"脏读"
- RC (read commit):读已提交,"不可重复读"。解决了脏读的问题
- oracle默认的隔离级别
- "同一个事务中同一个查询可能读的数据不一样" 比如A事务读取两次数据,第一次读完之后,B事务修改了数据的内容,A事务第二次读数据, 就会读到最新的数据,两次读的数据不一样,所以叫不可重复读。
- RR (repeatable read):"可重复读",其实也是读提交的数据。
- MySQL默认的隔离级别,MySQL其实通过MVCC解决了幻读问题。
- "同一个事务中同一个查询读的数据一定一样" 比如A事务读取两次数据,第一次读完之后,B事务修改了数据的内容,A事务第二次读数据, 就还是第一次读取的数据的值(也就是读了旧值,可以理解为第一次读完有缓存,第二次直接从缓存中取数据) 但是如果是B插入一条数据的话(insert),A第二次读就会发现多了一条数据,也就是"幻读(虚读)"。 (对于该隔离级别来说,本来再读的话应该还是之前的值,结果 发现多了数据,那好像出现幻觉了,哈哈哈哈)
- Serializalbe: 串行,一般不会使用,它会给每一 行读取的数据加锁,造成大量的等待和锁冲突。
事务的几种状态:
- 活动的:事务中的sql正在执行中。
- 部分提交的:当事务的最后一个操作完成时,但是由于操作都是在内存中进行,并没有刷新到磁盘时候的状态。
- 失败的:遇到错误。
- 中止的:如果中途发生失败的状态,就将事务进行回滚。
- 提交的:部分提交的状态刷新到磁盘。
ACID靠什么保证? "靠2个日志+MVCC"
- A (原子性):由undo log日志保证,记录需要回滚的日志信息(记录修改的逆操作)
- C (一致性):由其他三大特征来保证,程序代码实现就行.
- I (隔离性):由MVCC保证
- D (持久性):由内存+redo log保证,修改数据的同时在内存和redo log记录此次操作,宕机 的时候也可以从redo log中恢复数据。
日志的具体操作如下
二.redo log
组成:
- redo log buffer:保存在内存中。
- redo log file:保存在磁盘中进行持久化。
流程:
有了上面的流程,我们现在就是担心步骤3,因为redo log file同样也需要保证宕机不丢数据。
所以有了刷盘策略,调整innodb_flush_log_at_trx_commit参数
- 设置为0:每次提交事务不进行刷盘操作,innodb后台线程每隔1s进行刷盘。
- 设置为1:每次事务提交时进行刷盘操作。(默认)
- 设置为2:每次事务提交只会把redo log buffer写入page cache,由os自己决定什么时候进行刷盘。(page cache叫做文件系统缓存,是操作系统的缓存)
写入redo log buffer的过程?
- MySQL把底层的一次原子操作称为一个Mini-Transaction,简称mtr。每一个mtr包含一组redo日志,是不可分割的。
- 比如一个update语句,可能修改的记录不止一条,每一条就是一个mtr,每个mtr里又有一组数据。
- redo log buffer维护一个全局变量,每次写到redo log buffer是一个个不可分割的mtr。也就是log buffer的最小单位是mtr。但是不同事务不同语句的mtr是可以挨着放,没有要求。
关于redo log file的一些细节:
- redo日志文件不止一个,而是以日志文件组的形式出现的,以ib_logfile[数字]进行命名。
- 当写到最后一个日志的时候,重新回到第一个日志文件进行写。
三.undo 日志
作用:
- 回滚数据:记录记录修改的逆操作。注意:只是逻辑上恢复成原来的样子,所有的修改都被逻辑取消了,数据结构和页本事在回滚之后可能大不相同。
- MVCC
undo 的存储结构:
- innodb对undo log采用回滚段的方式,每一个回滚段有1024个段,每个段有很多的undo页。
- 每一个事务开启的时候,都需要申请一个undo页。但是每一个事务分配一个undo页是非常浪费的,于是提出了undo页的重用。
- 当事务提交的时候,就会放入一个链表中,然后判断undo页的使用空间是否小于3/4,小于就可以被重用,其他事务可以写在undo页的后面
undo log的删除
- 针对于insert操作:直接删除。
- 针对于update操作:形成版本链表,为MVCC提供服务。
整个日志流程
寄语:顶尖高手,比的是慢,是笨,是扎实,是聪明人下笨功夫!