亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

SpringBoot使用 druid 連接池來(lái)優(yōu)化分頁(yè)語(yǔ)句

瀏覽:7日期:2023-04-11 13:08:36

一、前言

一個(gè)老系統(tǒng)隨著數(shù)據(jù)量越來(lái)越大,我們察覺(jué)到部分分頁(yè)語(yǔ)句拖慢了我們的速度。

鑒于老系統(tǒng)的使用方式,不打算使用pagehelper和mybatis-plus來(lái)處理,加上系統(tǒng)里使用得是druid連接池,考慮直接使用druid來(lái)優(yōu)化。

二、老代碼

老代碼是使用得一個(gè)mybatis插件進(jìn)行的分頁(yè),分頁(yè)的核心代碼如下:

// 記錄統(tǒng)計(jì)的 sqlString countSql = 'select count(0) from (' + sql+ ') tmp_count';PreparedStatement countStmt = connection.prepareStatement(countSql);BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), parameterObject);setParameters(countStmt, mappedStatement, countBS,parameterObject);

在原始的 sql 外面包裝了一個(gè) count sql,當(dāng)然很多插件都是這樣做的。

三、druid 的 PagerUtil

示例 sql(有比較復(fù)雜的坐標(biāo)計(jì)算)

SELECT g.* , ROUND(6378.138 * 2 * ASIN(SQRT(POW(SIN((? * PI() / 180 - t.latitude * PI() / 180) / 2), 2) + COS(? * PI() / 180) * COS(t.latitude * PI() / 180) * POW(SIN((? * PI() / 180 - t.longitude * PI() / 180) / 2), 2))), 2) AS distancecd , t.agentname, t.agentlogo, t.compaddressFROM t_bas_integral_goods g LEFT JOIN t_bas_agent t ON g.agentid = t.AGENTIDWHERE t.AGENTTYPE = ’2’ AND t.pass = ’0’ AND t.dl_type = ’4’ AND g.type = 0ORDER BY distancecd ASC

使用 Druid 生成 count sql:

String countSql = PagerUtils.count(sql, DbType.mysql);System.out.println(countSql);

輸出:

SELECT COUNT(*)FROM t_bas_integral_goods g LEFT JOIN t_bas_agent t ON g.agentid = t.AGENTIDWHERE t.AGENTTYPE = ’2’ AND t.pass = ’0’ AND t.dl_type = ’4’ AND g.type = 0

我們可以看到優(yōu)化后的 count sql 變得十分簡(jiǎn)潔,坐標(biāo)計(jì)算的都已經(jīng)丟棄掉。 注意:PagerUtil還有l(wèi)imit方法用來(lái)生成limit語(yǔ)句,感興趣的同學(xué)可以自行試驗(yàn)。

四、改造mybatis分頁(yè)插件

4.1 踩坑之路

看到上面 druid PagerUtils count 的優(yōu)化效果,立馬開(kāi)始改造起來(lái),起初只改掉了countSql,

String countSql = PagerUtils.count(sql, dbType);PreparedStatement countStmt = connection.prepareStatement(countSql);BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), parameterObject);setParameters(countStmt, mappedStatement, countBS,parameterObject);

啟動(dòng)起來(lái)測(cè)試一番就發(fā)現(xiàn)報(bào)錯(cuò)了,因?yàn)樵?sql 中含有?變量,優(yōu)化后的 sql 已經(jīng)沒(méi)有變量了,插件還會(huì)繼續(xù)給他設(shè)置變量。 我們要怎么解決這個(gè)問(wèn)題呢?

我們?cè)倩仡^看看pagehelper和mybatis-plus是怎么實(shí)現(xiàn)的!它倆都是基于jsqlparser對(duì) sql 進(jìn)行解析,然后處理。

要多加一個(gè)jsqlparser?沒(méi)必要沒(méi)必要,druid 的 sql 解析功能也是很強(qiáng)大的,我看了看PagerUtils.count方法的源碼,大不了用 druid 的 sql 解析實(shí)現(xiàn)一遍。

看了看源碼之后我陷入了沉思,有必要搞這么復(fù)雜么?有沒(méi)有更好的方法?我反復(fù) debug 發(fā)現(xiàn)了,DynamicSqlSource中有帶#{xxx}這樣的原始 sql,

那么我是否可以使用 druid 先對(duì)這種 mybatis 占位符的 sql 進(jìn)行優(yōu)化呢?我們來(lái)試試:

示例 sql:

select * from xxx where type = #{type} order by xx

輸出:

SELECT COUNT(*)FROM xxxWHERE type = #{type}

完美!!! 4.2 繼續(xù)踩坑

然而直接在 Mapper 上注解的 sql 還是有問(wèn)題,拿不到原始的 sql,debug 發(fā)現(xiàn) RawSqlSource 在構(gòu)造器里就將 sql 處理成了?號(hào)掛參的形式。

@Select('select * from xxx where type = #{type} order by xx')Object test(@Param('type') String type);

那么我只能看看能不能擴(kuò)展它,我找到了它是在XMLLanguageDriver里進(jìn)行初始化,這下好辦了,因?yàn)槲抑皵U(kuò)展過(guò)XMLLanguageDriver,它是可以自定義配置的。 于是我重寫了RawSqlSource, 添加上了包含 mybatis 參數(shù)占位符(#{})的rawSql字段。

/** * 原始 sql,用于方便 druid 工具進(jìn)行分頁(yè) * * @author L.cm */public class MicaRawSqlSource implements SqlSource { private final String rawSql; private final SqlSource sqlSource; public MicaRawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) { this(configuration, getSql(configuration, rootSqlNode), parameterType); } public MicaRawSqlSource(Configuration configuration, String sql, Class<?> parameterType) { SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> clazz = parameterType == null ? Object.class : parameterType; this.rawSql = sql; this.sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>()); } // ... ...}

自此全部邏輯已經(jīng)走通,我們?cè)賮?lái)看看我們的PagePlugin核心代碼:

// 進(jìn)行分頁(yè)Configuration configuration = mappedStatement.getConfiguration();SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);Class<?> parameterType = parameterObject.getClass();Connection connection = (Connection) invocation.getArgs()[0];// 1. 對(duì) sql 進(jìn)行判斷,如果沒(méi)有 ? 號(hào),則直接處理String boundRawSql = boundSql.getSql();if (boundRawSql.indexOf(CharPool.QUESTION_MARK) == -1) { // 不包含 ? 號(hào) String countSql = PagerUtils.count(boundRawSql, dbType); SqlSource newSqlSource = sqlSourceParser.parse(countSql, parameterType, new HashMap<>()); BoundSql newBoundSql = newSqlSource.getBoundSql(parameterObject); int count = getCount(connection, mappedStatement, parameterObject, newBoundSql); StringBuilder sqlBuilder = new StringBuilder(boundRawSql); Page page = getPageParam(parameterObject, sqlBuilder, count); String pageSql = generatePageSql(sqlBuilder.toString(), dbType, page); // 將分頁(yè)sql語(yǔ)句反射回BoundSql. setField(boundSql, 'sql', pageSql); return invocation.proceed();}// 2. 按 SqlSource 進(jìn)行解析SqlSource sqlSource = mappedStatement.getSqlSource();// xml 中的動(dòng)態(tài) sqlint count;if (sqlSource instanceof DynamicSqlSource) { SqlNode rootSqlNode = PagePlugin.getField(sqlSource, 'rootSqlNode'); DynamicContext context = new DynamicContext(configuration, parameterObject); rootSqlNode.apply(context); // 生成 count sql,帶 #{xxx} 變量的 sql String countSql = PagerUtils.count(context.getSql(), dbType); SqlSource newSqlSource = sqlSourceParser.parse(countSql, parameterType, context.getBindings()); BoundSql newBoundSql = newSqlSource.getBoundSql(parameterObject); count = getCount(connection, mappedStatement, parameterObject, newBoundSql);} else if (sqlSource instanceof MicaRawSqlSource) { String rawSql = ((MicaRawSqlSource) sqlSource).getRawSql(); DynamicContext context = new DynamicContext(configuration, parameterObject); // 生成 count sql,帶 #{xxx} 變量的 sql String countSql = PagerUtils.count(rawSql, dbType); SqlSource newSqlSource = sqlSourceParser.parse(countSql, parameterType, context.getBindings()); BoundSql newBoundSql = newSqlSource.getBoundSql(parameterObject); count = getCount(connection, mappedStatement, parameterObject, newBoundSql);} else { throw new IllegalArgumentException('不支持的 sql 分頁(yè)形式,請(qǐng)使用 xml 或者注解');}

五、結(jié)論

整個(gè)老服務(wù)通過(guò)切換到 mica(深度定制)的微服務(wù)架構(gòu)(演示環(huán)境僅僅在單服務(wù)低內(nèi)存配置)之后速度提升效果明顯,當(dāng)然后面我們還會(huì)繼續(xù)進(jìn)行優(yōu)化。

到此這篇關(guān)于SpringBoot使用 druid 連接池來(lái)優(yōu)化分頁(yè)語(yǔ)句的文章就介紹到這了,更多相關(guān)SpringBoot druid 連接池分頁(yè)內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 色综合久久综合中文小说 | 免费播放欧美毛片欧美a | 国产精品久久久久亚洲 | 首页亚洲国产丝袜长腿综合 | 亚洲人交性视频 | 亚洲在线视频免费 | 欧美在线一区二区三区欧美 | a级黄色影院| 制服丝袜视频在线 | 欧美一区二区三区黄色 | 中文字幕在线观看不卡 | 一级特黄录像绵费播放 | 国产精品拍拍拍福利在线观看 | 亚洲国产成人精品91久久久 | 国产精品极品美女自在线看免费一区二区 | 性色aⅴ闺蜜一区二区三区 性色a按摩videos | 二区在线视频 | 成人国产精品高清在线观看 | 欧美小younv 欧美性xxxxx极品老少 | 欧美黄色免费网址 | 屁屁影院一区二区三区 | 国产精品吹潮在线播放 | 免费麻豆视频 | 午夜色大片在线观看 | 国产综合色在线视频区色吧图片 | 国产偷国产偷亚洲高清在线 | 任我爽在线视频精品凹凸精品分类 | 国产白嫩美女在线观看 | 国产精品亚洲精品观看不卡 | 免费一级大毛片a一观看不卡 | 国产亚洲人成网站观看 | 欧美经典成人在观看线视频 | 欧美日韩国产在线观看 | 黄色黄站 | 国产黄a三级三级三级 | 久久精品成人国产午夜 | 欧美日韩一区二区在线视频 | 国产高清在线a视频大全凹凸 | 特级淫片国产免费高清视频 | 亚洲一区二区精品推荐 | 丁香婷婷在线观看 |