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 aaa\G
*************************** 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'的列也需要进行转换,但是没有报错。。。
这个细节还有待研究。。。