MySQL不會丟失數據的祕密,就藏在它的 7種日誌裏

本文收錄在 GitHub 地址 https://github.com/chengxy-nds/Springboot-Notebookmysql

進入正題前先簡單看看MySQL的邏輯架構,相信我用的着。git

MySQL邏輯架構

MySQL的邏輯架構大體能夠分爲三層:程序員

  • 第一層:處理客戶端鏈接、受權認證,安全校驗等。github

  • 第二層:服務器server層,負責對SQL解釋、分析、優化、執行操做引擎等。面試

  • 第三層:存儲引擎,負責MySQL中數據的存儲和提取。sql

咱們要知道MySQL的服務器層是無論理事務的,事務是由存儲引擎實現的,而MySQL中支持事務的存儲引擎又屬InnoDB使用的最爲普遍,因此後續文中提到的存儲引擎都以InnoDB爲主。數據庫

MySQL數據更新流程

記住! 記住! 記住! 上邊這張圖,她是MySQL更新數據的基礎流程,其中包括redo logbin logundo log三種日誌間的大體關係,好了閒話少說直奔主題。緩存

redo log(重作日誌)

redo log屬於MySQL存儲引擎InnoDB的事務日誌。安全

MySQL的數據是存放在磁盤中的,每次讀寫數據都需作磁盤IO操做,若是併發場景下性能就會不好。爲此MySQL提供了一個優化手段,引入緩存Buffer Pool。這個緩存中包含了磁盤中部分數據頁(page)的映射,以此來緩解數據庫的磁盤壓力。服務器

當從數據庫讀數據時,首先從緩存中讀取,若是緩存中沒有,則從磁盤讀取後放入緩存;當向數據庫寫入數據時,先向緩存寫入,此時緩存中的數據頁數據變動,這個數據頁稱爲髒頁Buffer Pool中修改完數據後會按照設定的更新策略,按期刷到磁盤中,這個過程稱爲刷髒頁

MySQL宕機

若是刷髒頁還未完成,可MySQL因爲某些緣由宕機重啓,此時Buffer Pool中修改的數據尚未及時的刷到磁盤中,就會致使數據丟失,沒法保證事務的持久性。

爲了解決這個問題引入了redo log,redo Log如其名側重於重作!它記錄的是數據庫中每一個頁的修改,而不是某一行或某幾行修改爲怎樣,能夠用來恢復提交後的物理數據頁,且只能恢復到最後一次提交的位置。

redo log用到了WAL(Write-Ahead Logging)技術,這個技術的核心就在於修改記錄前,必定要先寫日誌,並保證日誌先落盤,才能算事務提交完成。

有了redo log再修改數據時,InnoDB引擎會把更新記錄先寫在redo log中,在修改Buffer Pool中的數據,當提交事務時,調用fsync把redo log刷入磁盤。至於緩存中更新的數據文件什麼時候刷入磁盤,則由後臺線程異步處理。

注意:此時redo log的事務狀態是prepare,還未真正提交成功,要等bin log日誌寫入磁盤完成纔會變動爲commit,事務纔算真正提交完成。

這樣一來即便刷髒頁以前MySQL意外宕機也不要緊,只要在重啓時解析redo log中的更改記錄進行重放,從新刷盤便可。

大小固定

redo log採用固定大小,循環寫入的格式,當redo log寫滿以後,從新從頭開始如此循環寫,造成一個環狀。

那爲何要如此設計呢?

由於redo log記錄的是數據頁上的修改,若是Buffer Pool中數據頁已經刷磁盤後,那這些記錄就失效了,新日誌會將這些失效的記錄進行覆蓋擦除。

上圖中的write pos表示redo log當前記錄的日誌序列號LSN(log sequence number),寫入還未刷盤,循環日後遞增;check point表示redo log中的修改記錄已刷入磁盤後的LSN,循環日後遞增,這個LSN以前的數據已經全落盤。

write poscheck point之間的部分是redo log空餘的部分(綠色),用來記錄新的日誌;check pointwrite pos之間是redo log已經記錄的數據頁修改數據,此時數據頁還未刷回磁盤的部分。當write pos追上check point時,會先推進check point向前移動,空出位置(刷盤)再記錄新的日誌。

注意:redo log日誌滿了,在擦除以前,須要確保這些要被擦除記錄對應在內存中的數據頁都已經刷到磁盤中了。擦除舊記錄騰出新空間這段期間,是不能再接收新的更新請求的,此刻MySQL的性能會降低。因此在併發量大的狀況下,合理調整redo log的文件大小很是重要。

crash-safe

由於redo log的存在使得Innodb引擎具備了crash-safe的能力,即MySQL宕機重啓,系統會自動去檢查redo log,將修改還未寫入磁盤的數據從redo log恢復到MySQL中。

MySQL啓動時,無論上次是正常關閉仍是異常關閉,老是會進行恢復操做。會先檢查數據頁中的LSN,若是這個 LSN 小於 redo log 中的LSN,即write pos位置,說明在redo log上記錄着數據頁上還沒有完成的操做,接着就會從最近的一個check point出發,開始同步數據。

簡單理解,好比:redo log的LSN是500,數據頁的LSN是300,代表重啓前有部分數據未徹底刷入到磁盤中,那麼系統則將redo log中LSN序號300到500的記錄進行重放刷盤。

undo log(回滾日誌)

undo log也是屬於MySQL存儲引擎InnoDB的事務日誌。

undo log屬於邏輯日誌,如其名主要起到回滾的做用,它是保證事務原子性的關鍵。記錄的是數據修改前的狀態,在數據修改的流程中,同時會記錄一條與當前操做相反的邏輯日誌到undo log中。

咱們舉個栗子:假如更新ID=1記錄的name字段,name原始數據爲小富,現改name爲程序員內點事

事務執行update X set name = 程序員內點事 where id =1語句時,先會在undo log中記錄一條相反邏輯的update X set name = 小富 where id =1記錄,這樣當某些緣由致使服務異常事務失敗,就能夠藉助undo log將數據回滾到事務執行前的狀態,保證事務的完整性。

那可能有人會問:同一個事物內的一條記錄被屢次修改,那是否是每次都要把數據修改前的狀態都寫入undo log呢?

答案是不會的!

undo log只負責記錄事務開始前要修改數據的原始版本,當咱們再次對這行數據進行修改,所產生的修改記錄會寫入到redo logundo log負責完成回滾,redo log負責完成前滾。

回滾

未提交的事務,即事務未執行commit。但該事務內修改的髒頁中,可能有一部分髒塊已經刷盤。若是此時數據庫實例宕機重啓,就須要用回滾來將先前那部分已經刷盤的髒塊從磁盤上撤銷。

前滾

未徹底提交的事務,即事務已經執行commit,但該事務內修改的髒頁中只有一部分數據被刷盤,另一部分還在buffer pool緩存上,若是此時數據庫實例宕機重啓,就須要用前滾來完成未徹底提交的事務。將先前那部分因爲宕機在內存上的將來得及刷盤數據,從redo log中恢復出來並刷入磁盤。

數據庫實例恢復時,先作前滾,後作回滾。

若是你仔細看過了上邊的 MySQL數據更新流程圖 就會發現,undo logredo logbin log三種日誌都是在刷髒頁以前就已經刷到磁盤了的,相互協做最大限度保證了用戶提交的數據不丟失。

bin log(歸檔日誌)

bin log是一種數據庫Server層(和什麼引擎無關),以二進制形式存儲在磁盤中的邏輯日誌。bin log記錄了數據庫全部DDLDML操做(不包含 SELECTSHOW等命令,由於這類操做對數據自己並無修改)。

默認狀況下,二進制日誌功能是關閉的。能夠經過如下命令查看二進制日誌是否開啓:

mysql> SHOW VARIABLES LIKE 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | OFF   |
+---------------+-------+

bin log也被叫作歸檔日誌,由於它不會像redo log那樣循環寫擦除以前的記錄,而是會一直記錄日誌。一個bin log日誌文件默認最大容量1G(也能夠經過max_binlog_size參數修改),單個日誌超過最大值,則會新建立一個文件繼續寫。

mysql> show binary logs;
+-----------------+-----------+
| Log_name        | File_size |
+-----------------+-----------+
| mysq-bin.000001 |      8687 |
| mysq-bin.000002 |      1445 |
| mysq-bin.000003 |      3966 |
| mysq-bin.000004 |       177 |
| mysq-bin.000005 |      6405 |
| mysq-bin.000006 |       177 |
| mysq-bin.000007 |       154 |
| mysq-bin.000008 |       154 |

bin log日誌的內容格式其實就是執行SQL命令的反向邏輯,這點和undo log有點相似。通常來講開啓bin log都會給日誌文件設置過時時間(expire_logs_days參數,默認永久保存),要否則日誌的體量會很是龐大。

mysql> show variables like 'expire_logs_days';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| expire_logs_days | 0     |
+------------------+-------+
1 row in set

mysql> SET GLOBAL expire_logs_days=30;
Query OK, 0 rows affected

bin log主要應用於MySQL主從模式(master-slave)中,主從節點間的數據同步;以及基於時間點的數據還原。

主從同步

經過下圖MySQL的主從複製過程,來了解下bin log在主從模式下的應用。

  • 用戶在主庫master執行DDLDML操做,修改記錄順序寫入bin log;

  • 從庫slave的I/O線程鏈接上Master,並請求讀取指定位置position的日誌內容;

  • Master收到從庫slave請求後,將指定位置position以後的日誌內容,和主庫bin log文件的名稱以及在日誌中的位置推送給從庫;

  • slave的I/O線程接收到數據後,將接收到的日誌內容依次寫入到relay log文件最末端,並將讀取到的主庫bin log文件名和位置position記錄到master-info文件中,以便在下一次讀取用;

  • slave的SQL線程檢測到relay log中內容更新後,讀取日誌並解析成可執行的SQL語句,這樣就實現了主從庫的數據一致;

基於時間點還原

咱們看到bin log也能夠作數據的恢復,而redo log也能夠,那它們有什麼區別?

  • 層次不一樣:redo log 是InnoDB存儲引擎實現的,bin log 是MySQL的服務器層實現的,但MySQL數據庫中的任何存儲引擎對於數據庫的更改都會產生bin log。

  • 做用不一樣:redo log 用於碰撞恢復(crash recovery),保證MySQL宕機也不會影響持久性;bin log 用於時間點恢復(point-in-time recovery),保證服務器能夠基於時間點恢復數據和主從複製。

  • 內容不一樣:redo log 是物理日誌,內容基於磁盤的頁Page;bin log的內容是二進制,能夠根據binlog_format參數自行設置。

  • 寫入方式不一樣:redo log 採用循環寫的方式記錄;binlog 經過追加的方式記錄,當文件大小大於給定值後,後續的日誌會記錄到新的文件上。

  • 刷盤時機不一樣:bin log在事務提交時寫入;redo log 在事務開始時即開始寫入。

bin log 與 redo log 功能並不衝突而是起到相輔相成的做用,須要兩者同時記錄,才能保證當數據庫發生宕機重啓時,數據不會丟失。

relay log(中繼日誌)

relay log日誌文件具備與bin log日誌文件相同的格式,從上邊MySQL主從複製的流程能夠看出,relay log起到一箇中轉的做用,slave先從主庫master讀取二進制日誌數據,寫入從庫本地,後續再異步由SQL線程讀取解析relay log爲對應的SQL命令執行。

slow query log

慢查詢日誌(slow query log): 用來記錄在 MySQL 中執行時間超過指定時間的查詢語句,在 SQL 優化過程當中會常用到。經過慢查詢日誌,咱們能夠查找出哪些查詢語句的執行效率低,耗時嚴重。

出於性能方面的考慮,通常只有在排查慢SQL、調試參數時纔會開啓,默認狀況下,慢查詢日誌功能是關閉的。能夠經過如下命令查看是否開啓慢查詢日誌:

mysql> SHOW VARIABLES LIKE 'slow_query%';
+---------------------+--------------------------------------------------------+
| Variable_name       | Value                                                  |
+---------------------+--------------------------------------------------------+
| slow_query_log      | OFF                                                    |
| slow_query_log_file | /usr/local/mysql/data/iZ2zebfzaequ90bdlz820sZ-slow.log |
+---------------------+--------------------------------------------------------+

經過以下命令開啓慢查詢日誌後,我發現 iZ2zebfzaequ90bdlz820sZ-slow.log 日誌文件裏並無內容啊,可能由於我執行的 SQL 都比較簡單沒有超過指定時間。

mysql>  SET GLOBAL slow_query_log=ON;
Query OK, 0 rows affected

上邊提到超過 指定時間 的查詢語句纔算是慢查詢,那麼這個時間閾值又是多少嘞?咱們經過 long_query_time 參數來查看一下,發現默認是 10 秒。

mysql> SHOW VARIABLES LIKE 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+

這裏咱們將 long_query_time 參數改小爲 0.001秒再次執行查詢SQL,看看慢查詢日誌裏是否有變化。

mysql> SET GLOBAL long_query_time=0.001;
Query OK, 0 rows affected

果真再執行 SQL 的時,執行時間大於 0.001秒,發現慢查詢日誌開始記錄了。

慢查詢日誌

general query log

通常查詢日誌(general query log):用來記錄用戶的全部操做,包括客戶端什麼時候鏈接了服務器、客戶端發送的全部SQL以及其餘事件,好比 MySQL 服務啓動和關閉等等。MySQL服務器會按照它接收到語句的前後順序寫入日誌文件。

因爲通常查詢日誌記錄的內容過於詳細,開啓後 Log 文件的體量會很是龐大,因此出於對性能的考慮,默認狀況下,該日誌功能是關閉的,一般會在排查故障需得到詳細日誌的時候纔會臨時開啓。

咱們能夠經過如下命令查看通常查詢日誌是否開啓,命令以下:

mysql> show variables like 'general_log';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | OFF   |
+---------------+-------+

下邊開啓通常查詢日誌並查看日誌存放的位置。

mysql> SET GLOBAL general_log=on;
Query OK, 0 rows affected
mysql> show variables like 'general_log_file';
+------------------+---------------------------------------------------+
| Variable_name    | Value                                             |
+------------------+---------------------------------------------------+
| general_log_file | /usr/local/mysql/data/iZ2zebfzaequ90bdlz820sZ.log |
+------------------+---------------------------------------------------+

執行一條查詢 SQL 看看日誌內容的變化。

mysql> select * from t_config;
+---------------------+------------+---------------------+---------------------+
| id                  | remark     | create_time         | last_modify_time    |
+---------------------+------------+---------------------+---------------------+
| 1325741604307734530 | 我是廣播表 | 2020-11-09 18:06:44 | 2020-11-09 18:06:44 |
+---------------------+------------+---------------------+---------------------+

咱們看到日誌內容詳細的記錄了全部執行的命令、SQL、SQL的解析過程、數據庫設置等等。

通常查詢日誌

error log

錯誤日誌(error log): 應該是 MySQL 中最好理解的一種日誌,主要記錄 MySQL 服務器每次啓動和中止的時間以及診斷和出錯信息。

默認狀況下,該日誌功能是開啓的,經過以下命令查找錯誤日誌文件的存放路徑。

mysql> SHOW VARIABLES LIKE 'log_error';
+---------------+----------------------------------------------------------------+
| Variable_name | Value                                                          |
+---------------+----------------------------------------------------------------+
| log_error     | /usr/local/mysql/data/LAPTOP-UHQ6V8KP.err |
+---------------+----------------------------------------------------------------+

注意:錯誤日誌中記錄的可並不是全是錯誤信息,像 MySQL 如何啓動 InnoDB 的表空間文件、如何初始化本身的存儲引擎,初始化 buffer pool 等等,這些也記錄在錯誤日誌文件中。

總結

MySQL做爲咱們工做中最常接觸的中間件,熟練使用只算是入門,若是要在簡歷寫上一筆精通,還須要深刻了解其內部工做原理,而這7種日誌也只是深刻學習過程當中的一個起點,學無止境,兄嘚幹就完了!

整理了幾百本各種技術電子書,有須要的同窗能夠,在我同名公衆號回覆[ 666 ]自取。技術羣快滿了,想進的同窗能夠加我好友,和大佬們一塊兒吹吹技術,期待你的加入。

在這裏插入圖片描述

不管你是剛入行、仍是已經有幾年經驗的程序員,相信這份面試提綱都會給你很多助力,長按二維碼關注 『 程序員內點事 』 ,回覆 『 offer 』 自行領取,祝你們 offer 拿到手軟

在這裏插入圖片描述

相關文章
相關標籤/搜索