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