软件系统研究文档
系统名称:MyCAT问题总结
Q群文件-MyCAT问题总结V1.2 - 图1

(仅供内部使用)














项目组:DBMW
时 间:2016-02-22


版本修订历史

日期 版本 说明/备注 作 者 审核人1 审核人2
2016.02.22 V1.0 章颖、赵闪
2018.01.29 V1.1 文中标红为补充内容 何清华























MyCAT常见问题总结

1 介绍

mycat是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。目前生产环境使用的版本主要为0.9、1.4以及1.6。
具体原理介绍及项目源码可查看GitHub WiKi:
https://github.com/MyCATApache/Mycat-Server/wiki

2 SQL限制

MyCAT作为公司使用的数据库中间件,由于其对一些SQL语句的解析存在一定的限制,而由于MyCAT1.0采用的解析器是fdbparser,而1.3之后的解析器统一改为druidparser,现将一些主要的DML语句在使用不同版本的基础上进行测试;
首先需要注意的一点是MyCAT1.0不支持大部分的DDL语句,因此在1.0版本上一般禁止使用DDL语句,一些建库和建表操作都是通过配置文件实现的,现在主要对一些DML语句的限制进行说明;
在MyCAT1.0版本大部分的简单DML语句是支持的,现对一些SELECT语句进行说明,因为很多对SELECT语句的限制其实也非常适用于INSERT、UPDATE和DELETE操作。
注意:不管是哪个版本的MYCAT,都存在一个限制,即分片字段不能更新。

2.1 DML语句的执行情况解析

2.1.1 DISTINCT操作

针对版本1.0,在单库下执行SELECT DISTINCT操作不会出现任何问题,但在多库情况下,这个操作将会出现问题,因为fdbparser对此操作不会进行解析,只是把此语句转发到每个具体的分库,这样就会导致一个非常严重的后果,每个库返回的DISTINCT结果都出现在了结果集中,导致MYCAT呈现的DISTINCT结果并不DISTINCT,因此在分库的情况下应避免含有此关键字的SQL语句,而在应用层进行拼接;

2.1.2 WHERE操作

(1)一般的where操作MyCAT都会支持,但带有分片字段的where会出现一定的问题,where后带有分片字段后,将会到该分片字段所在的库去执行相应的操作(MyCAT只对应单库的情况下没有任何影响),这就导致所有的查询结果其实都是在分片字段所在库的返回结果,因此类似在分片字段上执行“!=”、“>=”以及where后面带有“or”等操作将会导致返回的结果集并非真正的结果集,而只是一个单库的结果集,值得一提的是, where后带有普通字段的操作却不会出现任何问题。例如:SELECT * from t_day_flow WHERE mobile >=’1024’;返回的结果集则是‘1024’分片后所对应的结果;SELECT from t_day_flow WHERE useflow=10 or appid like ‘%21cn%’的结果则是正常的;(mobile为分片字段);
在MyCAT1.4版本中,类似SELECT
from t_day_flow WHERE mobile >=’1024’的bug问题得到了修复,但SELECT from t_day_flow WHERE mobile=’13632342036’ or appid like ‘%21cn%’ and useflow >10这种类型的问题还是会存在,指定了库就会进入到它所在的库进行操作,而or操作之后的也是在指定库中进行的。
(2)在MyCat1.4版本中,如果数据库实例中不存在某一字段,但在mycat中将该字段作为条件进行操作时,语句还能执行成功;造成的结果是mycat内存耗尽,最后被卡死。具体原因是所执行的sql得不到结果则一直执行下去,造成数据拥堵,当数据拥堵达到一定量时,内存被耗尽,mycat被卡死。具体看下面一个例子,select
from invoice_info where phoneNum=’111111111’ and email=’xjqang@163.com’,
因为invoice_info表中根本不存在字段 email,因此在wrapper.log 日志中报出内存用尽的错误:
Q群文件-MyCAT问题总结V1.2 - 图2
同时在mycat.log日志中报出错误:
WARN [$_NIOREACTOR-27-RW] (SingleNodeHandler.java:189) -execute sql err : errno:1054 Unknown column ‘email’ in ‘where clause’ con:MySQLConnection

2.1.3 limit m,n操作

分页查询实际上返回的结果集是m+n行的结果,而不是对m+n筛选后得到的n行结果;这也是fdbparser解析器的一个bug问题;

2.1.4 replace into操作

该语法存在矛盾性(该语句的含义是:数据库中不存在就insert,存在就replace,即更新。根据拆分表原则:insert 时必须带拆分字段,而更新时绝不能更新拆分字段,即绝不能带拆分字段,这两种逻辑是矛盾的),所以MyCAT禁止这类操作。

2.1.5 子查询语句

对于子查询,MyCAT是支持的,但在分表分库场景下,MyCAT并不能跨库查询,导致查询的结果实际上是每个单库里的查询结果的汇总,因此如果涉及到的子查询会在不同的分库里,有可能会导致最终的结果并不是准确的结果;如果能保证涉及的子查询不会进行跨库操作,或者只对应一个单库,这种子查询是完全没有问题的。

2.1.6 GROUP BY和ORDER BY操作

这类语句在MyCAT中是支持的,但涉及到多个分片库时,解析器会将SQL语句路由到每个分片库上去执行,最后返回的结果实际上是每个分库结果的汇总,同样不会进行跨库操作,不过ORDER BY的结果是正常的,GROUP BY的结果却不正常。

2.1.7 INSERT操作

(1)进行insert操作时,必须要带有分片字段,同时不支持进行批量插入,同时insert … on duplicate key update也是不支持的,因为对分片字段是无法进行更新的;同时对SELECT的一些条件限制也同样适用于insert;

2.1.8 UPDATE操作

主要是要注意对分片字段不能进行更新。

2.2 DML语句操作规范

2.2.1 MyCAT1.0版本下部分DML语句操作规范

单库 多库
SELECT DISTINCT … 允许 禁止
SELECT … where “分片字段”=‘’and … 允许 允许
SELECT … where “分片字段”=‘’or … 允许 禁止
SELECT … where “分片字段”!=‘’and … 允许 禁止
SELECT … where “分片字段”like‘’and … 允许 禁止
SELECT…from WHERE “分片字段” >=’1024’ 允许 禁止
SELECT…from WHERE “分片字段” in… 允许 允许
SELECT…from WHERE “分片字段” between…and… 允许 允许
SELECT…from WHERE “非分片字段” 条件判断 允许 允许
order by …limit m 允许 允许
order by …limit m,n 禁止 禁止
join查询 允许 禁止
SELECT CONCAT from… 允许 允许
分片字段更新 禁止 禁止
批量插入 禁止 禁止
insert into…on duplicate key 禁止 禁止
replace into 禁止 禁止

2.2.2 MyCAT1.4版本部分DML语句操作规范

相对MyCAT1.0来讲,MyCAT1.4采用了druidparser解析器,支持的SQL语句更多,但由于许多SQL语句进行了跨库操作,其得到的结果是不准确的,只相当于将单库查询的结果合并在了一起,这样的结果和跨库查询的结果是不同的,因此,在正式的生产环境中,应避免这些操作。

单库 多库
SELECT DISTINCT … 允许 禁止
SELECT … where “分片字段”=‘’and … 允许 允许
SELECT … where “分片字段”=‘’or … 允许 禁止
SELECT … where “分片字段”!=‘’and … 允许 允许
SELECT … where “分片字段”like‘’and … 允许 禁止
SELECT…from WHERE “分片字段” >=’1024’ 允许 允许
SELECT…from WHERE “分片字段” in… 允许 允许
SELECT…from WHERE “分片字段” between…and… 允许 允许
SELECT…from WHERE “非分片字段” 条件判断 允许 允许
order by …limit m 允许 允许
order by …limit m,n 允许 允许
join查询 允许 禁止
SELECT CONCAT from… 允许 允许
分片字段更新 禁止 禁止
批量插入 禁止 禁止
insert into…on duplicate key 禁止 禁止
replace into 禁止 禁止


值得注意的是,我们在1.4版本进行测试后发现,对于where和limit m,n进行测试后发现,最新的druidparser解析器时能避免1.0的一些bug的,对于GROUP BY实现了跨库查询结果的优化,但其优化并不彻底,所以类似GROUP BY操作最好避免。
因此,即使是最新的1.4版本也只是部分避免了一些DML语句的bug,但却没有实现跨库查询,只是将一些在单库中查询的结果进行了一定程度的优化,对复杂的SQL语句的支持还是不够,因此,在多个分库情况下时,应避免一些复杂SQL运行。

3 连接数控制

(1)对于后端数据库连接数控制主要由schema.xml文件中的dataHost中的maxCon参数控制,当前端连接数较多时,需要适当增大maxCon参数,同时注意,所有负载均衡的maxCon之和应小于mysql设置的max-connection参数。
(2)0.9版本如果发生过多的idle timeout空闲超时连接(可以通过统计日志,命令为:cat /data/logs/mycat/mycat.log |grep ‘idle timeout’|wc –l的超时总数来判断),如:单台mycat超过400个空闲连接被回收,说明mycat前端连接过多,超负载的情况下可能会导致某些表的逻辑丢失(非物理删除),应用层无法查询而报错,此时需要进行reload @@config;热切更新配置或者重启mycat方能恢复表正常配置的数量。
Q群文件-MyCAT问题总结V1.2 - 图3

4 对事务的支持

MyCAT中的事务分为两种情况:
(1) SQL不跨分片:事务中的SQL在单个节点执行;
(2) SQL跨分片:事务中的SQL在多个节点执行;
其中,在单个datanode上执行的事务和标准的数据库事务模式相同,要么提交要么回滚,不存在中间检查点的操作;而对于跨多个datanode的事务,MyCAT执行一种弱XA事务模式,即首先事务内的SQL会在各自的分片上执行并返回状态码,若某个分片上的返回码是error,则MyCAT认为事务失败,事务回滚,MyCAT收到回滚指令后,依次回滚事务中涉及的所有分片;若事务中的所有SQL的执行都返回成功的返回码,则应用程序提交事务,MyCAT会同时向事务中涉及的节点发送提交事务的指令。
Q群文件-MyCAT问题总结V1.2 - 图4
如图所示,这是在单个节点执行事务操作时的结果;通过set autocommit=0;开启事务,值得注意的是,我们即使开启了事务,但show variables like ‘%autocommit%’;得到的值确和我们开启事务不相符(这时因为show variables操作默认都会去分主1执行的缘故);但这些都没有影响我们的操作,事务最后能够成功提交,而且,在事务未提交阶段,事务还能保证隔离性操作;
Q群文件-MyCAT问题总结V1.2 - 图5
如上图所示,当对多个节点进行操作时,只要有一个节点commit失败,这个事务中其他节点也会失败,最终实现整个事务的回滚;
只有当所有节点都提交成功时,跨节点事务才实现提交成功,这就是弱XA的原理。
值得注意的是MyCAT1.3版本之前都不支持begin操作,1.4之后开始支持begin transaction操作。
XA事务成功的过程如下
Q群文件-MyCAT问题总结V1.2 - 图6
Xa事务的完成分两个阶段一是prepare阶段,另一个是commit阶段,只有两个阶段都成功了,xa事务才能成功;
Q群文件-MyCAT问题总结V1.2 - 图7
而对于XA事务失败的情况,则是指多个资源管理器至少有一个没有prepare,这时,会导致其他节点rollback,从而导致整个事务失败;

5 读写分离

由schema.xml中的balance参数控制
0(BALANCE_NONE):不开启读写分离机制。
1(BALANCE_ALL_BACK):全部的readHost与非当前写writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与 M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。
2(BALANCE_ALL):所有的readHost与writeHost都参与select语句的负载均衡,也就是说,当系统的写操作压力不大的情况下,所有主机都可以承担负载均衡
3(BALANCE_ALL_READ):全部的readHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与 M2互为主备),正常情况下,只有S1,S2都参与select语句的负载均衡。

Q群文件-MyCAT问题总结V1.2 - 图8
上述配置情况下,当写挂了,读不可用
Q群文件-MyCAT问题总结V1.2 - 图9
Q群文件-MyCAT问题总结V1.2 - 图10
上述配置情况下,当写挂了,可以继续使用。

MyCAT1.0 MyCAT1.4
是否支持读写分离
Balance的值 0,1,2 0,1,2,3
是否支持主从延时监控 ×

表1:Mycat1.0与Mycat1.4读写分离功能对比情况


判断服务器是否宕机是通过mycat的心跳检测机制实现的,如果心跳检测3次失败,则判定服务不可用,心跳检测间隔时间可以在server.xml文件中设置:
5000
单位:毫秒

6 在线重新加载配置文件

(1)对于0.9低版本的cobar,重启方式为先kill相关进程,再执行./ startup.sh,并注意删除bin目录下生成的异常中断说明文件hs_err_pid13212.log,core.9666等。
(2)Mycat1.0使用管理端口的reload @@config功能会断开后端数据库连接,而且该功能不稳定,会出现reload不生效的情况。
(3)Mycat1.4使用管理端口的reload @@config可以reload基本配置,如果需要reload所有配置,需要使用reload @@config_all命令,该功能还是会断开后端数据库连接,使用时会影响线上正在连接的服务。
因此,对于在线服务,不推荐使用reload @@config功能;当上一层还有haproxy服务时,在mycat进行重启前,可以先进性热切换,先将需要重启的mycat注释掉,再对mycat进行重启,检查无误后,再在haproxy配置中取消注释。

7 日志

(1)org.apache.log4j.DailyRollingFileAppender采用这种每天归档日志的形式时,修改log4j.xml中的日志配置文件不会即时生效
(2)org.apache.log4j.ConsoleAppender采用这种归档日志方式,修改log4j.xml中的日志配置文件可以即时生效,修改log4j.xml时,console会load这个./conf/log4j.xml文件
mycat1.0的日志文件有console.log和mycat.log
mycat1.4的日志文件有wrapper.log和mycat.log

8 字符集问题

1.4版本如果存在非utf8字符集连接如:latin1,按debug日志的经验,当连接复用时,可能会发生字符集的继承,即上一个连接为latin1字符集,当下一次写入复用此连接时,原utf8的字符集会被篡改为latin1,若此时存在中文,会导致写入的乱码。
Q群文件-MyCAT问题总结V1.2 - 图11