【MySQL数据库教程天花板,mysql安装到mysql高级,强!硬!-哔哩哔哩】
【阿里巴巴Java开发手册】https://www.w3cschool.cn/alibaba_java
事务日志4种特性:原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢?
事务的隔离性由锁机制
实现。
而事务的原子性、一致性和持久性由事务的redo日志和undo日志来保证。
REDO LOG称为重做日志
,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性。
UNDO LOG称为回滚日志
,回滚行记录到某个特定版本,用来保证事务的原子性、一致性。
有的DBA或许会认为UNDO是REDO的逆过程,其实不然。REDO和UNDO都可以视为一种恢复操作
,但是:
redo log:是存储引擎层(innodb)生成的日志,记录的是
物理级别
上的页修改操作,比如页号xxx、偏移量yyy写入了‘zzz’数据。主要为了保证数据的可靠性;
undo log:是存储引擎(innodb)生成的日志,记录的是
逻辑操作
日志,比如对某一行数据进行了INSERT语句操作,那么undo log就记录一条与之相反的DELETE操作。主要用于事务的回滚
(undo log记录的是每个修改操作的逆操作
)和一致性非锁定读
(undo log回滚行记录到某种特定的版本—MVCC,即多版本并发控制)。
InnoDB存储引擎是以
页为单位
来管理存储空间的。在真正访问页面之前,需要把磁盘上
的页缓存到内存中的Buffer Pool
之后才可以访问。所有的变更都必须先更新缓冲池
中的数据,然后缓冲池中脏页
会以一定的频率被刷入磁盘(chekPoint机制
),通过缓冲池来优化CPU和磁盘之间的鸿沟,这样就可以保证整体的性能不会下降太快。
redo日志降低了刷盘频率
redo日志占用的空间非常小
存储表空间ID、页号、偏移量以及需要更新的值,所需的存储空间是很小的,刷盘快。
redo日志是顺序写入磁盘的
在执行事务的过程中,每执行一条语句,就可能产生若干条redo日志,这些日志是按照产生的顺序写入磁盘的
,也就是使用顺序IO,效率比随机IO快。
事务执行过程中,redo log不断记录
redo log跟bin log的区别,redo log是存储引擎层
产生的,而bin log是数据库层
产生的。假设一个事务,对表做10万行的记录插入,在这个过程中,一直不断的往redo log顺序记录,而bin log不会记录,直到这个事务提交,才会写入到bin log文件中。
Redo log可以简单分为以下两个部分:
重做日志的缓冲(redo log buffer)
,保存在内存中,是易失的。
在服务器启动时就像操作系统申请了一大片称之为redo log buffer的连续内存
空间,翻译成中文就是redo日志缓冲区。这片内存空间被划分成若干个连续的redo log block
。一个redo log block占用512字节
大小。
参数设置:innodb_log_buffer_size
redo log buffer大小,默认16M
,最大值是4096M,最小值为1M。
重做日志文件(redo log file)
,保存在硬盘中,是持久的。
体会:Write-Ahead Log(预先日志持久化):在持久化一个数据页之前,先将内存中相应的日志页持久化。
redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后
一定的频率
刷入到真正的redo log file中。这里的一定频率怎么看待呢?这就是我们要说的刷盘策略。
注意,redo log buffer刷盘到redo log file的过程并不是真正刷到磁盘中去,只是刷到了文件系统缓存
(page cache)中去(这是现代操作系统为了提高文件写入效率做的一个优化),真正的写入会交给系统自己来决定(比如page cache足够大了)。那么对于InnoDB来说就存在一个问题,如果交给系统来同步,同样如果系统宕机,那么数据也丢失了(虽然整个系统宕机的概率还是比较小的)。
针对这种情况,InnoDB给出
innodb_flush_log_at_trx_commit
参数,该参数控制commit提交事务时,如何将redo log buffer中的日志刷新到redo log file中。它支持三种策略:
设置为0
:表示每次事务提交时不进行刷盘操作。(系统默认master thread每隔1s进行一次重做日志的同步)。
设置为1
:表示每次事务提交时都将进行同步,刷盘操作(默认值
)。
设置为2
:表示每次事务提交时都只把redo log buffer内容写入page cache,不进行同步。由os自己决定什么时候同步到磁盘文件。
另外,InnoDB存储引擎有一个后台线程,每个1秒
,就会把redo log buffer
中的内容写到文件系统缓存(page cache
),然后调用刷盘操作。
也就是说一个没有提交事务的redo log
记录,也可能会刷盘。因为事务执行过程redo log记录是会写入redo log buffer
中,这些redo log记录会被后台线程
刷盘。
除了后台线程每秒1次
的轮询操作,还有一种情况,当redo log buffer
占用的空间即将达到innodb_log_buffer_size
(这个参数默认是16M)的一半的时候,后台线程会主动刷盘。
可通过show variables like 'innodb_flush_log_at_trx_commit';
查看
小结:innodb_flush_log_at_trx_commit=1
为1
时,只要事务提交成功,redo log
记录就一定在硬盘里,不会有任何数据丢失。
如果事务执行期间MySQL
挂了或宕机,这部分日志丢了,但事务并没有提交,所以日志丢了也不会有任何损失,可以保证ACID的D,数据绝对不会丢失,但是效率最差
的。
建议使用默认值
,虽然操作系统宕机的概率理论小于数据库宕机的概率,但是一般既然使用了事务,那么数据的安全相对来说更重要一些。
小结:innodb_flush_log_at_trx_commit=2
为2
时,只要事务提交成功,redo log buffer中的内容只写入文件缓存(page cache
)。
如果仅仅只是MySQL
挂了不会有任何数据丢失,但是操作系统宕机可能会有1
秒数据的丢失,这种情况下无法满足ACID中的D。但数值2肯定是效率最高的。
小结:innodb_flush_log_at_trx_commit=0
为0
时,master thread中每1
秒进行一次重做日志的fsync操作,因此实例crash最多丢失1秒钟内
的事务。(master thread是负责将缓冲池中的数据异步刷新到磁盘,保持数据的一致性)。
数值0的话,是一种折中的做法,它的IO效率理论是高于1的,低于2的,这种策略也有丢失数据的风险,也无法保证D。
redo log是事务持久性的保证,undo log是事务原子性的保证。在事务中
更新数据
的前置操作
其实是要先写入一个undo log
。
事务需要保证
原子性
,也就是事务中操作要么全部完成,要么什么也不做。但有时候事务执行到一半会出现一些情况,比如:
情况一:事务执行过程中可能遇到各种错误,比如服务器本身的错误
,操作系统错误
,甚至是突然断电
导致的错误。
情况二:程序员可以在事务执行过程中手动输入ROLLBACK
语句结束当前事务的执行。
以上情况出现,我们需要把数据改回原先的样子,这个过程称之为回滚
,这样就可以造成一个假象:这个事务看起来什么都没做,所以符合原子性
要求。
每当我们要对一条记录做改动时(这里的
改动
可以指INSERT、DELETE、UPDATE
),都需要“留一手” 一 一 把回滚时所需的东西记下来。比如:
你插入一条记录
时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉
就好了。(对于每个INSERT,InnoDB存储引擎会完成一个DELETE)。
你删掉了一条记录
,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入
到表中就好了。(对于每个DELETE,InnoDB存储引擎会执行一个INSERT)。
你修改了一条记录
,至少要把修改这条记录前的旧值都记录下来,这样之后回滚再把这条记录更新为旧值
就好了。(对于每个UPDATE,InnoDB存储引擎会执行一个相反的UPDATE,将修改前的行放回去)。
MySQL把这些为了回滚而记录的这些内容称之为
撤销日志
或者回滚日志
(即undo log
)。注意,由于查询操作(SELECT
)并不会修改任何用户记录,所以在查询操作执行时,并不需要记录
相应的undo日志。
此外,undo log
会产生redo log
,也就是undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护。
用户对undo日志可能
有误解
:undo用于将数据库物理地恢复到执行语句或事物之前的样子。但事实并非如此。undo是逻辑日志
,因此只是将数据库逻辑恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。
这是因为在多用户并发系统中,可能会有数十、数百甚至数千个并发事务。数据库的主要任务就是协调对数据记录的并发访问。比如,一个事务在修改当前一个页中某几条记录,同时还有别的事务在对同一个页中另几条记录进行修改。因此,不能将一个页回滚到事务开始的样子,因为这样会影响其他事务正在进行的工作。
undo的另一个作用是MVCC,即InnoDB存储引擎中MVCC的实现是通过undo来完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。
undo log
是逻辑日志,对事务回滚时,只是将数据库逻辑地恢复到原来的样子。
redo log
是物理日志,记录的是数据页的物理变化,undo log不是redo log的逆过程。