­

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'的列也需要進行轉換,但是沒有報錯。。。

這個細節還有待研究。。。