Mysql自动超时重连导致的中文乱码问题
今天有客户反应从自选股服务器获取的自定义板块中文名称乱码,之前一直都是正常的。看到乱码两字,头脑中首先冒出来的就是查看mysql数据库中的编码集,输入SHOW VARIABLES LIKE ‘char%’;命令查看编码全部都是utf编码,没有问题。
再查看服务器日志:
服务器运行过程中没有进行重启,也没有错误日志,但是日志中显示有长达八个多小时(日志打印的是0时区的,把这个时间加上8小时就是我们的北京时间)没有进行过数据流的交互,因为我们是股票行情软件,在凌晨到早上9点开市前基本不会有活跃的用户,看到这,立马想到了mysql的自动超时重连导致之前的编码设置无效。
使用mysql C API时,由于数据库中的编码都是utf8的,因此我们也需要在调用api时指定连接字符集,而这个是通过SET NAMES来实现的。由于我们的服务器连接数据库时通过mysql_options指定了MYSQL_OPT_RECONNECT超时重连选项,在每次执行sql语句之前先执行一次mysql_ping,来自动检查重连。当开启了自动重连,连接断开时,会尝试重新连接数据库,重新连接虽然好用,但是也会存在以下问题:
- 任何活动的交易都被回滚,autocommit模式被置为reset。
- 所有表锁都释放
- 所有临时表是关闭(撤消)
- Session variables are reinitialized to the values of the corresponding variables.会话变量被重新初始化为相应的变量。 这也影响那些隐式声明的变量,比如使用SET NAMES。This also affects variables that are set implicitly by statements such as SET NAMES.
- 用户变量设置都将丢失。
- 编制报表释放。
- 句柄变量被关闭。
- LAST_INSERT_ID()被重置为0 。
- 使用GET_LOCK()获得的锁被释放
第四点和第五点可以一起理解,即当mysql的连接进行自动重连时,之前针对一个连接进行的用户设置都将失效,比如使用SET NAMES。而我们服务器恰好是通过mysql_query(pSqlCon, “SET NAMES UTF8”);实现的。
那如何解决重连之后的编码设置问题呢?mysql c api还提供了另一种方式:MYSQL_SET_CHARSET_NAM。初始化数据库句柄后马上用mysql_options设定MYSQL_SET_CHARSET_NAME属性为utf8,这样就不用显式地用 SET NAMES语句指定连接字符集,且用mysql_ping重连断开的长连接时也会把连接字符集重置为utf8。代码如下:
问题解决了,虽然是一个较常见的坑,但还是被自己踩到了。针对mysql的自动重连和mysql的字符集做以下几点总结:
1, my.cnf中的default_character_set设置只影响mysql命令连接服务器时的连接字符集,不会对使用libmysqlclient库的应用程序产生任何作用,因此我们在调用MySQL c api时还需要指定连接字符集。
2, mysql的默认连接超时时间是28800s,也就是八小时,通过命令show variables like ‘%timeout%’;可以查看wait_timeout字段。也可以通过修改/etc/my.cnf中的wait_timeout值来解决这个问题,但是不能根治。
3, mysql默认编码是latin1,当数据库和连接字符集都使用latin1时,大部分情况下都可以解决乱码问题,但是却无法以字符为单位来进行SQL操作,一般情况下将数据库和连接字符集都置为utf8是较好的选择
4, 通过设置MYSQL_OPT_RECONNECT开启mysql的自动重连功能后,针对mysql的相关变量尽可能都使用mysql_options结合mysql_option枚举进行设置,避免重连后的设置无效。