1.MySQL的內部組件結構
????????大體來說,MySQL可以分為Server層和存儲引擎層兩部分。
2.Server層
????????Server層主要包括連接器、查詢緩存、分析器、優化器和執行器等,涵蓋MySQL的大多數核心服務功能,以及所有的內置函數(如日期、時間、數據和加密函數),所有跨存儲引擎的功能都在這一層實現,比如存儲過程、觸發器、視圖等。
????????下面詳細介紹一些Server層的這些組件。
2.1連接器
????????我們知道由于MySQL是開源的,他有非常多種類的客戶端:navicat,mysql,front,jdbc,SQLyog等非常豐富的客戶端,包括各種編程語言實現的客戶端連接程序,這些客戶端要向MySQL發起通信都必須先跟Server端建立通信連接,而建立連接的工作就是有連接器完成的。
????????第一步,客戶端會先連接到這個數據庫上,這時候首先處理連接的是連接器。連接器負責跟客戶端建立連接、獲取權限、維持和管理連接。連接命令一般是這么寫的:
mysql -h host[數據庫地址] -u root[用戶] -p root [密碼] -p 3306
????????連接命令中的mysql是客戶端工具,用來和服務端建立連接。在完成經典的TCP握手后,連接器就要開始認證你的身份,這個時候用到就是你輸入的用戶名和密碼。
????????連接過程中進行認證身份時,認證成功和失敗會觸發兩種處理模式:
????????1.如果用戶名或者密碼不對,就會收到一個'Access denied for user'的錯誤,然后客戶端結束執行。
????????2.如果用戶名密碼認證通過,連接器就會到權限表查詢中當前認證通過的用戶擁有的權限。隨后,這個連接里面的權限判斷邏輯,都依賴于此時讀取到的權限。
????????權限的認證處理邏輯意味著,一個用戶成功建立連接之后,即使使用更高權限的賬號對該賬號進行權限修改之后,也不會影響已經存在的連接的權限。修改完成之后,只有再新建的連接才會使用新的權限設置。
2.2查詢緩存
????????連接建立完成之后,就可以執行SELECT語句了。Server層的執行邏輯就到了第二步:查詢緩存。
????????MySQL拿到一個查詢請求后,會先到查詢緩存查看是否執行過這條SQL語句。之前執行過的語句及其結果都會以key-value鍵值對的形式去緩存到內存中。key時查詢的語句,value是查詢的結果。如果查詢的語句能夠在緩存中直接找到,就會將查詢語句對應的緩存值數據返回給客戶端。
????????如果語句不在查詢緩存中,就會繼續后面的執行階段。執行完成之后,執行結果會存入到查詢緩存中。
????????MySQL借助查詢緩存來提高系統的執行效率,緩存中有的查詢語句可以直接將對應的數據返回,無需再走后續的查詢流程了。
????????但是在MySQL8.0已經將查詢緩存刪除了,因為這個查詢緩存比較雞肋的。
????????為什么大多數情況下這個查詢緩存比較雞肋呢?
????????因為查詢緩存往往弊大于利。查詢緩存的失效是非常頻繁的,只要有對一個表的更新,這個表上所有查詢緩存都會被清空。因此很可能雖然花了一些額外操作將查詢結果存儲成功了,還沒有進行使用呢,結果發生了一次數據更新,其中的數據全部被清空了。對于更新比較頻繁的數據庫來說,查詢緩存的命中率是很低的,所以這個查詢緩存是比較雞肋的。
????????一般建議大家在靜態表中使用查詢緩存。什么是靜態表呢?就是一般我們極少更新的表。比如,一個系統配哦之表,字典表等。那這張表上的查詢才適合使用查詢緩存。
????????MySQL也提供了按需使用的方式,在8.0版本之前,如果不想使用查詢緩存,可以將my.cnf的參數query_cache_type設置為DEMAND。
????????在my.cnf的配置文件中,query_cache_type可以設定三個值,0代表關閉查詢緩存OFF,1代表開啟ON,2(DEMAND)代表SQL語句中有SQL_CACHE關鍵詞才緩存。
query_cache_type=2
????????這樣對于默認的SQL語句都不使用查詢緩存。而對于你確定要使用查詢緩存的語句,可以使用SQL_CACHE關鍵字顯式指定,像下面的這個語句一樣:
SELECT SQL_CACHE * FROM test WHERE id = 5;
????????可以使用下面的語句查看是否開啟了緩存機制:
SHOW GLOBAL VARIABLES LIKE "%query_cache_type%";
????????總體來看,查詢緩存是一個比較雞肋的功能,5.7版本的MySQL也建議關閉。
2.3分析器
????????如果沒有命中查詢緩存,就要開始真正執行語句了。首先,MySQL需要知道SQL語句的含義是什么,因此需要對SQL語句進行解析,此時就需要使用詞法分析器了。
????????分析器會先進行“詞法分析”。執行的SQL指令是由多個字符串和空格組成的一條SQL語句,MySQL需要識別出里面的字符串分別表示什么,代表什么。
????????假設現在執行的是下面的SQL語句:
SELECT * FROM test WHERE id = 1;
????????MySQL的詞法分析器會通過SQL語句中的SELECT關鍵字識別出,這是一個查詢語句。也要把字符串test識別為“數據表名稱test”。將字符id識別為“數據列id”。
????????進行了這些詞法的識別之后,就要進行“語法分析”。根據詞法分析的結果,語法分析器會根據語法規則,判斷執行的SQL語句是否滿足MySQL的語法規則。
????????如果輸出的SQL語法不對,就會收到“You have an error in your SQL syntax”的錯誤提醒,比如下面這個錯誤的SQL語句:
SELECT * FROME t1 WHERE id = 1;
????????執行了這個錯誤的SQL語句,MySQL的分析器發現SQL語句語法出現了錯誤,便會拋出1064的錯誤:
[42000][1064] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROME t1 WHERE id = 1' at line 1
????????下面是SQL分析的整體分析流程:
????????開始的時候會先進入詞法分析階段,在詞法分析的過程中會檢測是否到達了終結符,如果沒有到達終結符就會繼續執行詞法分析,如果到達了終結符,就會繼續向下執行到語法分析階段。
????????語法分析階段,會逐漸將詞法分析的結果投喂給分析機,如果解析成功就會添加到AST樹中,如果分析失敗就會返回到語法分析部分并拋出相應的錯誤。
????????構建好AST樹后,整個分析的階段便完成了。
????????SQL語句經過分析器分析完畢后,會形成下面這樣的語法AST樹:
????????下面是InnoDB整體的執行流程:接下來就進入到了優化器階段。
2.4優化器
????????經過了分析器,MySQL已經得知你需要做什么了。在開始執行之前,還需要經過優化器的處理。
????????優化器是在表里面有很多個索引的時候,決定使用哪個索引。或者在一個語句有多表關聯(join)的時候,決定各個表的連接順序,以及一些MySQL內部的優化機制。
????????在優化器這個執行流程中會生成執行計劃在,執行計劃可以使用explain查看到執行計劃。
2.5執行器
????????開始執行的時候,要先判斷一下你的數據表是否有查詢權限,如果沒有,就會返回沒有權限的錯誤(如果命中了查詢緩存,也會在查詢緩存返回結果前執行權限校驗)
????????由于執行器部分需要調度的是執行引擎去執行SQL語句,現在主要使用的執行引擎是InnoDB,所以我們著重介紹的是InnoDB執行引擎的底層調度執行原理,MyISAM等其它執行引擎不再介紹。
3.InnoDB底層原理
3.1.1client和Server層
????????client和Server層的如下:
????????client就是發送SQL語句到MySQL服務器的客戶端,這里發送了一條SQL語句是“UPDATE t1 SET name = 'XingHai666' WHERE id = 1”。
????????Server層剛剛已經介紹過了,Server層是用于執行除了底層引擎以外的通用部分,引擎決定的是執行SQL語句的部分,這部分回因為底層引擎的不同產生差異性。Server層主要做的是連接鑒權,詞法分析、語法分析、SQL優化和查詢緩存的部分。
????????當Server層將客戶端發送來的SQL語句進行解析優化完成后,會交給執行器去調度底層執行引擎執行代碼。
3.1.2數據頁加載
????????進行數據更新操作的時候,不會直接去修改磁盤ibd文件中的數據,而是會進行一系列的操作之后,才會真正去修改磁盤ibd文件中的數據。
????????首先第一步進行的是數據頁的加載,下面是該步操作的涉及的具體組件:
????????在InnoDB引擎中,有一個Buffer Pool緩存池,該緩存池用于從ibd文件中將對應數據一頁的數據都拉取到Buffer Pool緩存池中。
????????當目標數據所在頁數據都被拉取到Buffer Pool中之后,就會進行第二步undo回滾鏈的構建。
????????Buffer Pool是在內存中的,所以其性能是比較高的。
3.1.3undo回滾鏈的構建
????????在前面我們有講過,undo回滾鏈主要是用于進行事務回滾和事務數據可見性的,依靠undo日志回滾鏈可以實現事務的回滾操作以及讀已提交,可重復讀的現象。
????????第二步就是進行了undo回滾鏈的構建:
????????當數據被加載到InnoDB存儲引擎的Buffer Pool緩存池之后,就會將寫入更新的數據的舊值寫入到undo回滾日志文件中。
????????當事務提交失敗之后,進行rollback回滾操作時,可以通過undo日志鏈中的數據回滾Buffer Pool緩存池中的數據(因為此時ibd磁盤中的數據還沒有進行修改,所以ibd文件中的數據是無需修改的)
3.1.4更新緩存池中的數據
????????當undo回滾鏈構建成功后,InnoDB引擎就會開始執行第三步了,更新Buffer Pool中的數據:
????????將需要更新的數據更新到InnoDB的緩存池中,此時需要更新的數據就在緩存池中更新完成了。
3.1.5redo Log Buffer的構建
????????當Buffer Pool緩存池中的數據更新完成后,就要開始構建redo日志鏈,redo日志鏈的作用是負責當數據庫/服務器down掉之后,對數據進行恢復的,這個組件只有在InnoDB引擎中才會有,是非常重要的部分,可以保障數據庫中數據的安全。
????????下面展示了了Redo Log Buffer緩存池的構建的組件部分:
????????Redo Log Buffer是位于InnoDB存儲引擎中的,是一個內存區域,執行器可以將redo日志寫入到這塊內存區域中,以提高整體日志的寫入性能。
3.1.6redoLog順序寫入磁盤
????????當執行器將redoLog寫入到Redo Log Buffer之后,還需要將redoLog順序寫到磁盤中。之所以進行磁盤順序寫操作,是因為剛剛介紹過redoLog重做日志是用于在系統宕機時,幫助系統恢復沒有寫入到ibd文件中的數據的。但是如果將數據僅僅是存儲在Redo Log Buffer內存區域中,系統宕機后,redoLog數據還是會消失,所以就需要將數據持久化到硬盤中,這樣宕機之后數據就不會丟失了。
????????下面展示的是從Redo Log Buffer中拉取數據磁盤順序寫到磁盤中,完成持久化。
????????InnoDB引擎會從Redo Log Buffer拉取數據,順序寫入到磁盤已經準備好的相關文件中。其中磁盤中并不是存儲了修改了xx數據,而是進行物理修改,在磁盤中存儲的是在哪一頁做了什么修改。當需要通過redo log進行恢復數據的時候,可以直接根據里面存儲的數據,進行恢復物理上的數據。
????????需要注意的是,redo log buffer寫入數據到redo日志文件中,并不是直接將redo log buffer中的數據同步寫入到redo日志文件中的,而是借助了一個Page Cache來提高整體數據同步的性能。整體的數據流轉流程如下:
????????PageCache在redo log buffer和redo日志文件中間作為中間層負責承載加速,提高數據的流轉速度,這里的設計類似于CPU和內存通信的設計,CPU運算速度遠大于內存加載數據的速度,為了可以更好的提高性能,使用高速緩存解決了該問題。對于Page Cache也是,buffer內存的速度遠大于日志文件磁盤的速度,引入Page Cache可以提高一定的性能。
????????需要注意的是,PageCache是建立在操作系統級別的,而不是數據庫級別的。
????????redo log日志文件數據恢復的機制?
????????當事務提交了之后,buffer pool中的數據還沒有同步寫入到ibd文件中時,此時系統宕機了,可以使用redo log中的日志數據恢復磁盤ibd文件里的數據。
????????為什么要使用redo log日志來實現數據的恢復?直接寫入到ibd文件中不行嗎?
????????我們需要搞清楚的是,數據更改寫入ibd文件和redo log日志文件磁盤寫入機制的不同,首先磁盤寫入ibd文件使用的是隨機讀寫操作,redo log日志寫入磁盤使用的是磁盤順序寫,對于機械硬盤來說,磁盤順序寫的性能是遠高于磁盤隨機寫,因為機械硬盤是一種物理的存儲結構,讀取數據時是在機械圓盤上進行尋道查詢物理地址,如果是磁盤順序寫,那尋道速度特別快,但是如果是磁盤隨機寫,尋道的時候非常費勁,所以對于機械硬盤來說,磁盤隨機寫的性能是遠低于磁盤順序寫的。MySQL基于這個考慮,使用redo log可以很迅速的將更改的數據持久化存儲下來,防止數據丟失。
????????仔細思考一下,如果沒有redo log機制,直接將數據寫入到ibd文件中,由于要進行的是磁盤隨機寫,如果事務特別多,都進行提交操作,需要耗費的性能是比較高的,所以如果在寫入到ibd文件的途中,由于花費的事件比較長,如果系統突然宕機了,則可能會導致數據丟失。但是如果是使用redo log日志,進行磁盤順序寫時,寫入的速度是非常快的,這樣就可以盡可能的減少更改數據丟失的可能性。
????????但是現代化服務器中,大部分服務器都會采用SSD存儲數據,對于SSD來說,磁盤隨機寫和磁盤順序寫的性能差距不大,這個優化其實是可以不做的,但是MySQL設計的時代,機械硬盤是主流,這個優化手段可以明顯提高數據的安全性。
????????為什么ibd數據寫入磁盤是使用的磁盤隨機寫呢?
????????ibd數據寫入磁盤是使用磁盤順序寫的原因主要是:不同的數據表對應著不同的不同的ibd文件,比如執行10條SQL語句,修改不同的表,需要修改不同表的ibd文件,不能實現順序寫文件的效果。
????????為什么redo log日志寫入磁盤是使用的磁盤順序寫呢?
在InnoDB底層的設定出存儲磁盤空間重,準備了專門存儲redo log日志的文件,存儲數據時,會將日志數據追加到日志文件的末尾,所以就實現了磁盤順序寫。
????????總結:寫redo log后刷新數據表文件的機制交WAL機制(Write-Ahead Logging),效率更高。
3.1.7binlog寫入磁盤
????????當redoLog被順序寫入磁盤之后,就要開始進行下一步操作,準備寫入binlog數據,binlog數據是為了進行歸檔回滾,用于將數據恢復之前未修改的狀態,可以防止刪庫跑路,進行數據版本回滾等。
????????下面是binlog日志寫入磁盤的操作:
????????當redo log數據被順序寫入到磁盤之后,就開始了下一步操作,將binlog日志數據寫入到磁盤中,這一步操作其實是屬于Server層的操作,也就是說不是InnoDB獨有的操作,所有的執行引擎,都會執行binlog日志歸檔操作。
????????binlog歸檔日志里面一般存儲的是SQL數據,可以借助里面的SQL語句進行相關的回滾操作。
????????與redo log一樣,binlog也使用了PageCache進行加速,執行器會先將數據寫入到PageCache中,由PageCache將數據同步到binlog歸檔日志文件的磁盤中。
3.1.8寫入commit標記到redo日志文件中
????????在binlog歸檔日志寫入完成之后,InnoDB引擎會將commit標記寫入到redo日志文件中,提交事務完成之后,該標記可以保證事務提交后redo于binlog數據一致。
????????下面是寫入commit標記到redo日志文件中的操作:
????????具體介紹一下為什么要在binlog日志文件寫入成功之后,需要將commit標記寫入redo log日志中:
????????當事務提交成功的時候,才會響應回去commit成功的信息。具體在什么時候返回呢?為了保證數據可以回滾,需要等待redo log日志文件和binlog日志文件中的數據均寫入成功之后,才會響應回去commit成功的信息,目的是保證redo log數據和binlog數據的一致性。
????????當事務提交后,會檢測redo日志中是否有commit標記,如果有這個標記才會響應提交成功,如果沒有這個標記,就會等著redo log和binlog同步成功后再返回。
3.1.9異步IO線程更新數據
????????InnoDB底層執行的最后一步是異步IO線程將Buffer Pool中存儲的已經被更新過的數據,異步更新到磁盤的IBD存儲文件中。
????????下面是異步IO線程更新數據的流程:
????????當redo log和binlog數據都被記錄好之后,異步IO線程會去Buffer Pool中拉取數據,拉取數據時,是將更改數據整頁的數據都拉取到,以頁page為單位,將數據更新到磁盤ibd文件中。
????????至此,InnoDB引擎的整體執行流程介紹完畢。
3.2梳理redoLog的關鍵參數和存儲機理
3.2.1redoLog buffer大小參數設置
????????MySQL的redoLog buffer的大小是可以通過參數進行配置的。
????????這個參數可以使用innodb_log_buffer_size配置redo log buffer的大小參數,默認是16M,最大值是4096M,最小值是1M.
????????可以使用下面的語句查詢innodb_log_buffer_size參數對應的配置參數:
SHOW VARIABLES LIKE '%innodb_log_buffer_size%'
????????查詢的結果如下:
????????可以從查詢結果中看出redo log buffer的內存大小設置為16M。
3.2.2innodb_log_group_home_dir參數設置
????????innodb_log_group_home可以設置redo log文件存儲位置參數,默認值是"./",即innodb數據文件存儲位置(mysql/data文件下面)。
????????可以使用下面的語句查詢innodb_log_group_home_dir參數的值:
SHOW VARIABLES LIKE '%innodb_log_group_home_dir%';
????????查詢出的數據如下:
????????從查詢出來的數據可以看出,innodb_log_group_home_dir設置的值是".\",即mysql/data文件夾下面。
????????可以查看到redo log的磁盤文件存儲在mysql/data下面。
3.2.3innodb_log_files_in_group參數設置
????????innodb_log_files_in_group參數可以設置redo log文件的個數,命名方式為:ib_logfile0,ib_logfile1...ib_logfileN,默認是2個,最大100個。
????????可以通過下面的SQL語句查看innodb_log_files_in_group的參數設置:
SHOW VARIABLES LIKE '%innodb_log_files_in_group%';
????????查詢結果如下:
????????可以看到默認的redo log日志文件的默認值是兩個。
3.2.4innodb_log_file_size參數設置
????????innodb_lof_file_size參數可以設置單個redo log文件的大小,默認值是48M,最大值是512G,注意這里指的最大值是整個redo log系列文件之和,即(innodb_log_files_in_group * innodb_log_file_size)不能大于最大值512G。
????????可以通過下面的SQL語句查看innodb_log_file_size的參數設置:
SHOW VARIABLES LIKE '%innodb_log_file_size%'
????????查詢結果如下:
????????可以看到innodb_log_file_size的默認配置就是48M。
3.2.5redo log寫入磁盤過程分析
????????前面已經將redo log所有的參數已經介紹完畢了,現在主要介紹一下redo log寫入磁盤的整個過程。
????????我們知道redo log是由多個日志文件組成的,那么它是如何完成磁盤順序讀取呢?文件之前是如何尋址的呢,接下來我們一探究竟。
????????redo log是從頭開始寫的,寫完一個文件繼續寫另一個文件,寫到最后一個文件的末尾,就又回到第一個文件開頭循環寫,類似一個循環數組結構。
????????假設redo log由四個文件組成,下面是redo log文件的整體讀取流程圖:
????????ib_logfile所有的文件以前后相連的狀態,組成一個環形數據存儲狀態。
????????在整個體系中,由兩個數據指針,一個指針是write pos,即寫入位置;另一個指針式check point,即檢查點。
????????write pos:當前記錄的位置,一邊向里面寫,一邊后移,寫到第3號文件末尾后就會回到0號文件開頭。
????????check point是當前要擦除的位置,也是往后推移并且循環的,擦除記錄前需要將記錄更新到數據文件上。
????????write pos和check point之間的部分就是空著可寫的部分,可以用來記錄新的操作。如果write pos追上check point,就表示redo log已經寫滿了,這時候就不能再執行新的更新了,需要停下來擦掉一些記錄。將check point推進一下。
????????接下來我們分析一下redo log的寫入策略。
3.2.6redo log寫入策略
????????在MySQL的配置中,有一個參數innodb_flush_log_at_trx_commit。
????????這個參數就是來控制redo log的寫入策略的,它有三種可能的取值:
????????1.設置為0:表示每次事務提交時都只是將redo log留在redo log buffer中,數據庫宕機可能會丟失數據。
????????2.設置為1(默認值):表示每次事務員提交時都將redo log直接持久化到磁盤,數據最安全,不會因為數據庫宕機就丟失數據,但是效率會稍微差一些,線上系統推薦這個設置。
????????3.設置為2:每次事務提交時,都是將redo log日志直接寫入到Page Cache中,這種情況如果數據庫宕機是不會丟失數據的,但是操作系統如果宕機了,Page Cache中的數據還沒有來得及寫入磁盤文件的話就會丟失數據。
????????InnoDB有一個后臺線程,每隔1秒,就會把redo log buffer中的日志,調用操作系統函數write寫到文件系統的Page Cache,然后調用操作系統函數fsnyc持久化到磁盤文件。
????????下面是redo log寫入策略的流程圖:
????????可以使用下面的語句查詢innodb_flush_log_at_trx_commit的參數值:
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
????????下面是查詢的結果:
????????通過查詢的結果可以發現,innodb_flush_log_at_trx_commit的默認值是1,即可理解為當redo log寫入redo log buffer之后,會立刻拉取到Page Cache中,并拉取到redo log磁盤文件中。
????????可以通過以下語句設置innodb_flush_log_at_trx_commit的參數值,也可以在my.ini或者my.cnf文件中配置:
SET GLOBAL innodb_flush_log_at_trx_commit = 2;
????????執行語句后,我們再去執行下面語句去查看innodb_flush_log_at_trx_commit的值:
????????可以看到innodb_flush_log_at_trx_commit參數值已經被修改為2了,所以現在的redo log的寫入策略已經被修改為將redo log日志寫入到redo log buffer中,緊接著將redo log日志寫入到Page Cache中,不會再將數據拉取到redo log磁盤文件了,需要等待異步線程定時拉取更新。
????????但是還是建議大家將值修改為1(默認值),因為redo log寫入策略為1時安全屬性是最高的,即使是MySQL/操作系統宕機,都不會出現數據丟失的現象。
3.3梳理binlog二進制歸檔日志
????????binlog歸檔日志用于數據修改后的回滾操作,所有引擎都會有這部分,歸屬于Server層。
3.3.1基礎分析
????????binlog二進制日志記錄保存了所有執行過的修改操作語句,不保存查詢操作。如果MySQL服務意外停止,可以通過二進制日志文件排查,用戶操作或者表結構操作,從而來恢復數據庫的數據。
????????啟動binlog記錄功能,會影響服務器性能,但如果需要恢復數據或者主從復制功能,則好處大于對服務器的影響。
????????可以使用下面的語句查看binlog的相關日志:
-- 查看binlog相關參數
SHOW VARIABLES LIKE '%log_bin%'
????????查詢的結果如下:
????????詳細介紹這些參數:
????????1.log_bin:binlog日志是否處于打開狀態。
????????2.log_bin_basename:binlog日志的基本文件名稱,后面會追加標識來表示每一個文件,binlog日志文件會滾動增加。
????????3.log_bin_index:指定的是binlog文件的索引文件,這個文件管理了所有的binlog文件的目錄。
????????4.sql_log_bin:sql語句語句是否寫入binlog文件,ON代表需要寫入,OFF代表不需要寫入,如果想要在主庫上執行一些操作,但不復制到slave從庫上,可以通過修改sql_log_bin來實現。比如說,模擬主從同步復制異常。即開啟sql_log_bin時,是可以正常執行主庫的操作以及主從同步的操作的;關閉sql_log_bin是,是可以完成主庫的相關的操作的,但是主從同步的時候會出現各種異常BUG。
????????在MySQL5.7的版本中,binlog默認是關閉的,8.0默認是打開的。上圖中log_bin的值是ON就代表binlog是關閉狀態,關閉binlog功能,需要修改配置文件my.ini(windows)或者my.cnf(linux),然后重啟數據庫。
????????MySQL5.7必須使用修改配置文件的方式來修改binlog的相關配置,因為修改了配置之后,重啟才會生效,使用SET GLOBAL這種語句,它并不會做一個持久化的修改,重啟之后修改的配置就會失效了。但是MySQL8是可以直接通過SET GLOBAL的方式去修改配置,因為MySQL對于這種指令式全局修改參數之后,會做持久化處理,重啟后修改的配置依舊生效,所以是可以使用全局指令的方式修改參數后重啟的,這樣參數也會生效的。
????????在MySQL配置文件中的[mysqld]部分增加如下配置:
# log-bin設置binlog的存放位置,可以是絕對路徑,也可以是相對路徑,這是寫的相對路徑,則binlog文件默認會放在data數據目錄下,并且binlog日志文件名稱使用binlog作為前綴
log-bin=binlog
# Server Id是數據庫服務器id,隨便寫一個都可以,這個id用來在mysql的集群環境中標記唯一mysql服務器,集群環境中每臺mysql服務器的id不能一樣,否則啟動會拋出錯誤。
server-id=1
# 其它配置
binlog_format=row # 日志文件格式,下面會詳細解釋
expire_logs_days=15 # 執行自動刪除binlog日志文件的天數,默認為0,表示永久不刪除
max_binlog_size = 200M # 單個binlog日志文件的大小限制,默認為1GB
在數據庫的data數據文件夾中可以看到我們所有的binlog相關的日志文件:
????????前面這些binlog.xxxxx文件都是binlog日志文件,binlog.index文件是binlog文件的索引文件,在這個文件中管理了所有的binlog文件的目錄。
????????可以執行下面的命令查看有多少個binlog文件:
SHOW binary logs;
????????查詢結果如下:
????????可以看到當前的數據庫中還是又很多binlog文件的,可能是因為重啟的次數較多,因為每次重啟都會新建一個binlog文件。
3.3.2binlog的日志格式
????????可以使用參數binlog_format設置binlog日志的記錄格式,MySQL支持三種格式類型:
????????1.STATEMENT:基于SQL語句的復制,每一條修改數據的SQL都會被記錄到master機器(主節點機器)的bin-log中,這種方式日志量小,節約IO開銷,提升性能,但是對于一些執行過程中才能確定結果的函數,比如UUID(),SYSDATE()等函數如果隨SQL同步到slave機器(從節點機器)去執行,則結果會和master機器執行的不一樣。
????????2.ROW:基于行的復制,日志中會記錄每一行數據被修改的形式,然后在slave端再對相同的數據進行修改記錄中每一行數據修改的細節,可以解決函數、存儲過程在slave機器的復制問題,但是這種方式日志量較大,性能不如Statement。舉個例子,假設UPDATE語句更新10行數據,Statement方式只會記錄這條SQL數據,但是ROW的方式就記錄被修改的10行數據。
????????3.MIXED:混合模式復制,實際就是前兩種模式的結合,在Mixed模式下,MySQL會根據執行的每一條具體的SQL語句來區分對待記錄的日志形式,也就是再Statement和Row之間選擇一種,如果SQL里面有函數或者一些執行時才知道結果的情況,會選擇Row,其它情況選擇Statemment,更加推薦這一種根據情況抉擇的。
3.3.3binlog寫入磁盤機制
????????binlog寫入磁盤機制主要通過sync_binlog參數控制,默認值是0,現在詳細介紹一下sync_binlog的參數配置:
????????1.sync_binlog為0的時候,表示每次提交事務,都只需要write到Page Cache中,由系統自行判斷什么時候調用fsync寫入磁盤。雖然性能得到提升,但是機器宕機,Page Caceh里面的binlog會丟失,數據的安全性會下降。
????????2.sync_binlog為1的時候,表示每次提交事務都會調用操作系統的write函數將binlog寫入到Page Cache中,緊接著會調用fsync函數將binlog從Page Cache中拉取到binlog磁盤文件中,這種方式是最安全的。
????????3.sync為N時(N > 1),表示每次提交都調用操作系統的write函數將binlog寫入到Page Cache中,累計了N個事務后,才會調用操作系統函數fsync將binlog從Page Cache拉取到磁盤中,這種如果機器宕機會丟失N個事務的binlog。
當發生以下事件時,binlog日志文件會重新生成:
????????1.服務器啟動或者重新啟動。
????????2.服務器刷新日志,執行命令flush logs。
????????3.日志文件大小達到max_binlog_size值,默認值為1GB。
3.3.4刪除binlog日志文件
????????MySQL提供了三種刪除binlog日志文件:
????????1.刪除當前所有二進制日志文件:
RESET master;
????????2.刪除指定日志文件之前的所有日志文件,下面這個時刪除6之前的所有日志文件,當前指定的文件不會刪除:
PURGE master logs TO 'binlog.00006';
????????3.刪除指定日期前的日志索引中的binlog日志文件:
PURGE master logs before '2023-01-21 14:00:00';
3.3.5查看binlog日志文件
????????可以使用mysql自帶命令工具mysqlbinlog查看binlog的日志內容。
????????查看binlog二進制文件(命令行方式,無需登錄MySQL):
mysqlbinlog --no-defaults -v --base64-output=decode-rows "D:\devlop\MySQL Server 8.0\data\binlog.000005"
????????也可以在后面追加一些查詢條件:
mysqlbinlog --no-defaults -v --base64-output=decode-rows "D:\devlop\MySQL Server 8.0\data\binlog.000005" start-datetime="2025-01-21 00:00:00" stop-datetime="2025-01-22 00:00:00" start-position="5000" stop-position="20000"
????????執行mysqlbinlog指令的命令:
mysqlbinlog --no-defaults -v --base64-output=decode-rows "D:\devlop\MySQL Server 8.0\data\binlog.000005"
????????查出來的binlog日志文件內容如下:
# The proper term is pseudo_replica_mode, but we use this compatibility alias
# to make the statement usable on server versions 8.0.24 and older.
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 4
#250722 11:14:43 server id 1 end_log_pos 126 CRC32 0xa6f14657 Start: binlog v 4, server v 8.0.29 created 250722 11:14:43 at startup
ROLLBACK/*!*/;
# at 126
#250722 11:14:43 server id 1 end_log_pos 157 CRC32 0xb4cfc451 Previous-GTIDs
# [empty]
# at 157
#250722 11:50:18 server id 1 end_log_pos 234 CRC32 0x041f4720 Anonymous_GTID last_committed=0 sequence_number=1 rbr_only=no original_committed_timestamp=1753156218872739 immediate_commit_timestamp=1753156218872739 transaction_length=246
# original_commit_timestamp=1753156218872739 (2025-07-22 11:50:18.872739 中國標準時間)
# immediate_commit_timestamp=1753156218872739 (2025-07-22 11:50:18.872739 中國標準時間)
/*!80001 SET @@session.original_commit_timestamp=1753156218872739*//*!*/;
/*!80014 SET @@session.original_server_version=80029*//*!*/;
/*!80014 SET @@session.immediate_server_version=80029*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 234
#250722 11:50:18 server id 1 end_log_pos 403 CRC32 0xd3415446 Query thread_id=13 exec_time=0 error_code=0 Xid = 314
use `test`/*!*/;
SET TIMESTAMP=1753156218/*!*/;
SET @@session.pseudo_thread_id=13/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1168113696/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8mb4 *//*!*/;
SET @@session.character_set_client=255,@@session.collation_connection=255,@@session.collation_server=255/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
/*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/;
/*!80013 SET @@session.sql_require_primary_key=0*//*!*/;
/* ApplicationName=IntelliJ IDEA 2024.2.3 */ alter table t1add name int null
/*!*/;
# at 403
#250722 11:50:39 server id 1 end_log_pos 482 CRC32 0xa17a6bf0 Anonymous_GTID last_committed=1 sequence_number=2 rbr_only=no original_committed_timestamp=1753156239233279 immediate_commit_timestamp=1753156239233279 transaction_length=259
# original_commit_timestamp=1753156239233279 (2025-07-22 11:50:39.233279 中國標準時間)
# immediate_commit_timestamp=1753156239233279 (2025-07-22 11:50:39.233279 中國標準時間)
/*!80001 SET @@session.original_commit_timestamp=1753156239233279*//*!*/;
/*!80014 SET @@session.original_server_version=80029*//*!*/;
/*!80014 SET @@session.immediate_server_version=80029*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 482
#250722 11:50:39 server id 1 end_log_pos 662 CRC32 0x545f2147 Query thread_id=13 exec_time=0 error_code=0 Xid = 396
SET TIMESTAMP=1753156239/*!*/;
/*!80013 SET @@session.sql_require_primary_key=0*//*!*/;
/* ApplicationName=IntelliJ IDEA 2024.2.3 */ alter table t1modify name varchar(32) null
/*!*/;
# at 662
#250722 11:50:56 server id 1 end_log_pos 741 CRC32 0x2195b99f Anonymous_GTID last_committed=2 sequence_number=3 rbr_only=yes original_committed_timestamp=1753156256486964 immediate_commit_timestamp=1753156256486964 transaction_length=311
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
# original_commit_timestamp=1753156256486964 (2025-07-22 11:50:56.486964 中國標準時間)
# immediate_commit_timestamp=1753156256486964 (2025-07-22 11:50:56.486964 中國標準時間)
/*!80001 SET @@session.original_commit_timestamp=1753156256486964*//*!*/;
/*!80014 SET @@session.original_server_version=80029*//*!*/;
/*!80014 SET @@session.immediate_server_version=80029*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 741
#250722 11:50:56 server id 1 end_log_pos 825 CRC32 0xc8545129 Query thread_id=12 exec_time=0 error_code=0
SET TIMESTAMP=1753156256/*!*/;
BEGIN
/*!*/;
# at 825
#250722 11:50:56 server id 1 end_log_pos 882 CRC32 0x386275fa Table_map: `test`.`t1` mapped to number 114
# at 882
#250722 11:50:56 server id 1 end_log_pos 942 CRC32 0xca43cc93 Update_rows: table id 114 flags: STMT_END_F
### UPDATE `test`.`t1`
### WHERE
### @1=1
### @2=1
### @3=NULL
### SET
### @1=1
### @2=1
### @3='LiLei'
# at 942
#250722 11:50:56 server id 1 end_log_pos 973 CRC32 0xe055a72b Xid = 480
COMMIT/*!*/;
# at 973
#250722 11:58:59 server id 1 end_log_pos 1052 CRC32 0xc7768802 Anonymous_GTID last_committed=3 sequence_number=4 rbr_only=yes original_committed_timestamp=1753156739427840 immediate_commit_timestamp=1753156739427840 transaction_length=292
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
# original_commit_timestamp=1753156739427840 (2025-07-22 11:58:59.427840 中國標準時間)
# immediate_commit_timestamp=1753156739427840 (2025-07-22 11:58:59.427840 中國標準時間)
/*!80001 SET @@session.original_commit_timestamp=1753156739427840*//*!*/;
/*!80014 SET @@session.original_server_version=80029*//*!*/;
/*!80014 SET @@session.immediate_server_version=80029*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 1052
#250722 11:58:59 server id 1 end_log_pos 1127 CRC32 0xc33076fc Query thread_id=12 exec_time=0 error_code=0
SET TIMESTAMP=1753156739/*!*/;
BEGIN
/*!*/;
# at 1127
#250722 11:58:59 server id 1 end_log_pos 1184 CRC32 0x236c879e Table_map: `test`.`t1` mapped to number 114
# at 1184
#250722 11:58:59 server id 1 end_log_pos 1234 CRC32 0xedad9f38 Write_rows: table id 114 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=2
### @2=2
### @3='NiuMa'
# at 1234
#250722 11:58:59 server id 1 end_log_pos 1265 CRC32 0x0f5ef106 Xid = 522
COMMIT/*!*/;
# at 1265
#250722 11:59:27 server id 1 end_log_pos 1344 CRC32 0x086cb601 Anonymous_GTID last_committed=4 sequence_number=5 rbr_only=yes original_committed_timestamp=1753156767438213 immediate_commit_timestamp=1753156767438213 transaction_length=292
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
# original_commit_timestamp=1753156767438213 (2025-07-22 11:59:27.438213 中國標準時間)
# immediate_commit_timestamp=1753156767438213 (2025-07-22 11:59:27.438213 中國標準時間)
/*!80001 SET @@session.original_commit_timestamp=1753156767438213*//*!*/;
/*!80014 SET @@session.original_server_version=80029*//*!*/;
/*!80014 SET @@session.immediate_server_version=80029*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 1344
......
????????能看到里面由具體執行的修改偽SQL語句以及執行時的相關情況。
3.3.6binlog日志文件恢復數據
????????用binlog日志文件恢復數據其實就是回放執行之前記錄在binlog里的SQL。
????????舉一個數據恢復的例子:
????????1.先向數據表中插入相關的數據:
INSERT INTO `test`.`t1` (id, name, balance) VALUES (21, 'aa', 1000);
INSERT INTO `test`.`t1` (id, name, balance) VALUES (22, 'bb', 2000);
????????2.然后將插入的新數據刪除,模擬數據被不小心刪除的場景:
DELETE FROM `test`.`t1` WHERE id IN (21, 22);
????????3.查詢binlog日志中記錄的信息:
# at 1052
#250726 14:29:16 server id 1 end_log_pos 1131 CRC32 0x1edaff52 Anonymous_GTID last_committed=3 sequence_number=4 rbr_only=yes original_committed_timestamp=1753511356228725 immediate_commit_timestamp=1753511356228725 transaction_length=289
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
# original_commit_timestamp=1753511356228725 (2025-07-26 14:29:16.228725 中國標準時間)
# immediate_commit_timestamp=1753511356228725 (2025-07-26 14:29:16.228725 中國標準時間)
/*!80001 SET @@session.original_commit_timestamp=1753511356228725*//*!*/;
/*!80014 SET @@session.original_server_version=80029*//*!*/;
/*!80014 SET @@session.immediate_server_version=80029*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 1131
#250726 14:29:16 server id 1 end_log_pos 1206 CRC32 0xf51a85bb Query thread_id=9 exec_time=0 error_code=0
SET TIMESTAMP=1753511356/*!*/;
BEGIN
/*!*/;
# at 1206
#250726 14:29:16 server id 1 end_log_pos 1263 CRC32 0x544a85c4 Table_map: `test`.`t1` mapped to number 83
# at 1263
#250726 14:29:16 server id 1 end_log_pos 1310 CRC32 0xdca8dd01 Write_rows: table id 83 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=21
### @2='aa'
### @3=1000
# at 1310
#250726 14:29:16 server id 1 end_log_pos 1341 CRC32 0xc84bf74e Xid = 77
COMMIT/*!*/;
# at 1341
#250726 14:29:16 server id 1 end_log_pos 1420 CRC32 0x19c122ed Anonymous_GTID last_committed=4 sequence_number=5 rbr_only=yes original_committed_timestamp=1753511356244868 immediate_commit_timestamp=1753511356244868 transaction_length=289
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
# original_commit_timestamp=1753511356244868 (2025-07-26 14:29:16.244868 中國標準時間)
# immediate_commit_timestamp=1753511356244868 (2025-07-26 14:29:16.244868 中國標準時間)
/*!80001 SET @@session.original_commit_timestamp=1753511356244868*//*!*/;
/*!80014 SET @@session.original_server_version=80029*//*!*/;
/*!80014 SET @@session.immediate_server_version=80029*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 1420
#250726 14:29:16 server id 1 end_log_pos 1495 CRC32 0x4ff035b9 Query thread_id=9 exec_time=0 error_code=0
SET TIMESTAMP=1753511356/*!*/;
BEGIN
/*!*/;
# at 1495
#250726 14:29:16 server id 1 end_log_pos 1552 CRC32 0x20cce7f3 Table_map: `test`.`t1` mapped to number 83
# at 1552
#250726 14:29:16 server id 1 end_log_pos 1599 CRC32 0xce583b8c Write_rows: table id 83 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=22
### @2='bb'
### @3=2000
# at 1599
#250726 14:29:16 server id 1 end_log_pos 1630 CRC32 0xe6c91b72 Xid = 84
COMMIT/*!*/;
# at 1630
????????4.找到兩條插入數據的SQL,每條SQL的上下都有BEGIN和COMMIT,我們找到了一條sql BEGIN前面的文件位置標識at 1052(這里時文件的位置標識),再找到第二條SQL COMMIT后面的文件位置標識at 1630。
????????可以根據文件位置標識來恢復數據,執行如下SQL:
mysqlbinlog --no-defaults --start-position=1052 --stop-position=1630 --database=test "D:\devlop\MySQL Server 8.0\data\binlog.000015" | mysql -uroot -p123456 -v test
????????使用如下的SQL查詢數據是否被恢復了:
SELECT * FROM `test`.`t1` WHERE id IN (21, 22);
????????查詢結果如下:
????????可以看到被刪除的數據已經全部恢復了。
3.3.7binlog如何合理做到防止程序員刪庫跑路
????????如果需要恢復大量的數據,比如程序員經常說到的刪庫跑路的話題,假設將數據庫中的數據都刪除了該如何恢復呢?如果數據庫之前沒有備份,所有binlog日志都在的化,就可以從binlog的第一個文件開始逐個恢復每個binlog里面的數據。但是這種情況一般是不太可能的,因為binlog日志文件比較大,早期的binlog文件是會定期刪除的,所以一般不可能用binlog文件恢復整個數據庫的。
????????一般合理的方案是:
????????1.每天凌晨對數據庫做一次全量備份。
????????2.開啟binlog,并且設定一個定時刪除binlog的時間,一般時間就是在備份的時間的附近刪除。
????????3.當需要恢復數據庫時,可以使用最近一次全量備份再加上備份時間點之后的binlog來恢復數據。
????????備份數據庫一般可以使用mysqldump命令工具對數據庫進行恢復:
????????1.備份整個數據:
mysqldump -uroot -p test > test.sql
Enter password: ******
????????可以發現此時數據庫中所有的數據表數據都被備份了:
????????2.備份整個表:
mysqldump -uroot -p test t1 > test-t1.sql
Enter password: ******
????????可以發現此時數據庫中t1數據表中的數據都被備份了:
????????3.恢復整個庫:
mysqldump -uroot -p test < test.sql
3.3.8為什么會有redo log和binlog兩份日志呢?
????????binlog是早于redo log出現的,binlog位于Server層,是所有的執行引擎都會有的,redo log位于InnoDB的執行引擎層,只有InnoDB執行引擎才會有。
????????在早期的MyISAM執行引擎中,是沒有事務的概念的,即每一個SQL語句都是一個原子事務,不可以自己定義原子事務。
????????只要原子事務提交成功之后,binlog就會記錄日志,在原子事務執行成功前,系統崩潰了,binlog日志不會自動記錄,數據也不會記錄,即完成了自動回滾,但是如果原子事務執行成功了,并且進行了提交,但是磁盤的數據還沒有修改成功,此時MySQL可以使用binlog日志中記錄的數據來執行SQL恢復之前的數據。
????????但是當另一個公司將InnoDB以插件的形式引入MySQL時,此時可以自定義多條SQL語句為原子事務了,開啟一個事務之后,在事務中執行了多個數據修改操作之后,如果沒有提交事務,宕機了,數據自然就不會被修改,因為根本沒有提交,但是如果提交之后,宕機了,但是此時數據還沒有刷新到數據庫,但是因為binlog是做歸檔操作的,并不是用于做數據恢復工作的,所以InnoDB就引入了一套新的日志體系redo log,來解決在事務提交后沒有刷新到硬盤時,因為數據庫宕機導致的數據丟失問題。
????????其中主要是保障了InnoDB的crash-safe能力。
3.4undo log回滾日志
3.4.1基礎分析
????????InnoDB對undo log文件的管理采用段的方式,也就是回滾段(rollback segment)。每個回滾段記錄了1024個undo log segment,每個事務只會使用一個undo log segment。
????????在MySQL5.5的時候,只有一個回滾段,那么同時最大支持的事務數量1024個。在MySQL5.6開始,InnoDB支持最大128回滾段,故其支持同時在線的事務限制提高到了128 * 1024個。
????????使用下面的語句查看undo log回滾日志的相關配置:
SHOW VARIABLES LIKE '%innodb_undo_%';
????????查詢結果如下:
????????1.innodb_undo_directory:設置undo log文件所在的路徑。該參數的默認值是‘.\’,即InnoDB數據文件存儲位置,目錄下ibdata1文件就是undo log存儲的位置(當不設置innodb_undo_tablespaces的參數的時候,默認就是使用ibdata1文件來存儲undolog日志的)
????????2.innodb_undo_tablespaces:設置unlog log文件的數量,這樣回滾段可以較為平均地分布多個文件中。設置該參數之后,會在路徑innodb_undo_directory看到以undo為前綴的文件。
????????在MySQL8.0.2之前可以使用innodb_undo_logs參數來設置MySQL支持的分段數,默認是128,在MySQL8被刪除了,現在使用innodb_rollback_segments代替:
SHOW VARIABLES LIKE 'innodb_rollback_segments';
3.4.2undo log日志什么時候刪除
????????新增類型的,在事務提交之后就可以清除掉了。
????????修改類型的,事務提交之后不能立即清除掉,這樣日志需要用于MVCC機制,只有當沒有事務被用到該版本信息時才可以清除。
3.5復雜日志設計面試題詳解
????????為什么MySQL不能直接更新磁盤上的數據而是設置這么一套復雜的機制來執行SQL?
????????因為MySQL在執行SQL寫入數據的時候,其寫入磁盤的機理是磁盤隨機寫,對于MySQL當時的年代來說,磁盤隨機寫的性能太差了,所以直接更新磁盤文件是不能讓MySQL數據庫抗住很高的并發的。
????????MySQL這套機制看起來復雜,但是它可以保證每個更新請求都是更新內存BufferPool,然后順序寫日志文件,同時還能保證各種異常情況下的數據一致性。
????????更新內存的性能是極高的,順序寫磁盤上的日志文件的性能也是非常高的,要遠高于隨機讀寫磁盤文件。
????????正式借助這套機制,MySQL才可以在配置較高的服務器上實現幾千甚至上萬的并發量。
3.6錯誤日志
????????MySQL其中還有一個非常重要的機制是錯誤日志,它記錄了數據庫的啟動和停止,以及運行過程中發生任何嚴重錯誤時的相關信息。
????????當數據庫出現任何故障導致無法正常使用時,建議首先查看此日志。
????????在MySQL數據庫中,錯誤日志功能是默認開啟的,而且無法被關閉。
????????使用下面的語句可以查看MySQL錯誤日志存放位置:
SHOW VARIABLES LIKE '%log_error%';
????????運行結果:
????????找到log_error對應的錯誤日志存儲地址,打開文件夾可以看到里面記錄的一些錯誤日志:
3.7通用查詢日志
????????通用查詢日志記錄了用戶的所有操作,包括啟動和關閉MySQL服務,所有用戶的連接開始時間和截止時間,發給MySQL數據庫服務器的所有SQL指令等,如SELECT、SHOW等,無論是SQL的語法正確還是錯誤,也無論是SQL成功還是失敗,MySQL都會將其記錄下來。
????????通用查詢日志可以用來還原操作時的具體場景,幫助我們準確定位一些疑難問題,比如重復支付等問題。但是由于通用查詢日志記錄了所有的SQL指令及一些細節信息,所以其會消耗大量的系統資源并占用大量的磁盤空間,MySQL默認是關閉該日志記錄的,建議只有當需要調試定位系統問題的時候才打開。
????????使用下面的查詢語句可以查看通用查詢日志是否打開以及通用查詢日甚至的存儲位置:
SHOW VARIABLES LIKE '%general_log%';
????????查詢結果:
????????根據配置變量general_log的值可以發現,默認情況下通用查詢日志是關閉的,可以使用下面的語句開啟通用查詢日志:
SET GLOBAL general_log=on;
????????根據配置變量general_log_file的值可以看到通訊查詢日志存儲在什么位置。