讲三者区别比较好的文章
数据库通常借助⽇志来实现事务,常见的有undo log、redo log,undo/redo log都能保证事务特性,undolog实现事务原⼦性,redolog实现事务的持久性。为了最⼤程度避免数据写⼊时io瓶颈带来的性能问题,MySQL采⽤了这样⼀种缓存机制:当query修改数据库内数据时,InnoDB先将该数据从磁盘读取到内存中,修改内存中的数据拷贝,并将该修改⾏为持久化到磁盘上的事务⽇志(先写redo log buffer,再定期批量写⼊),⽽不是每次都直接将修改过的数据记录到硬盘内,等事务⽇志持久化完成之后,内存中的脏数据可以慢慢刷回磁盘,称之为Write-Ahead Logging。事务⽇志采⽤的是追加写⼊,顺序io会带来更好的性能优势。
为了避免脏数据刷回磁盘过程中,掉电或系统故障带来的数据丢失问题,InnoDB采⽤事务⽇志(redo log)来解决该问题。
数据库数据存放的⽂件称为data file;
⽇志⽂件称为log file;
数据库数据是有缓存的,如果没有缓存,每次都写或者读物理disk,那性能就太低下了。数据库数据的缓存称为data buffer,⽇志(redo)
缓存称为log buffer。
内存缓冲池
buffer pool如果mysql不⽤内存缓冲池,每次读写数据时,都需要访问磁盘,必定会⼤⼤增加I/O请求,导致效率低下。所以Innodb引擎在读写数据时,把相应的数据和索引载⼊到内存中的缓冲池(buffer pool)中,⼀定程度的提⾼了数据读写的速度。
buffer pool:占最⼤块内存,⽤来存放各种数据的缓存包括有索引页、数据页、undo页、插⼊缓冲、⾃适应哈希索引、innodb存储的锁信息、数据字典信息等。⼯作⽅式总是将数据库⽂件按页(每页16k)读取到缓冲池,然后按最近最少使⽤(lru)的算法来保留在缓冲池中的缓存数据。如果数据库⽂件需要修改,总是⾸先修改在缓存池中的页(发⽣修改后即为脏页dirty page),然后再按照⼀定的频率将缓冲池的脏页刷新
到⽂件。
表空间
表空间可看做是InnoDB存储引擎逻辑结构的最⾼层。表空间⽂件:InnoDB默认的表空间⽂件为ibdata1。
段:表空间由各个段组成,常见的段有数据段、索引段、回滚段(undo log段)等。
区:由64个连续的页组成,每个页⼤⼩为16kb,即每个区⼤⼩为1MB。
页:每页16kb,且不能更改。常见的页类型有:数据页、Undo页、系统页、事务数据页、插⼊缓冲位图页、插⼊缓冲空闲列表页、未
压缩的⼆进制⼤对象页、压缩的⼆进制⼤对象页。
redo log 和undo log
为了满⾜事务的持久性,防⽌buffer pool数据丢失,innodb引⼊了redo log。为了满⾜事务的原⼦性,innodb引⼊了undo log。
Undo log 是为了实现事务的原⼦性。还⽤Undo Log来实现多版本并发控制(简称:MVCC)。
delete/update操作的内部机制
当事务提交的时候,innodb不会⽴即删除undo log,因为后续还可能会⽤到undo log,如隔离级别为repeatable read时,事务读取的都是开启事务时的最新提交⾏版本,只要该事务不结束,该⾏版本就不能删除,即undo log不能删除。
但是在事务提交的时候,会将该事务对应的undo log放⼊到删除列表中,未来通过purge来删除。并且提交事务时,还会判断undo log分配的页是否可以重⽤,如果可以重⽤,则会分配给后⾯来的事务,避免为每个独⽴的事务分配独⽴的undo log页⽽浪费存储空间和性能。
通过undo log记录delete和update操作的结果发现:(insert操作⽆需分析,就是插⼊⾏⽽已)
事务的所有操作,要么全部完成,要不都不做,不能只做⼀半。如果在执⾏的过程中发⽣了错误,要回到事务开始时的状态,所有的操
作都要回滚。
Undo Log的原理很简单,为了满⾜事务的原⼦性,在操作任何数据之前,⾸先将数据备份到⼀个地⽅(这个存储数据备份的地⽅称为Undo Log)。然后进⾏数据的修改。如果出现了错误或者⽤户执⾏了ROLLBACK语句,系统可以利⽤Undo Log中的备份将数据恢复到事务
开始之前的状态。
假设有A、B两个数据,值分别为1,2。进⾏+2的事务操作。
A.事务开始.
B.记录A=1到undo log.
C.修改A=3.
D.记录B=2到undo log.
E.修改B=4.
F.将undo log写到磁盘。
G.将数据写到磁盘。
H.事务提交
这⾥有⼀个隐含的前提条件:‘数据都是先读到内存中,然后修改内存中的数据,最后将数据写回磁盘’。
之所以能同时保证原⼦性和持久化,是因为以下特点:
A. 更新数据前记录Undo log。
B. 为了保证持久性,必须将数据在事务提交前写到磁盘。只要事务成功提交,数据必然已经持久化。
C. Undo log必须先于数据持久化到磁盘。如果在G,H之间系统崩溃,undo log是完整的,可以⽤来回滚事务。
D. 如果在A-F之间系统崩溃,因为数据没有持久化到磁盘。所以磁盘上的数据还是保持在事务开始前的状态。
缺点:每个事务提交前将数据和Undo Log写⼊磁盘,这样会导致⼤量的磁盘IO,因此性能很低。
redo log就是保存执⾏的SQL语句到⼀个指定的Log⽂件,当mysql执⾏数据恢复时,重新执⾏redo log记录的SQL操作即可。
更正:
redo log通常是物理⽇志,记录的是数据页的物理修改,⽽不是某⼀⾏或某⼏⾏修改成怎样怎样,它⽤来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后⼀次提交的位置)。
引⼊buffer pool会导致更新的数据不会实时持久化到磁盘,当系统崩溃时,虽然buffer pool中的数据丢失,数据没有持久化,但是系统可以根据Redo Log的内容,将所有数据恢复到最新的状态。redo log在磁盘上作为⼀个独⽴的⽂件存在。默认情况下会有两个⽂件,名称分别ib_logfile0和ib_logfile1。
参数innodb_log_file_size指定了redo log的⼤⼩;innodb_log_file_in_group指定了redo log的数量,默认为2; innodb_log_group_home_dir
指定了redo log所在路径。
ROLLBACK语句,系统可以利⽤Undo Log中的备份将数据恢复到事务开始之前的状态。与redo log不同的是,磁盘上不存在单独的undo log⽂件,它存放在数据库内部的⼀个特殊段(segment)中,这称为undo段(undo segment),undo段位于共享表空间内。
Innodb为每⾏记录都实现了三个隐藏字段:
6字节的事务ID(DB_TRX_ID)
7字节的回滚指针(DB_ROLL_PTR)
隐藏的ID
redo log的记录内容
undo log和 redo log本⾝是分开的。innodb的undo log是记录在数据⽂件(ibd)中的,⽽且innodb将undo log的内容看作是数据,因此对undo log本⾝的操作(如向undo log中插⼊⼀条undo记录等),都会记录redo log。undo log可以不必⽴即持久化到磁盘上。即便丢失了,也可以通过redo log将其恢复。因此当插⼊⼀条记录时:
②Undo + Redo事务的简化过程
A.事务开始.
B.记录A=1到undo log.
C.修改A=3.
D.记录A=3到redo log.
E.记录B=2到undo log.
F.修改B=4.
G.记录B=4到redo log.
H.将redo log写⼊磁盘。
I.事务提交
A-G的过程是在内存中进⾏的,相应的操作记录在redo log buffer(B&E),redo log buffer(E&G)中,事务执⾏结果(此时未提交)也存在db buffer中(C&F),buffer满了就写⼊磁盘当中,如果buffer存储的事务数量都是1个,也就意味着是将⽇志⽴即刷⼊磁盘,那么数据的⼀致性很好保证。如果存储多个的话,是⼀次事务完成就会先将redo log同步到磁盘当中并有⼀个状态位来记录是否提交,再去真正的提交事务,将db buffer 中的数据同步到DB的磁盘当中去。要保证在db buffer中的内容写⼊磁盘数据库⽂件之前,应当把log buffer的内容写⼊磁盘⽇志⽂件。这种⽅式可以减少磁盘IO,增加吞吐量。不过,这种⽅式适⽤于⼀致性要求不⾼的情景。因为如果出现断电等系统故障,log buffer、db buffer中的完成的事务还没同步到磁盘会丢失。像银⾏这种要求事务较⾼的⼀致性,就⼀定要保证每次事务都要记录到磁盘中,
如果服务器down了的时候去redo log中恢复,重做⼀次已经提交的事务。
数据持久化
buffer pool中维护⼀个按脏页修改先后顺序排列的链表,叫flush_list。根据flush_list中页的顺序刷数据到持久存储。按页⾯最早⼀次被
修改的顺序排列。正常情况下,dirty page什么时候flush到磁盘上呢?
恢复策略
前⾯说到未提交的事务和回滚了的事务也会记录Redo Log,因此在进⾏恢复时,这些事务要进⾏特殊的处理.有2种不同的恢复策略:
A. 进⾏恢复时,只重做已经提交了的事务。(返回给客户端的是已经提交⼀定保证数据的可恢复持久性)
B. 进⾏恢复时,重做所有事务包括未提交的事务和回滚了的事务。然后通过Undo Log回滚那些未提交的事务。⽐如在B-E过程中down机了,那么恢复时根据undo log去重新模拟当时的情景(但是如果log buffer的空间很⼤,log没有同步到磁盘这个过程就没有办法来进⾏,同时由于事务没有提交,返回给客户端的值是未提交成功,所以也没有关系)。