数据库事务第一章---事务日志

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据库事务第⼀章---事务⽇志
⾸先抛出问题,望⼤家读完后,可以解答:什么是数据库事务?为什么要有数据库事务,解决什么问题?
⼀:知识回顾
⼀个合理的数据库设计,需要保证ACID原则(虽然被提及太多次,但这⾥还是要啰嗦⼀下)
原⼦性(Atomicity)
原⼦性是指事务是⼀个不可分割的⼯作单位,事务中的⼀系列操作要么都发⽣,要么都不发⽣。

⼀致性(Consistency)
事务前后数据的完整性必须保持⼀致。

隔离性(Isolation)
事务的隔离性是多个⽤户并发访问数据库时,数据库为每⼀个⽤户开启的事务,不能被其他事务的操作数据所⼲扰,多个并发事务之间要相互隔离。

持久性(Durability)
持久性是指⼀个事务⼀旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发⽣故障也不应该对其有任何影响
举个说烂的例⼦。

从A账户转账100元到B账户去。

第⼀步 A账户扣去100,第⼆步,B账户上增加100。

这两步操作肯定在同⼀个事务⾥。

原⼦性:要么这两步都不发⽣,要么都发⽣,不可以发⽣了第⼀步,然后第⼆步不发⽣了。

⼀致性:两步发⽣后,数据库⾥的数据要保持⼀致,总钱数肯定不能减少或者增多。

隔离性:这个例⼦中不能体现,下⼀章节会细讲
持久性:假如第⼀步后,服务宕掉了,等到重启后,数据库得有恢复数据的能⼒,要么回滚第⼀步,要么继续执⾏第⼆步。

⼆:提出问题
在详细介绍数据库技术层⾯是如何实现上述原则前,我们需要先了解系统程序是如何修改数据库数据的。

我们⼤家都知道,数据库的最终数据都是存在硬盘上的。

如果在设计程序时,需要频繁的和硬盘的数据进⾏IO交互,响应时间就会⾮常缓慢(为什么IO交互时间慢,因为这⾥是随机IO,需要在磁盘上多个地⽅移动磁头,这⾥不再细说),所以这是我们需要避免的。

为了解决这个问题,数据库在⾃⾝的设计上,它也作出了努⼒。

当第⼀次从数据库⾥查询出⼀系列数据时,数据库会将该查询结果数据缓存在它⾃⼰的内存中(也称⾼速缓冲区,就是⼀块内存结构,⽤来存储数据备份,所有数据库⽤户共享),所以其实每⼀次的查询,服务器进程都会尝试先从⾼速缓冲区⾥捞数据,如果命中,就直接返回,避免了直接和硬盘进⾏IO交互,如果命中不了,才会去硬盘上找。

修改操作也如此,会先去修改内存中的数据,然后写⼊硬盘上去。

问题来了:
1. ⾼速缓冲区是什么?
mysql使⽤的是InnoDB存储引擎。

InnoDB层有⾃⼰的缓冲区,存放在主机内存中,它的⽬的主要是在应⽤层管理⾃⼰的数据,避免慢速的读写操作影响了InnoDB的响应时间。

这个缓冲区分为两块,innodb buffer pool 和 relog buffer ,前者就是上述的⾼速缓冲
区,InnoDB buffer pool存储了从磁盘设备读到的InnoDB数据,也缓冲了对InnoDB数据写。

relog buffer 下⾯的内容会着重介绍,这⾥先略过。

2. 如果修改完了内存数据,还没来得及写⼊硬盘,数据库服务挂了,操作会不会丢失?
数据库为此同样也做出了努⼒。

它也怕写⼊硬盘时出现故障,所以它⼲脆先不写⼊硬盘⾥,⽽是先通过事务⽇志的⽅式记录操作⾏为。

三:引出主题 -- 事务⽇志
什么是事务⽇志呢?为什么要有事务⽇志呢?
事务⽇志是真实存在主机硬盘上的⽇志⽂件。

简单来说,每⼀次的修改操作,都先修改⾼速缓存区拷贝的数据(这⾥不太准确,下⾯会详述),然后将修改⾏为记录到事务⽇志⾥去,之后,才异步将修改内容持久化到硬盘上去。

事务⽇志可以帮助提⾼事务效率:
使⽤事务⽇志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改⾏为记录到持久在硬盘上的事务⽇志中,⽽不⽤每次都将修改的数据本⾝持久到磁盘。

事务⽇志采⽤的是追加的⽅式,因此写⽇志的操作是磁盘上⼀⼩块区域内的顺序I/O,⽽不像随机I/O需要在磁盘的多个地⽅移动磁头,所以采⽤事务⽇志的⽅式相对来说要快得多。

事务⽇志持久以后,内存中被修改的数据在后台可以慢慢刷回到磁盘。

如果数据的修改已经记录到事务⽇志并持久化,但数据本⾝没有写回到磁盘,此时系统崩溃,存储引擎在重启时能够⾃动恢复这⼀部分修改的数据。

⽬前来说,⼤多数存储引擎都是这样实现的,我们通常称之为预写式⽇志(Write-Ahead Logging),也称⽇志先⾏,修改数据需要写两次磁盘(第⼀次是将更新⾏为记录到事务⽇志,第⼆次是真正的将更新后的数据记录到数据库⽂件中)。

可能看了上⾯的东西,你还是不清楚事务⽇志是个什么,也不太清楚数据存储的流程是怎么⾛的,那么我们现在可以总结⼀下,确实⽐较复杂,⽹上查阅的资料也概括的不全⾯。

MySQL redo log存储状态
1. 存在redo log buffer中,物理上在MySQL进程内存中;
2. 写到磁盘write, 但是没有持久化fsync,物理上是在⽂件系统的page cache⾥⾯;
3. 持久化到磁盘,对应的是hard disk.
当有修改操作发⽣时,
第⼀步,会去修改 innodb buffer pool(上⾯有过介绍)内存中的数据,同时也会在 relog buffer 内存中记录下操作步骤,由于这两个都是修改内存的操作,速度⾮常快。

第⼆步,InnoDB 有⼀个后台线程,每隔 1 秒,就会把 redo log buffer 中的⽇志,调⽤ write ⽅法将 redo log 写到⽂件系统的 page cache(这个就是操作系统的⽂件存储区域),这⾥就是在写事务⽇志⽂件,存储在硬盘上,然后调⽤ fsync 同步⽅法持久化到磁盘。

那这个log到底记录了什么?分两部分
1. redo log
在系统启动的时候,就已经为redo log分配了⼀块连续的存储空间,以顺序追加的⽅式记录Redo Log,通过顺序IO来改善性能。

所有的事务共享redo log的存储空间,它们的Redo Log按语句的执⾏顺序,依次交替的记录在⼀起。

如下⼀个简单⽰例:
记录1:<trx1, insert...>
记录2:<trx2, delete...>
记录3:<trx3, update...>
记录4:<trx1, update...>
记录5:<trx3, insert...>
2. undo log
undo log主要为事务的回滚服务。

在事务执⾏的过程中,除了记录redo log,还会记录⼀定量的undo log。

undo log记录了数据在每个操作前的状态,如果事务执⾏过程中需要回滚,就可以根据undo log进⾏回滚操作。

单个事务的回滚,只会回滚当前事务做的操作,并不会影响到其他的事务做的操作。

以下是undo+redo事务的简化过程
假设有2个数值,分别为A和B,值为1,2, 分别更为3,4
1. start transaction;
2. 记录 A=1 到undo log;
3. update A = 3;
4. 记录 A=3 到redo log;
5. 记录 B=2 到undo log;
6. update B = 4;
7. 记录B = 4 到redo log;
8. 将redo log刷新到磁盘
9. commit
在1-8的任意⼀步系统宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响。

如果在8-9之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时redo log已经持久化。

若在9之后系统宕机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据redo log把数据刷回磁盘。

所以,redo log其实保障的是事务的持久性和⼀致性,⽽undo log则保障了事务的原⼦性。

扩展:为了控制 redo log 的写⼊策略,InnoDB 提供了 innodb_flush_log_at_trx_commit 参数,控制 redo log 的刷新。

它有三种可能取值:
设置为 0 的时候,表⽰每次事务提交时都只是把 redo log 留在 redo log buffer 中 ; 这样可能丢失1s的事务数据。

设置为 1 的时候,表⽰每次事务提交时都将 redo log 直接持久化到磁盘;这样的话,数据库对IO的要求⾮常⾼,如果底层硬件提供的IOPS⽐较差,MySQL数据库并发很快就会由于硬件IO的问题⽽⽆法提升。

(当然,InnoDB的组提交⽅法为降低IOPS做了很⼤优化)设置为 2 的时候,表⽰每次事务提交时都只是把 redo log 写到 page cache。

如果只是MySQL数据库挂掉了,由于⽂件系统没有问题,那么对应的事务数据并没有丢失。

只有在数据库所在的主机操作系统损坏或者突然掉电的情况下,数据库的事务数据可能丢失1秒之类的事务数据。

这样的好处就是,减少了事务数据丢失的概率,⽽对底层硬件的IO要求也没有那么⾼(log buffer写到⽂件系统中,⼀般只是从log buffer的内存转移的⽂件系统的内存缓存中,对底层IO没有压⼒)
注意,事务执⾏中间过程的 redo log 也是直接写在 redo log buffer 中的,这些 redo log 也会被后台线程⼀起持久化到磁盘。

也就是说,⼀个没有提交的事务的 redo log,也是可能已经持久化到磁盘的。

数据丢失的场景:
如果主机掉电或者MySQL异常宕机,innodb buffer pool将⽆法及时刷新到磁盘,那么InnoDB就只能从上⼀个checkpoint使⽤redo log来前滚;⽽redo log buffer如果不能及时刷新到磁盘,那么由于redo log中数据的丢失,就算使⽤redo 前滚,⽤户提交的事务由于没有真正的记录到⾮易失型的磁盘介质中,就丢失掉了。

引出⼀个问题:是否保证原⼦性,就可以保持了⼀致性呢?
下⼀个章节会回答这个问题。

介绍数据库事务的隔离性是什么?解决了什么问题?如何实现的?。

相关文档
最新文档