MySQL核心月報2014.10-MySQL· 捉蟲動態·binlog重放失敗

db匠發表於2016-05-23

背景

在 MySQL 日常維護中,要回滾或者恢復資料,我們經常會用 binlog 來在資料庫上重放,執行類似下面的語句:

mysqlbinlog mysql-bin.000001 | mysql -hxxxx -Pxx -u

最近遇到了這樣一個問題,在重放 binlog 時,mysqld 報這樣的錯

ERROR 1064 (42000) at line 25: 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 `DELIMITER ;


分析

上面的錯是說語法不對,難道是 binlog 寫錯了,為了方便檢視,先把 mysqlbinlog 解析結果儲存到一個檔案

mysqlbinlog mysql-bin.000001 > abc.sql

然後開啟 abc.sql 檔案,會看到這樣的語句

“CREATE TABLE t_binlog_sbr(a int)^@”

最後面的奇怪的 “^@” 這是啥呢,我們用二進位制方式開啟檔案後,發現這個其實是1個位元組,值是 00,被顯示成 “^@”了。

為啥後面會多 1 個 0 呢,後來發現是使用者在用 MySQL C API 時用錯了,具體是這個函式 mysql_real_query,基原型是

int mysql_real_query(MYSQL *mysql, const char *stmt_str, unsigned long length)

詳細說明參考這裡,length 參數列示 stmt_str 的長度,所以正常的呼叫應該是這樣的:

mysq_real_query(mysql, sql, strlen(sql))

可是使用者在使用時多加了個1,變成這樣

mysq_real_query(mysql, sql, strlen(sql) + 1)

最終導致記錄的 binlog 後面多了個 ` `。 這個問題只在 statement 格式有,row 格式沒有。


解決方法

有同學會問,+1 可以,+2、 +3 呢,這個是不可以的,>=2 的都是不行的,語句發過來後,mysqld 在 parse_sql 階段直接報錯返回了,後面就不會執行了。

1) 修改程式碼

mysq_real_query(mysql, sql, strlen(sql) + 1) 這種用法是不對的,但是 MySQL 卻允許,雖然這麼用是不對的,但是為了相容性,最好還是允許這種使用方式,但是在寫binlog的時候做個判斷,長度是不是寫錯了,錯了的話糾正過來,在 THD::binlog_query 裡面改。


2) 5.6 版本加引數

如果是用 5.6 版本的 mysql client 的話,在重放時出錯提示資訊不一樣,是類似下面這樣的,更加友好,這個錯誤是 mysql client 報的,不是mysqld報的:

ERROR at line 24: ASCII ` ` appeared in the statement, but this is not allowed unless option –binary-mode is enabled and mysql is run in non-interactive mode. Set –binary-mode to 1 if ASCII ` ` is expected….

5.6 版本的 mysql client 多了一個引數 –binary-mode,允許語句裡有 ` `,所以如果是用5.6的話,就可以不用修改程式碼,重放binlog時這樣做就可以了:

mysqlbinlog mysql-bin.000001 | mysql –binary-mode -hxxxx -Pxx -u


相關文章