背景
在 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 后面多了個 '\0'。 這個問題只在 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 '\0' 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 '\0' is expected....
5.6 版本的 mysql client 多了一個參數 --binary-mode,允許語句里有 '\0',所以如果是用5.6的話,就可以不用修改代碼,重放binlog時這樣做就可以了:
mysqlbinlog mysql-bin.000001 | mysql --binary-mode -hxxxx -Pxx -u
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态