InnoDB的事務與日志的實現方式?
有多少種日志?
- redo日志
- undo日志
日志的存放形式?
- redo:在頁修改的時候,先寫到redo log buffer里面,然后寫到redo log的文件系統緩存里面(fwrite),然后再同步到磁盤文件(fsync)
- undo:在MySQL5.5之前,undo只能存放在ibdata文件里面,5.6之后,可以通過設置innodb_undo_toblespaces參數把undo log存放在ibdata之外。
事務時如何通過日志來實現的?
基本流程如下:
- 因為事務在修改頁時,要先記undo,在記undo之前要記undo的redo,然后修改數據頁,再記數據頁修改的redo。redo(里面包括undo修改)一定要比數據頁先持久化到磁盤。
- 當事務需要回滾時,因為有undo,可以把數據頁回滾到前鏡像的狀態。
- 崩潰回復時,如果redo log中事務沒有對應的commit記錄,那么需要用undo把該事務的修改回滾到事務開始之前。如果有commit記錄,就用redo前滾到該事務完成時並提交掉。
MySQL binlog的幾種日志錄入格式以及區別?
各種日志格式的涵義?
binlog有三種格式類型,分別如下:
1)statement
每一條會修改數據的SQL都會記錄在binlog中。
- 優點:不需要記錄每一行的變化,減少了binlog日志量,節約了IO,提高性能。(相比row能節約多少性能與日志量,這個取決於應用的SQL情況,正常同一條記錄修改或者插入row格式所產生的日志量還小於Statement產生的日志量,但是考慮到如果帶條件的update操作,以及整表刪除,alter表等操作,ROW格式會產生大量日志,因此在考慮是否使用ROW格式日志時應該根據應用的實際情況,其所產生的日志量會增加多少,以及帶來的IO性能問題)
- 缺點:由於記錄的只是之行語句,為了這些語句能在slave上正確運行,因此還必須記錄每條語句在執行的時候的一些相關信息,以保證語句能在slave得到和在master端執行時候相同的結果。另外MySQL的復制,像一些特定函數功能,slave可與master上要保持一致會有很多相關問題(如sleep())函數,last_insert_id(),以及user-defined functions(udf)會出現問題。
- 使用以下函數的語句也無法復制:
- LOAD_FILE()
- UUID()
- USER()
- FOUND_ROW()
- SYSDATE()除非啟動時啟用了-system-is-now選項。
- 同時在insert_select會產生比RBR更多的行級鎖。
2)ROW
不記錄SQL語句上下相關信息,僅保存那條記錄被修改。
- 優點:binlog中可以不記錄執行的SQL語句的上下文相關的信息,僅需要記錄那一條記錄被修改成什么了。所以rowlevel的日志內容會非常清楚的記錄下每一行數據修改的細節。而且不會出現某些特定情況下的存儲過程,或function,以及trigger的調用和觸發無法被正確復制的問題。
- 缺點:所有的執行的語句當記錄到日志中的時候,都將以每行記錄,這樣可能會產生大量的日志內容,比如,一條update語句,修改多條記錄,則binlong中,每一條修改都會記錄,這樣造成binlog日志量會很大,特別是當執行alter table之類的語句的時候,由於表結構修改,每條記錄都發生改變,那么該表每一條記錄都會記錄到日志中。
3)MixedLevel
是以兩種level的混合使用。
- 一般的語句修改使用Statment格式保存binlog
- 如一些函數,statement無法完成主從復制的操作,則采用Row格式保存binlog。
MySQL會根據執行的每一條具體的SQL語句來區分對待記錄的日志形式,也就是在Statement和Row之間選擇一種。
新版本的MySQL中對row level模式也被做了優化,並不是所有的修改都會以row level模式。
- 像遇到表結構變更的時候就會以Statement模式來記錄。
- 至於Update或者Delete等修改數據的語句,還是會記錄所有行的變更,即使用Row模式。
使用場景?
在一條SQL操作了多行數據時,Statement更節省空間,Row更占用空間,但是Row模式更可靠。
因為,互聯網公司,使用Mysql的功能相對少,基本不使用存儲過程,觸發器,函數的功能,選擇默認的語句模式,Statement (默認)即可
結合第一個問題,每一種日志格式在復制中的優劣?
- Statement可能占用空間會相對小一些,傳送到slave的時間可能也短,但是沒有Row模式可靠。
- Row模式在操作多行數據時更占用空間,但是可靠。
所以,這是在占用空間和可靠之間的選擇
如何在線正確清理MySQL binlog?
MySQL中的binlog日志記錄了數據中的數據變動,便於對數據的基於時間點和機遇位置的回復。但日志文件的大小會越來越大,占用了大量的磁盤克難攻堅,因此需要定時清理一部分日志信息。
#首先查看主從庫正在使用的binlog文件名稱 show master(slave) status #刪除之前一定要備份 purge master logs before '2017-09-01 00:00:00';#刪除指定時間前的日志。 purge master logs to 'mysql-bin.000001' #刪除指定的日志文件 #自動刪除:通過設置binlog的過期時間讓系統自動刪除日志 show variables like 'expire_logs_days';查看過期時間 set global expire_log_days=30;# 設置過期時間
MySQL主從復制的流程是怎么樣的?
MySQL的主從復制是基於如下3個線程的交互(多線程復制里面應該是4類線程):
- Master上面的binlog dump線程,該線程負責將master的binlog event傳到slave
- Slave上面的IO線程,該線程負責接收Master傳過來的binlog,並寫入relay log。
- Slave上面的SQL線程,該線程負責讀取relay log並執行。
- 如果是多線程復制,無論是5.6庫級別的假多線程還是MariaDB或者5.7的真正的多線程復制,SQL線程只做coordinator,只負責把relay log中的binlog讀出來然后交給worker線程,worker線程負責具體binlog event的執行。
MySQL如何保證復制過程中數據的一致性的?
1.在MySQl5.5以及之前,slave的SQL線程執行的relay log的位置只能保存在文件(relay-log.info)里面,並且該文件默認每執行10000次事務醉一次同步到磁盤,這意味着slave意外crash重啟時,SQL線程執行到的位置和數據庫的數據是不一致的,將導致復制報錯。若功不重搭復制,則有可能會導致數據不一致。
- MySQL5.6引入參數relay_log_info_repository,將該參數設置為TABLE時,MySQL將SQL線程執行到的位置存到mysql.slave_relay_info_log表,這樣更新該表的位置和SQL線程執行的用戶事務綁定完成一個事務,這樣slave意外宕機后。slave通過innodb的崩潰回復可以把SQL線程執行到的位置和用到事務回復到一致性的狀態。
2.MySQL5.6引入GTID復制,每個GTID對應的事務在每個實例上面最多執行一次,這極大地提高了復制的數據一致性。
3.MySQL5.5引入版同步復制,用戶安裝版同步復制插件並且開啟參數后,設置超時時間,可保證在超時時間內如果binlog不穿到slave上面,那么用戶提交事務時不會返回,直到超時后切成異步復制,但是如果切成異步之前用戶線程提交時在master上面等待的時候,事務已經提交,該事務對master上面的其他session是可見的,如果這時master宕機,那么到slave上面該事務又不可見了,該問題直到5.7才解決。
4.MySQL5.7引入半同步復制,引入參數rpl_semi_sync_master_wait_point,該參數默認為after_sync,指的是在切成半同步之前,事務不提交,而是接受slave的AVK確認之后才提交該事務,從此,復制真正可以做到無損的了。
5.可以說一下5.7的無損情況下,master意外宕機,重啟后發現有binlog沒有傳到slave上面,這部分binlog怎么辦?分兩種情況討論:1.宕機時已經切成異步了,2.是宕機時還沒有切成異步?這個怎么判斷宕機時有沒有切成異步呢?分別怎么處理?
MySQL如何解決主從復制的延時性?
5.5是單線程復制,5.6是多庫復制(對於單庫或者單表的並發操作是沒用的),5.7是真正意義的多線程復制,它的原理是基於group commit,只要master上面的事務時group commit的,那slave上面也可以通過多個worker線程去並發執行,和MairaDB10.0.0.5引入多線程復制原理基本相同。
工作遇到的復制bug解決辦法?
5.6的多庫復制有時候自己會停止,我們寫了一個腳本重新start slave。
你是否做過主從一致性校驗,如果有,怎么做的,如果沒有,打算怎么做?
主從一致性校驗的工具有很多例如:checksum、mysqldiff、pt-table-checksum等
聊聊MySQL備份方式?備份策略是怎么樣的?