最近在研究mysql的执行流程,以及redolog,undolog,binlog的原理,了解了很多,不过还是有很多疑问。
下面是我根据网上资料整理的部分知识点。(可能有不对的地方)
我的疑问是undolog、redolog、binlog的刷盘顺序和commit的执行顺序是怎样的?
不妨先看一下我的理解:
innodb_flush_log_at_trx_commit
MySQL支持用户自定义在commit时如何将redo log buffer中的日志刷到磁盘的log file中。这种控制通过变量 innodb_flush_log_at_trx_commit 的值来决定。该变量有3种值:0、1、2,默认为1。但注意,这个变量只是控制commit动作是否刷新log buffer到磁盘。
当设置为1的时候,事务发生过程,日志一直积累在redo log buffer中。事务每次提交都会将redo log buffer中的日志写入System的os buffer并调用fsync()刷到log file on disk(磁盘)中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。
可以发现,redolog管的是事务提交后的。只要事务提交,该模式的mysql就会马上执行【写入redolog buffer——》写入系统OS buffer——》写入磁盘redolog file】。这样就可以保证事务提交后数据不丢失。
当设置为0的时候,事务发生过程,日志一直积累在redo log buffer中。事务提交时不产生redo 写操作,而是MySQL内部每秒操作一次,从redo log buffer,把数据写入到系统的os buffer并调用fsync()写入到log file on disk(磁盘)中。这个频率可以通过
innodb_flush_log_at_timeout
控制,默认是1s。要注意,这个刷日志频率和commit动作无关,也就是说无论是否有commit操作,mysql都会按照频率刷盘。所以如果发生crash(不管mysql实例或系统哪个crash),即丢失约1s内的事务修改操作。也就是说设置为0时,是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。注意:由于进程调度策略问题,这个【每秒执行一次 flush(刷到磁盘)操作】并不是保证100%的【每秒】,只能说【约1s】。
当设置为2的时候,事务发生过程,日志一直积累在redo log buffer中。每次提交都仅写入到系统的os buffer,由系统内部来fsync到磁盘文件(可能约每秒做一次fsync)。如果数据库实例crash,不会丢失redo log,系统会将redolog buffer的数据写入到磁盘,保证持久化;但是如果服务器crash,由于file system buffer可能还来不及fsync到磁盘文件,所以会丢失这一部分的数据。
用undo log+redo log实现原子性和持久化的事务的简化过程:
假设有A、B两个数据,值分别为1,2,分别修改为3,4。
- 事务开始(对应命令begin/start transaction)
- 记录A=1到undo log buffer中
- 修改A=3
- 记录A=3到redo log buffer
- 记录B=2到undo log buffer中
- 修改B=4
- 记录B=4到redo log buffer
- 提交事务(执行commit命令)
- 将undo log buffer写到磁盘undolog file,undo log持久化 (这里的写入时机我不确定是不是这样
- 将redo log buffer写到磁盘redolog file,redo log持久化,这里标记事务为prepare状态
- 写binlog
- innodb写redolog,并标记事务为commit状态
- 事务真正结束
这是我根据网上资料和自己的总结写出来的,感觉不太对。