天天看點

MYSQL資料庫的日志檔案

日志檔案:用來記錄MySQL執行個體對某種條件做出響應時寫入的檔案。如錯誤日志檔案、二進制日志檔案、慢查詢日志檔案、查詢日志檔案等。

show variables like 'log_error';

system hostname;

MYSQL資料庫的日志檔案

可以看到錯誤檔案的路徑和檔案名,預設情況下錯誤檔案的檔案名為伺服器的主機名。當出現MySQL資料庫不能正常啟動時,第一個必須查找的檔案應該就是錯誤日志檔案,該檔案記錄了出錯資訊,能很好地指導我們找到問題。

慢查詢能為SQL語句的優化帶來很好的幫助。可以設一個門檻值,将運作時間超過該值的所有SQL語句都記錄到慢查詢日志檔案中。該門檻值可以通過參數long_query_time來設定,預設值為10,代表10秒。預設情況下,MySQL資料庫并不啟動慢查詢日志,你需要手工将這個參數設為ON,然後啟動,可以看到如下結果:

show variables like '%long_query%'; 

show variables like 'slow_query_log%';

set global slow_query_log=ON;

MYSQL資料庫的日志檔案

 這裡需要注意兩點。首先,設定long_query_time這個門檻值後,MySQL資料庫會記錄運作時間超過該值的所有SQL語句,但對于運作時間正好等于long_query_time的情況,并不會被記錄下。也就是說,在源代碼裡是判斷大于long_query_time,而非大于等于。其次,從MySQL 5.1開始,long_query_time開始以微秒記錄SQL語句運作時間,之前僅用秒為機關記錄。這樣可以更精确地記錄SQL的運作時間,供DBA分析。對DBA來說,一條SQL語句運作0.5秒和0.05秒是非常不同的,前者可能已經進行了表掃,後面可能是走了索引。

另一個和慢查詢日志有關的參數是log_queries_not_using_indexes,如果運作的SQL語句沒有使用索引,則MySQL資料庫同樣會将這條SQL語句記錄到慢查詢日志檔案。首先,确認打開了log_queries_not_using_indexes: 

show variables like 'log_queries_not_using_indexes';

MYSQL資料庫的日志檔案

這裡詳細記錄了SQL語句的資訊,如上述SQL語句運作的賬戶和IP、運作時間、鎖定的時間、傳回行等。我們可以通過慢查詢日志來找出有問題的SQL語句,對其進行優化。随着MySQL資料庫伺服器運作時間的增加,可能會有越來越多的SQL查詢被記錄到了慢查詢日志檔案中,這時要分析該檔案就顯得不是很容易了。MySQL這時提供的mysqldumpslow指令,可以很好地解決這個問題: 

如果我們想得到鎖定時間最長的10條SQL語句,可以運作:

MySQL 5. 1開始可以将慢查詢的日志記錄放入一張表中,這使我們的查詢更加直覺。慢查詢表在mysql架構下,名為slow_log。其表結構定義如下:

參數log_output指定了慢查詢輸出的格式,預設為FILE,你可以将它設為TABLE,然後就可以查詢mysql架構下的slow_log表了,如:

show variables like 'log_output';

set global log_output='TABLE';

select sleep (10);

select * from mysql.slow_log\G

MYSQL資料庫的日志檔案
MYSQL資料庫的日志檔案

參數log_output是動态的,并且是全局的。我們可以線上進行修改。在上表中我設定了睡眠(sleep)10秒,那麼這句SQL語句就會被記錄到slow_log表了。

檢視slow_log表的定義會發現,該表使用的是CSV引擎,對大資料量下的查詢效率可能不高。我們可以把slow_log表的引擎轉換到MyISAM,用來進一步提高查詢的效率。但是,如果已經啟動了慢查詢,将會提示錯誤: 

不能忽視的是,将slow_log表的存儲引擎更改為MyISAM後,對資料庫還是會造成額外的開銷。不過好在很多關于慢查詢的參數都是動态的,我們可以友善地線上進行設定或者修改。

查詢日志記錄了所有對MySQL資料庫請求的資訊,不論這些請求是否得到了正确的執行。預設檔案名為:主機名.log。我們檢視一個查詢日志:

通過上述查詢日志你會發現,查詢日志甚至記錄了對access denied的請求。同樣,從MySQL 5.1開始,可以将查詢日志的記錄放入mysql架構下的general_log表,該表的使用方法和前面小節提到的slow_log基本一樣。

set global general_log=ON;

二進制日志記錄了對資料庫執行更改的所有操作,但是不包括SELECT和SHOW這類操作,因為這類操作對資料本身并沒有修改,如果你還想記錄SELECT和SHOW操作,那隻能使用查詢日志,而不是二進制日志了。此外,二進制還包括了執行資料庫更改操作的時間和執行時間等資訊。二進制日志主要有以下兩種作用:

恢複(recovery)。某些資料的恢複需要二進制日志,如當一個資料庫全備檔案恢複後,我們可以通過二進制日志進行point-in-time的恢複。

複制(replication)。其原理與恢複類似,通過複制和執行二進制日志使得一台遠端的MySQL資料庫(一般稱為slave或者standby)與一台MySQL資料庫(一般稱為master或者primary)進行實時同步。

通過配置參數log_bin[=name]可以啟動二進制日志。如果不指定name,則預設二進制日志檔案名為主機名,字尾名為二進制日志的序列号,所在路徑為資料庫所在目錄(datadir)如:

MYSQL資料庫的日志檔案

show variables like 'datadir';

MYSQL資料庫的日志檔案

 這裡的bin_log.00001即為二進制日志檔案,我們在配置檔案中指定了名稱,是以沒有用預設的檔案名。bin_log.index為二進制的索引檔案,用來存儲過往生産的二進制日志序号,通常情況下,不建議手工修改這個檔案。

二進制日志檔案在預設情況下并沒有啟動,需要你手動指定參數來啟動。可能有人會質疑,開啟這個選項是否會對資料庫整體性能有所影響。不錯,開啟這個選項的确會影響性能,但是性能的損失十分有限。根據MySQL官方手冊中的測試表明,開啟二進制日志會使得性能下降1%。但考慮到可以使用複制(replication)和point-in-time的恢複,這些性能損失絕對是可以并且是應該被接受的。

以下配置檔案的參數影響着二進制日志記錄的資訊和行為:

max_binlog_size

binlog_cache_size

sync_binlog

binlog-do-db

binlog-ingore-db

log-slave-update

binlog_format

max_binlog_size:參數max_binlog_size指定了單個二進制日志檔案的最大值,如果超過該值,則産生新的二進制日志檔案,字尾名+1,并記錄到.index檔案。從MySQL 5.0開始的預設值為1 073 741 824,代表1GB(之前的版本max-binlog-size預設大小為1.1GB)。

binlog_cache_size:當使用事務的表存儲引擎(如InnoDB存儲引擎)時,所有未送出(uncommitted)的二進制日志會被記錄到一個緩存中,等該事務送出時(committed)時直接将緩沖中的二進制日志寫入二進制日志檔案,而該緩沖的大小由binlog_cache_size決定,預設大小為32KB。此外,binlog_cache_size是基于會話(session)的,也就是說,當一個線程開始一個事務時,MySQL會自動配置設定一個大小為binlog_cache_size的緩存,是以該值的設定需要相當小心,不能設定過大。當一個事務的記錄大于設定的binlog_cache_size時,MySQL會把緩沖中的日志寫入一個臨時檔案中,是以該值又不能設得太小。通過show global status指令檢視binlog_cache_use、binlog_cache_disk_use的狀态,可以判斷目前binlog_cache_size的設定是否合适。binlog_cache_use記錄了使用緩沖寫二進制日志的次數,binlog_cache_disk_use記錄了使用臨時檔案寫二進制日志的次數。現在來看一個資料庫的狀态:

show variables like 'binlog_cache_size';

show global status like 'binlog_cache%'; 

MYSQL資料庫的日志檔案

使用緩沖次數0次,臨時檔案使用次數為0。看來,32KB的緩沖大小對于目前這個MySQL資料庫完全夠用,是以暫時沒有必要增加binlog_cache_size的值。

sync_binlog:預設情況下,二進制日志并不是在每次寫的時候同步到磁盤(我們可以了解為緩沖寫)。是以,當資料庫所在作業系統發生當機時,可能會有最後一部分資料沒有寫入二進制日志檔案中。這會給恢複和複制帶來問題。參數sync_binlog=[N]表示每寫緩沖多少次就同步到磁盤。如果将N設為1,即sync_binlog=1表示采用同步寫磁盤的方式來寫二進制日志,這時寫操作不使用作業系統的緩沖來寫二進制日志。該預設值為0,如果使用InnoDB存儲引擎進行複制,并且想得到最大的高可用性,建議将該值設為ON。不過該值為ON時,确實會對資料庫的IO系統帶來一定的影響。但是,即使将sync_binlog設為1,還是會有一種情況會導緻問題的發生。當使用InnoDB存儲引擎時,在一個事務發出COMMIT動作之前,由于sync_binlog設為1,是以會将二進制日志立即寫入磁盤。如果這時已經寫入了二進制日志,但是送出還沒有發生,并且此時發生了當機,那麼在MySQL資料庫下次啟動時,因為COMMIT操作并沒有發生,是以這個事務會被復原掉。但是二進制日志已經記錄了該事務資訊,不能被復原。這個問題可以通過将參數innodb_support_xa設為1來解決,雖然innodb_support_xa與XA事務有關,但它同時也確定了二進制日志和InnoDB存儲引擎資料檔案的同步。

binlog_do_db和binlog_ignore_db:參數binlog_do_db和binlog_ignore_db表示需要寫入或者忽略寫入哪些庫的日志。預設為空,表示需要将所有庫的日志同步到二進制日志。如果目前資料庫是複制中的slave角色,則它不會将從master取得并執行的二進制日志寫入自己的二進制日志檔案中。如果需要寫入,則需要設定log-slave-update。如果你需要搭建master=>slave=>slave架構的複制,則必須設定該參數。

binlog_format:這影響了記錄二進制日志的格式。在MySQL 5.1版本之前,沒有這個參數。所有二進制檔案的格式都是基于SQL語句(statement)級别的,是以基于這個格式的二進制日志檔案的複制(Replication)和Oracle邏輯Standby有點相似。同時,對于複制是有一定要求的如rand、uuid等函數,或者有使用觸發器等可能會導緻主從伺服器上表的資料不一緻(not sync),這可能使得複制變得沒有意義。另一個影響是,你會發現InnoDB存儲引擎的預設事務隔離級别是REPEATABLE READ。這其實也是因為二進制日志檔案格式的關系,如果使用READ COMMITTED的事務隔離級别(大多數資料庫,如Oracle、Microsoft SQL Server資料庫的預設隔離級别)會出現類似丢失更新的現象,進而出現主從資料庫上的資料不一緻。

MySQL 5. 1開始引入了binlog_format參數,該參數可設的值有STATEMENT、ROW和MIXED。

(1)STATEMENT格式和之前的MySQL版本一樣,二進制日志檔案記錄的是日志的邏輯SQL語句。

(2)在ROW格式下,二進制日志記錄的不再是簡單的SQL語句了,而是記錄表的行更改情況。基于ROW格式的複制類似于Oracle的實體Standby(當然,還是有些差別)。同時,對于上述提及的Statement格式下複制的問題給予了解決。MySQL 5.1版本開始,如果設定了binlog_format為ROW,你可以将InnoDB的事務隔離基本設為READ COMMITTED,以獲得更好的并發性。

(3)MIXED格式下,MySQL預設采用STATEMENT格式進行二進制日志檔案的記錄,但是在一些情況下會使用ROW格式,可能的情況有:

表的存儲引擎為NDB,這時對于表的DML操作都會以ROW格式記錄。

使用了UUID()、USER()、CURRENT_USER()、FOUND_ROWS()、ROW_COUNT()等不确定函數。

使用了INSERT DELAY語句。

使用了使用者定義函數(UDF)。

使用了臨時表(temporary table)。

此外,binlog_format參數還有對于存儲引擎的限制:

MYSQL資料庫的日志檔案

binlog_format是動态參數,是以可以在資料庫運作環境下進行更改,例如,我們可以将目前會話的binlog_format設為ROW,如:

show variables like '%binlog_format%';

MYSQL資料庫的日志檔案

通常情況下,我們将參數binlog_format設定為ROW,這可以為資料庫的恢複和複制帶來更好的可靠性。但是不能忽略一點的是,這會帶來二進制檔案大小的增加,有些語句下的ROW格式可能需要更大的容量。

将參數binlog_format設定為ROW,對于磁盤空間要求有了一定的增加。而由于複制是采用傳輸二進制日志方式實作的,是以複制的網絡開銷也有了增加。

二進制日志檔案的檔案格式為二進制,不能像錯誤日志檔案,慢查詢日志檔案用cat、head、tail等指令來檢視。想要檢視二進制日志檔案的内容,須通過MySQL提供的工具mysqlbinlog。對于STATEMENT格式的二進制日志檔案,使用mysqlbinlog後,看到就是執行的邏輯SQL語句,如:

update t2 set username=upper(username)where id=1,這個可以看到日志的記錄以SQL語句的方式(為了排版的友善,省去了一些開始的資訊)。在這個情況下,mysqlbinlog和Oracle LogMiner類似。但是如果這時使用ROW格式的記錄方式,則會發現mysqlbinlog的結果變得“不可讀”(unreadable),如:

mysqlbinlog向我們解釋了具體做的事情。可以看到,一句簡單的update t2 set username=upper(username)where id=1語句記錄為了對于整個行更改的資訊,這也解釋了為什麼前面我們更新了10萬行的資料,在ROW格式下,二進制日志檔案會增大了13MB。