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:           [email protected]  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'的列也需要进行转换,但是没有报错。。。

这个细节还有待研究。。。