17. SQL重写为limit Integer.MAX_VALUE的无奈

本文探讨了在使用sharding-jdbc时,为何需要将SQL的limit语句重写为Integer.MAX_VALUE,通过构造数据和执行SQL的例子,展示了不分库分表和分库分表情况下的差异。分析指出,如果不进行重写,group by等操作可能导致结果错误。最后,文章指出在分片存储数据的情况下,保证CRUD操作只路由到单一分片的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

阿飞Javaer,转载请注明原创出处,谢谢!!

这篇文章源于sharding-jdbc源码分析之重写的遗留问题,相关sharding-jdbc源码如下:

private void appendLimitRowCount(final SQLBuilder sqlBuilder, final RowCountToken rowCountToken, final int count, final List<SQLToken> sqlTokens, final boolean isRewrite) {
    SelectStatement selectStatement = (SelectStatement) sqlStatement;
    Limit limit = selectStatement.getLimit();
    if (!isRewrite) {
        ... ...
    } else if ((!selectStatement.getGroupByItems().isEmpty() || !selectStatement.getAggregationSelectItems().isEmpty()) && !selectStatement.isSameGroupByAndOrderByItems()) {
        // 如果要重写sql中的limit的话,且sql中有group by或者有group by & order by,例如"select user_id, sum(score) from t_order group by user_id order by sum(score) desc limit 5",那么limit 5需要重写为limit Integer.MAX_VALUE,原因接下来分析
        sqlBuilder.appendLiterals(String.valueOf(Integer.MAX_VALUE));
    } else {
        ... ...
    }
    ... ...
}

构造数据

为了解释为什么limit rowCount中的rowCount需要重写为Integer.MAX_VALUE,需要先构造一些数据,如下图所示:
t_order_0 & t_order_1两个分表中的数据

如果不分库分表的话,数据如下图所示:
t_order表中的数据

执行SQL

假定执行如下SQL:

select user_id, sum(score) from t_order group by user_id order by sum(score) desc limit 5;

结果如下所示:

user_idsum(score)
2036
2132
1030
1125
1420

假定select user_id, sum(score) from t_order group by user_id order by sum(score) desc limit 5;这个SQL不重写为limit 0, Integer.MAX_VALUE,那么t_order_0t_order_1的结果分别如下;
t_order_0的结果:

user_idsum(score)
1520
1618
2018
2116
1714

t_order_1的结果:

user_idsum(score)
1030
1125
1220
1320
1420

路由到两个表的执行结果归并后的结果如下:

user_idsum(score)
1030
1125
1520
1220
1320

分析

根据执行结果可知,主要差异在于,真实结果有user_id为20,21的数据。我们在看一下t_order_0t_order_1两个分表中这两个user_id的数据有什么特殊之处:
image.png

t_order_1这个分表中,由于user_id为20,21的score值在TOP 5以外。但是合并t_order_0t_order_1两个分表的结果,user_id为20的sum(score)能够排在第一(18+18=36);所以,如果group by这类的SQL不重写为limit 0, Integer.MAX_VALUE的话,会导致结果有误。所以sharding-jdbc的源码必须要这样重写,没有其他办法!

延伸

事实上不只是sharding-jdbc,任何有sharding概念的中间件例如ElasticSearch,都要这么处理,因为sharding后数据处理的流程几乎都要经过解析->重写->路由->执行->结果归并这几个阶段;所以,当我们的数据是以分片的方式存储时(分库分表,ElasticSearch等),尽量保证CRUD只路由到一个分片上。最起码要保证那些高并发低延迟的CRUD只路由到一个分片上,所以,sharding column的选取非常非常重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值