问题描述:
mycat环境: 使用mycat分片,后面挂两个分片shard01 和 shard02
# 分别在分片上创建测试表
create table wjf_test_mycat_orderby (
id int not null auto_increment,
name varchar(10) comment '名字',
amt int comment '数量',
primary key (id)
);
# 在 shard01 上执行插入
insert into wjf_test_mycat_orderby values (null,'w',1),(null,'w',1),(null,'w',1),(null,'w',1),(null,'j',1),(null,'j',1);
# 在 shard02 上执行插入
insert into wjf_test_mycat_orderby values (null,'w',1),(null,'w',1),(null,'j',1),(null,'j',1),(null,'j',1),(null,'j',1);
# 连接mycat,执行以下分组排序语句进行测试
select name,sum(amt) sum_amt from wjf_test_mycat_orderby group by name order by sum_amt desc limit 1;
如果不存在分片的情况下,name 为 'w’的sum数据为6,name 为 ‘j’ 的sum 数据也为6.
测试结果:
guser@99.48.210.224@99.48.210.224 [wjf]>select name,sum(amt) sum_amt from wjf_test_mycat_orderby group by name order by sum_amt desc limit 1;
+------+---------+
| name | sum_amt |
+------+---------+
| w | 4 |
+------+---------+
1 row in set (0.01 sec)
guser@99.48.210.224@99.48.210.224 [wjf]>select name,sum(amt) sum_amt from wjf_test_mycat_orderby group by name ;
+------+---------+
| name | sum_amt |
+------+---------+
| j | 6 |
| w | 6 |
+------+---------+
2 rows in set (0.01 sec)
测试结论:
对于分组查询limit的语句,其name 为 w的sum结果为4,是错误的。
问题分析
查看mycat路由分发策略:
2020-11-04 16:40:07.922 DEBUG [$_NIOREACTOR-5-RW] (io.mycat.cache.impl.EnchachePool.get(EnchachePool.java:71)) - SQLRouteCache hit cache ,key:wjfselect name,sum(amt) sum_amt from wjf_test_mycat_orderby group by name order by sum_amt desc limit 1
2020-11-04 16:40:07.922 DEBUG [$_NIOREACTOR-5-RW] (io.mycat.server.NonBlockingSession.execute(NonBlockingSession.java:126)) - ServerConnection [id=1, schema=wjf, host=99.48.210.224, user=guser,txIsolation=2, autocommit=true, schema=wjf, executeSql=select name,sum(amt) sum_amt from wjf_test_mycat_orderby group by name order by sum_amt desc limit 1]select name,sum(amt) sum_amt from wjf_test_mycat_orderby group by name order by sum_amt desc limit 1, route={
1 -> wjf_1{SELECT name, SUM(amt) AS sum_amt
FROM wjf_test_mycat_orderby
GROUP BY name
ORDER BY sum_amt DESC
LIMIT 1}
2 -> wjf_2{SELECT name, SUM(amt) AS sum_amt
FROM wjf_test_mycat_orderby
GROUP BY name
ORDER BY sum_amt DESC
LIMIT 1}
3 -> wjf_3{SELECT name, SUM(amt) AS sum_amt
FROM wjf_test_mycat_orderby
GROUP BY name
ORDER BY sum_amt DESC
LIMIT 1}
4 -> wjf_4{SELECT name, SUM(amt) AS sum_amt
FROM wjf_test_mycat_orderby
GROUP BY name
ORDER BY sum_amt DESC
LIMIT 1}
} rrs
mycat分发到mysql的语句是带着limit语句的,这样,如果name = ‘w’ 在shard01上排序后是满足条件的,可以查出来。
但是在shard02上排序后不满足条件,没有返回到mycat。
则在mycat层做聚合的时候,缺失了分片2的数据,导致结果不正确。
建议:
在分片库上做分组排序取topN数据的时候,sql语句做全量分组排序,然后在代码层取topN,这样取得数据才能使正确的。
但是全量分组排序,不论在mysql层、mycat层还是在代码层,都会有更多的性能损耗,这点需要考量。