1. 程式人生 > >17. SQL重寫為limit Integer.MAX_VALUE的無奈

17. SQL重寫為limit Integer.MAX_VALUE的無奈

阿飛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_id sum(score)
20 36
21 32
10 30
11 25
14 20

假定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_id sum(score)
15 20
16 18
20 18
21 16
17 14

t_order_1的結果:

user_id sum(score)
10 30
11 25
12 20
13 20
14 20

路由到兩個表的執行結果歸併後的結果如下:

user_id sum(score)
10 30
11 25
15 20
12 20
13 20

分析

根據執行結果可知,主要差異在於,真實結果有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的選取非常非常重要。