MySQL中的2個小問題
- 2019 年 12 月 19 日
- 筆記
MySQL中的2個小問題
今天晚上,在一個單機多實例的環境上,發生了一個錯誤,看著比較奇怪,之前也遇到過,但是沒有留意,今天花了一點時間,搞了一下,問題得到了解決,跟大家分享一下。
01
錯誤資訊無法顯示
問題描述:
ERROR 1238 (HY000): Unknown error 1238
我們知道,當我們在MySQL中執行一個SQL命令的時候,如果我們的命令寫錯了,MySQL會給我們進行報錯。我遇到的問題就是在單機多實例的環境下面,報錯的資訊缺失,如上所示,只有一個error code,以前碰到這個問題,都是從MySQL的官方文檔中去查這個error code對應的錯誤是什麼,(補充一下官網的error code鏈接)今天實在是受不了了,查了一下這個問題,先從錯誤日誌入手吧,我發現日誌中的內容都是這樣的:
2019-12-17T12:47:14.815692Z 0 [Note] 2019-12-17T12:47:14.816961Z 0 [Note] 2019-12-17T12:47:14.816973Z 0 [Note] 2019-12-17T12:47:14.817727Z 0 [Note] 2019-12-17T12:47:14.867818Z 0 [Note] 2019-12-17T12:47:14.868658Z 0 [Note] 2019-12-17T12:47:14.869041Z 0 [Note] 2019-12-17T12:47:21.682618Z 0 [Note]
很明顯,日誌中也沒有打出來錯誤資訊,這應該是錯誤資訊由於某種原因不能顯示了。再查看了歷史的err日誌關鍵字,如下:
[root@ log]# cat mysql.err |grep err [Warning] The syntax '--log_warnings/-W' is deprecated and will be removed in a future release. Please use '--log_error_verbosity' instead. [Warning] The syntax '--log_warnings/-W' is deprecated and will be removed in a future release. Please use '--log_error_verbosity' instead. [ERROR] Can't read from messagefile '/usr/local/mysql-5.5.19-linux2.6-x86_64/share/english/errmsg.sys'
發現了一行比較關鍵的字樣,就是messagefile無法從文件errmsg.sys中讀取,因為路徑是5.5.19版本的,這一點引起了我的注意,使用s命令查看了一下線上的資料庫版本:
/usr/local/mysql/bin/mysql Ver 14.14 Distrib 5.5.19, for linux2.6 (x86_64) using readline 5.1 Connection id: 3525 Current database: Current user: dba_admin@127.0.0.1 SSL: Not in use Current pager: stdout Using outfile: '' Using delimiter: ; Server version: 5.7.16-10-log Percona Server (GPL), Release , Revision a0c7d0d Protocol version:
可以看到資料庫的版本是5.7.16,但是客戶端的版本是5.5.19,到這裡,我開始懷疑是不是系統環境變數中配置的是5.5.19的客戶端,連接5.7.16的MySQL不兼容導致的,於是使用了新版本的5.7.16的客戶端來重新連接資料庫,發現問題還是一樣的,沒有報錯資訊。很顯然,跟客戶端沒有什麼大的關係。
客戶端排除了,那麼伺服器呢,是不是有些參數配置有問題?查了下這個errmsg的配置,發現是通過參數lc_messages_dir
來控制的,該參數的解釋如下:
The directory where error messages are located. The server uses the value together with the value of lc_messages
to produce the location for the error message file.
其中,lc_message是本地的錯誤資訊語言,默認的值是en_US,官網解釋是:
The locale to use for error messages. The default is en_US
. The server converts the argument to a language name and combines it with the value of lc_messages_dir
to produce the location for the error message file
看到這裡,可能問題就比較明顯了,查看了一下當前MySQL Server的這倆值,發現果然有點問題,如下:
mysql ::>>show variables like '%messages_dir%'; +-----------------------------------------+---------------------------------------------------------+ | Variable_name | Value | +-----------------------------------------+---------------------------------------------------------+ | lc_messages_dir | /usr/local/mysql-5.5.19-linux2.6-x86_64/share/ | +-----------------------------------------+---------------------------------------------------------+ rows in set (. sec)
可以看到,伺服器的版本是5.7.16,但是配置中的errmsg的文件路徑是5.5.19版本的,於是我重新修改了一下路徑,首先嘗試了一發手動修改,結果如下:
mysql ::>>set global lc_messages_dir='/usr/local/Percona-Server-5.7.16-10-Linux.x86_64.ssl101/share/'; ERROR (HY000): Unknown error
很明顯,不讓直接修改,而且報錯資訊依舊只有個Unknow error 1238,於是我在配置文件中添加了一個記錄:
lc-messages-dir=/usr/local/Percona-Server-5.7.--Linux.x86_64.ssl101/share
再次重啟資料庫,問題得到了解決,錯誤資訊也出來了。
mysq ::>>set global lc_messages_dir='/usr/local/Percona-Server-5.7.16-10-Linux.x86_64.ssl101/share/'; ERROR (HY000): Variable 'lc_messages_dir' is a read only variable
02
字元串截取的一個小方法
上面是錯誤資訊的問題,再來看第二個問題,今天遇到了一個需求,是把一個表中的最末尾的數字記錄都給取出來,每行記錄的是由字母和數字組成的varchar字元串,如下:
mysql ::>>select * from aaa; +-----------+ | name | +-----------+ | t8 | | number129 | | abc0111 | +-----------+ rows in set (. sec)
結果想要的是:
8
129
0111
這個問題,使用字元串的reverse函數和運算符中的"-"符號組合解決的,具體的方法如下,大家可以看看有沒有更好的方法:
1、使用reverse函數翻轉字元串,將數字反向放在前面
2、使用-符號對數字+字元的結構進行截斷,變成一個負的數字
3、將負的數字變為正
4、將正的數字翻轉即可
SQL如下:
mysql ::>>select reverse(name) from aaa; +---------------+ | reverse(name) | +---------------+ | 8t | | 921rebmun | | 1110cba | +---------------+ rows in set (. sec) mysql ::>>select -reverse(name) from aaa; +----------------+ | -reverse(name) | +----------------+ | -8 | | -921 | | -1110 | +----------------+ rows in set (. sec) mysql ::>>select -(-reverse(name)) from aaa; +-------------------+ | -(-reverse(name)) | +-------------------+ | 8 | | 921 | | 1110 | +-------------------+ rows in set (. sec) mysql ::>>select reverse(-(-reverse(name))) from aaa; +----------------------------+ | reverse(-(-reverse(name))) | +----------------------------+ | 8 | | 129 | | 0111 | +----------------------------+ rows in set (. sec) mysql ::>>select reverse(-(-reverse(name))) as name from aaa; +------+ | name | +------+ | 8 | | 129 | | 0111 | +------+ rows in set (. sec)
最後,拋一個還沒有解決的問題,如下:
首先我們創建一個表name,name是varchar(10)類型,如下:
mysql 22:24:49>>show create table aaaG *************************** 1. row *************************** Table: aaa Create Table: CREATE TABLE `aaa` ( `name` varchar(10) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec)
然後插入一組數據,進行update的演示
mysql ::>>select * from aaa; +------+ | name | +------+ | t8 | | 9 | | 10 | +------+ rows in set (. sec) #隱式類型轉換 mysql ::>>update aaa set name = 'number129' where name=; Query OK, row affected (. sec) Rows matched: Changed: Warnings: mysql :08:>>select * from aaa; +-----------+ | name | +-----------+ | t8 | | number129 | | 10 | +-----------+ rows in set (. sec) #第二次隱式類型轉換出錯 mysql ::>>update aaa set name='abc0111' where name=; ERROR (): Truncated incorrect DOUBLE value: 'number129'
我們可以看到,當我們更新name='9'的時候,我們故意寫成了name=9,可以發現,執行成功了,但是當我們使用同樣的方法去更新name=10的記錄的時候,執行就會報錯,提示name='number129'的列發生了不正確的截斷,事實上,我們沒有更新這一行記錄。
從報錯資訊來看,mysql在處理隱式轉換的時候,是將表中的varchar數據轉換成整數來跟where條件進行匹配的,但是這樣似乎又解釋不通為什麼第一個update name=9的語法是正確的,因為name='t8'的列也需要進行轉換,但是沒有報錯。。。
這個細節還有待研究。。。