redo log的大小是固定的,在mysql中可以通過修改配置參數innodb_log_files_in_group和innodb_log_file_size配置日志文件數量和每個日志文件大小,redo log采用循環寫的方式記錄,當寫到結尾時,會回到開頭循環寫日志。redo log通常是物理日志,記錄的是數據頁的物理修改,而不是某一行或某幾行修改成怎樣怎樣,它用來恢復提交后的物理數據頁(恢復數據頁,且只能恢復到最后一次提交的位置)。
二進制日志相關內容,參考:MariaDB/MySQL的二進制日志。
redo log不是二進制日志。雖然二進制日志中也記錄了innodb表的很多操作,也能實現重做的功能,但是它們之間有很大區別。
- 二進制日志是在存儲引擎的上層產生的,不管是什么存儲引擎,對數據庫進行了修改都會產生二進制日志。而redo log是innodb層產生的,只記錄該存儲引擎中表的修改。並且二進制日志先於redo log被記錄。具體的見后文group commit小結。
- 二進制日志記錄操作的方法是邏輯性的語句。即便它是基於行格式的記錄方式,其本質也還是邏輯的SQL設置,如該行記錄的每列的值是多少。而redo log是在物理格式上的日志,它記錄的是數據庫中每個頁的修改。
- 二進制日志只在每次事務提交的時候一次性寫入緩存中的日志"文件"(對於非事務表的操作,則是每次執行語句成功后就直接寫入)。而redo log在數據准備修改前寫入緩存中的redo log中,然后才對緩存中的數據執行修改操作;而且保證在發出事務提交指令時,先向緩存中的redo log寫入日志,寫入完成后才執行提交動作。
- 因為二進制日志只在提交的時候一次性寫入,所以二進制日志中的記錄方式和提交順序有關,且一次提交對應一次記錄。而redo log中是記錄的物理頁的修改,redo log文件中同一個事務可能多次記錄,最后一個提交的事務記錄會覆蓋所有未提交的事務記錄。例如事務T1,可能在redo log中記錄了 T1-1,T1-2,T1-3,T1* 共4個操作,其中 T1* 表示最后提交時的日志記錄,所以對應的數據頁最終狀態是 T1* 對應的操作結果。而且redo log是並發寫入的,不同事務之間的不同版本的記錄會穿插寫入到redo log文件中,例如可能redo log的記錄方式如下: T1-1,T1-2,T2-1,T2-2,T2*,T1-3,T1* 。
- 事務日志記錄的是物理頁的情況,它具有冪等性,因此記錄日志的方式極其簡練。冪等性的意思是多次操作前后狀態是一樣的,例如新插入一行后又刪除該行,前后狀態沒有變化。而二進制日志記錄的是所有影響數據的操作,記錄的內容較多。例如插入一行記錄一次,刪除該行又記錄一次。
redo log包括兩部分:一是內存中的日志緩沖(redo log buffer),該部分日志是易失性的;二是磁盤上的重做日志文件(redo log file),該部分日志是持久的。
在概念上,innodb通過force log at commit機制實現事務的持久性,即在事務提交的時候,必須先將該事務的所有事務日志寫入到磁盤上的redo log file和undo log file中進行持久化。
為了確保每次日志都能寫入到事務日志文件中,在每次將log buffer中的日志寫入日志文件的過程中都會調用一次操作系統的fsync操作(即fsync()系統調用)。因為MariaDB/MySQL是工作在用戶空間的,MariaDB/MySQL的log buffer處於用戶空間的內存中。要寫入到磁盤上的log file中(redo:ib_logfileN文件,undo:share tablespace或.ibd文件),中間還要經過操作系統內核空間的os buffer,調用fsync()的作用就是將OS buffer中的日志刷到磁盤上的log file中。
也就是說,從redo log buffer寫日志到磁盤的redo log file中,過程如下:
在此處需要注意一點,一般所說的log file並不是磁盤上的物理日志文件,而是操作系統緩存中的log file,官方手冊上的意思也是如此(例如:With a value of 2, the contents of the InnoDB log buffer are written to the log file after each transaction commit and the log file is flushed to disk approximately once per second)。但說實話,這不太好理解,既然都稱為file了,應該已經屬於物理文件了。所以在本文后續內容中都以os buffer或者file system buffer來表示官方手冊中所說的Log file,然后log file則表示磁盤上的物理日志文件,即log file on disk。
另外,之所以要經過一層os buffer,是因為open日志文件的時候,open沒有使用O_DIRECT標志位,該標志位意味着繞過操作系統層的os buffer,IO直寫到底層存儲設備。不使用該標志位意味着將日志進行緩沖,緩沖到了一定容量,或者顯式fsync()才會將緩沖中的刷到存儲設備。使用該標志位意味着每次都要發起系統調用。比如寫abcde,不使用o_direct將只發起一次系統調用,使用o_object將發起5次系統調用。
MySQL支持用戶自定義在commit時如何將log buffer中的日志刷log file中。這種控制通過變量 innodb_flush_log_at_trx_commit 的值來決定。該變量有3種值:0、1、2,默認為1。但注意,這個變量只是控制commit動作是否刷新log buffer到磁盤。
- 當設置為1的時候,事務每次提交都會將log buffer中的日志寫入os buffer並調用fsync()刷到log file on disk中。這種方式即使系統崩潰也不會丟失任何數據,但是因為每次提交都寫入磁盤,IO的性能較差。
- 當設置為0的時候,事務提交時不會將log buffer中日志寫入到os buffer,而是每秒寫入os buffer並調用fsync()寫入到log file on disk中。也就是說設置為0時是(大約)每秒刷新寫入到磁盤中的,當系統崩潰,會丟失1秒鍾的數據。
- 當設置為2的時候,每次提交都僅寫入到os buffer,然后是每秒調用fsync()將os buffer中的日志寫入到log file on disk。
注意,有一個變量 innodb_flush_log_at_timeout 的值為1秒,該變量表示的是刷日志的頻率,很多人誤以為是控制 innodb_flush_log_at_trx_commit 值為0和2時的1秒頻率,實際上並非如此。測試時將頻率設置為5和設置為1,當 innodb_flush_log_at_trx_commit 設置為0和2的時候性能基本都是不變的。關於這個頻率是控制什么的,在后面的"刷日志到磁盤的規則"中會說。
在主從復制結構中,要保證事務的持久性和一致性,需要對日志相關變量設置為如下:
- 如果啟用了二進制日志,則設置sync_binlog=1,即每提交一次事務同步寫到磁盤中。
- 總是設置innodb_flush_log_at_trx_commit=1,即每提交一次事務都寫到磁盤中。
上述兩項變量的設置保證了:每次提交事務都寫入二進制日志和事務日志,並在提交時將它們刷新到磁盤中。
有了redo log,為啥還需要bin log呢?
1、redo log的大小是固定的,日志上的記錄修改落盤后,日志會被覆蓋掉,無法用於數據回滾/數據恢復等操作。
2、redo log是innodb引擎層實現的,並不是所有引擎都有。
基於以上,bin log必不可少。
1、bin log是server層實現的,意味着所有引擎都可以使用bin log日志
2、bin log通過追加的方式寫入的,可通過配置參數max_binlog_size設置每個binlog文件的大小,當文件大小大於給定值后,日志會發生滾動,之后的日志記錄到新的文件上。
3、bin log有兩種記錄模式,statement格式的話是記sql語句, row格式會記錄行的內容,記兩條,更新前和更新后都有。
bin log和redo log必須保持一致,不允許出現bin log有記錄但redo log沒有的情況,反之亦然。之前說過在一個事務中,redolog有prepare和commit兩種狀態,所以,在redolog狀態為prepare時記錄binlog可保證兩日志的記錄一致
相關參數設置建議:
1、
innodb_flush_log_at_trx_commit:設置為1,表示每次事務的redo log都直接持久化到磁盤(注意是這里指的是redo log日志本身落盤),保證mysql重啟后數據不丟失。
2、sync_binlog:設置為1,表示每次事務的bin log都直接持久化到磁盤(注意是這里指的是bin log日志本身落盤),保證mysql重啟后bin log記錄是完整的。
參考:https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html