shardingJdbc3.x?版本的分頁(yè)bug問(wèn)題解析
引言
shardingJdbc 改名為 shardingsphere ,同時(shí)項(xiàng)目也已經(jīng)畢業(yè)并成為 Apache 頂級(jí)項(xiàng)目,但是這是發(fā)現(xiàn)它的第二個(gè)重大 BUG,說(shuō)明還是有很大的進(jìn)步空間。
上次發(fā)現(xiàn)的重大 BUG :http://chabaoo.cn/program/286235e92.htm
BUG 的表象是在使用 shardingJdbc3.1.0 進(jìn)行分頁(yè)查詢的時(shí)候存在問(wèn)題,一般情況下 sql 是這樣的格式 limit x,y
,但是不管怎么翻頁(yè)查詢,x 的值一直都是 0,這會(huì)導(dǎo)致查詢出來(lái)的結(jié)果,每一次翻頁(yè)的數(shù)據(jù)會(huì)越來(lái)越多,都包含了前一頁(yè)的數(shù)據(jù)。
首先,使用 shardingJdbc 的情況下,理論上就不應(yīng)該存在分頁(yè)查詢。 原因可以想象使用 shardingJdbc 是為了分表,既然是分表了那么這種查詢必然會(huì)通過(guò)條件查詢所有涉及到的表來(lái)得出結(jié)果,然后聚合到內(nèi)存來(lái)進(jìn)行分頁(yè),這會(huì)使得機(jī)器的壓力是巨大的。
所以如果不涉及到分表的情況下,不應(yīng)該是用 shardingJdbc,如果使用了分表就不應(yīng)該這樣搞分頁(yè)查詢。
前提條件是理想的情況下,但是在某些情況下,誤用了導(dǎo)致沒有分表策略的表使用了 shardingJdbc 來(lái)進(jìn)行操作也是有可能的。
shardingJdbc3.1.0 在處理 sql 中含有 limit x,y
這種操作的時(shí)候會(huì)進(jìn)行特殊處理。
類名: io.shardingsphere.core.routing.router.sharding.ParsingSQLRouter
格式化 SQL 和處理 limit 的操作
以下是截取的一段它的格式化 SQL 和處理 limit 的操作。
@Override public SQLRouteResult route(final String logicSQL, final List<Object> parameters, final SQLStatement sqlStatement) { ...... if (sqlStatement instanceof SelectStatement && null != ((SelectStatement) sqlStatement).getLimit()) { processLimit(parameters, (SelectStatement) sqlStatement); } ...... } private void processLimit(final List<Object> parameters, final SelectStatement selectStatement) { boolean isNeedFetchAll = (!selectStatement.getGroupByItems().isEmpty() || !selectStatement.getAggregationSelectItems().isEmpty()) && !selectStatement.isSameGroupByAndOrderByItems(); selectStatement.getLimit().processParameters(parameters, isNeedFetchAll, databaseType); }
其中 selectStatement.getLimit () 得到的 Limit 類的實(shí)現(xiàn)有點(diǎn)問(wèn)題,這個(gè)導(dǎo)致了問(wèn)題就很大了。
類名: io.shardingsphere.core.parsing.parser.context.limit.Limit
實(shí)現(xiàn)方法
以下是截取的一段它的實(shí)現(xiàn)方法
* @param parameters parameters * @param isFetchAll is fetch all data or not * @param databaseType database type */ public void processParameters(final List<Object> parameters, final boolean isFetchAll, final DatabaseType databaseType) { fill(parameters); rewrite(parameters, isFetchAll, databaseType); } private void fill(final List<Object> parameters) { int offset = 0; if (null != this.offset) { offset = -1 == this.offset.getIndex() ? getOffsetValue() : NumberUtil.roundHalfUp(parameters.get(this.offset.getIndex())); this.offset.setValue(offset); } int rowCount = 0; if (null != this.rowCount) { rowCount = -1 == this.rowCount.getIndex() ? getRowCountValue() : NumberUtil.roundHalfUp(parameters.get(this.rowCount.getIndex())); this.rowCount.setValue(rowCount); } if (offset < 0 || rowCount < 0) { throw new SQLParsingException("LIMIT offset and row count can not be a negative value."); } } private void rewrite(final List<Object> parameters, final boolean isFetchAll, final DatabaseType databaseType) { int rewriteOffset = 0; int rewriteRowCount; if (isFetchAll) { rewriteRowCount = Integer.MAX_VALUE; } else if (isNeedRewriteRowCount(databaseType)) { rewriteRowCount = null == rowCount ? -1 : getOffsetValue() + rowCount.getValue(); } else { rewriteRowCount = rowCount.getValue(); } if (null != offset && offset.getIndex() > -1) { parameters.set(offset.getIndex(), rewriteOffset); } if (null != rowCount && rowCount.getIndex() > -1) { parameters.set(rowCount.getIndex(), rewriteRowCount); } }
在對(duì) limit 語(yǔ)法進(jìn)行處理的時(shí)候,會(huì)重寫掉這個(gè) offset,使得 offset=0,這就導(dǎo)致了每次向后翻頁(yè)操作的數(shù)據(jù)會(huì)把前一次的分頁(yè)查詢結(jié)果也帶上,每向后翻一頁(yè)就會(huì)還是從 0 開始讀取數(shù)據(jù)。
所以,解決方案就是如果不是分表就不要去用 shardingJdbc,如果是分表就不要去分頁(yè)查詢,性能損耗太嚴(yán)重,畢竟它得聚合到內(nèi)存之后再進(jìn)行分頁(yè)處理,這個(gè)數(shù)據(jù)量過(guò)大的時(shí)候真的很容易出事。
如果一定要這么玩,那么看下 shardingJdbc 的新版本 4.x 是否修復(fù)了這個(gè) BUG,聽說(shuō)已經(jīng)修復(fù)了,沒去試過(guò)。
以上就是shardingJdbc3.x 版本的分頁(yè)問(wèn)題解析的詳細(xì)內(nèi)容,更多關(guān)于shardingJdbc版本分頁(yè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springboot自動(dòng)掃描包路徑來(lái)龍去脈示例詳解
這篇文章主要介紹了Springboot自動(dòng)掃描包路徑來(lái)龍去脈示例詳解,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Java實(shí)現(xiàn)登錄密碼強(qiáng)度校驗(yàn)的項(xiàng)目實(shí)踐
本文主要介紹了Java實(shí)現(xiàn)登錄密碼強(qiáng)度校驗(yàn)的項(xiàng)目實(shí)踐,包括使用正則表達(dá)式匹配校驗(yàn)和密碼強(qiáng)度校驗(yàn)工具類這兩種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01IDEA報(bào)錯(cuò):Process terminated的問(wèn)題及解決
這篇文章主要介紹了IDEA報(bào)錯(cuò):Process terminated的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11spring-boot中spring-boot-maven-plugin報(bào)紅錯(cuò)誤及解決
這篇文章主要介紹了spring-boot中spring-boot-maven-plugin報(bào)紅錯(cuò)誤及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03詳解java并發(fā)之重入鎖-ReentrantLock
這篇文章主要介紹了java并發(fā)之重入鎖-ReentrantLock,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03Java創(chuàng)建數(shù)組的幾種方式總結(jié)
下面小編就為大家?guī)?lái)一篇Java創(chuàng)建數(shù)組的幾種方式總結(jié)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10Springboot2以代碼的方式統(tǒng)一配置Jackson教程
這篇文章主要介紹了Springboot2以代碼的方式統(tǒng)一配置Jackson教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11Java11中基于嵌套關(guān)系的訪問(wèn)控制優(yōu)化詳解
Java(和其他語(yǔ)言)通過(guò)內(nèi)部類支持嵌套類,要使其正常工作,需要編譯器執(zhí)行一些技巧,下面這篇文章主要給大家介紹了關(guān)于Java11中基于嵌套關(guān)系的訪問(wèn)控制優(yōu)化的相關(guān)資料,需要的朋友可以參考下2022-01-01