当前位置:   article > 正文

解决mysql深度分页问题

mysql深度分页问题

目录

1.基本分页:耗时0.019秒

2.深度分页:耗时10.236秒

3.深度ID分页:耗时0.052秒

4.两步走深度分页:耗时0.049秒+0.017秒

5.一步走深度分页:耗时0.05秒

6.集成BeanSearcher框架

7.万能优化技巧:索引。​编辑

8. 如果对数据插入的要求很高,比如每秒插入十多条数据,还是每条逐个插入,时间久了必然数据量非常大。这里还有一个方法可以试一试,那就是牺牲事务而获取性能,对不重要的数据可以这么做。将InnoDB引擎改为MyISAM引擎。


数据:单表数据25万条。

1.基本分页:耗时0.019秒

select * from cf_qb_info limit 0,20

2.深度分页:耗时10.236秒

select * from cf_qb_info limit 200000,20

3.深度ID分页:耗时0.052秒

提示:如果这一步很慢,count(1) 查询总数应该也会很慢-解决方式:请为主键加上unique索引。 

  1. -- 主键ID字段:NUMID
  2. select NUMID from cf_qb_info limit 200000,20

4.两步走深度分页:耗时0.049秒+0.017秒

基于第三步的缺陷(只能查出ID信息),我们可以先查出分页数据的ID,在根据ID查询数据。

select NUMID from cf_qb_info LIMIT 200000,20
  1. select * from cf_qb_info where NUMID in (
  2. '330681650000202108180227345510',
  3. '330681650000202108171031534500',
  4. '330681650000202108190251532141',
  5. '330681650000202108200246376830',
  6. '330681650000202108210229398665',
  7. '330681650000202108220236113895',
  8. '330681650000202108230230034133',
  9. '330681650000202108231017279739',
  10. '330681650000202108231043456276',
  11. '330681650000202108231051404340',
  12. '330681650000202108240237397251',
  13. '330681650000202108250221489228',
  14. '330681650000202108250241536726',
  15. '330681650000202108260253039326',
  16. '330681650000202108270216016138',
  17. '330681650000202108280234013754',
  18. '330681650000202108290230029720',
  19. '330681650000202108300255579204',
  20. '330681650000202108310234184991',
  21. '330681650000202109010237315937'
  22. );

两步合成一步SQL耗时:11.9秒;这一步着实出乎了我的意料。

  1. select * from cf_qb_info where NUMID in (
  2. select NUMID from (select NUMID from cf_qb_info LIMIT 200000,20) as t
  3. );

 鉴于这个结果:我们可以在程序里分成两步进行分页查询

5.一步走深度分页:耗时0.05秒

这一步是对第四步的优化,毕竟两条SQL还需要码代码。利用join 两条SQL合成一条。

  1. SELECT
  2. *
  3. FROM
  4. cf_qb_info a
  5. JOIN ( SELECT NUMID FROM cf_qb_info LIMIT 200000, 20 ) b ON a.NUMID = b.NUMID

6.集成BeanSearcher框架

原理是使用了BeanSearcher的sql拦截器对SQL进行拦截改造。https://bs.zhxu.cn/guide/latest/advance.html#sql-%E6%8B%A6%E6%88%AA%E5%99%A8https://bs.zhxu.cn/guide/latest/advance.html#sql-%E6%8B%A6%E6%88%AA%E5%99%A8

 ①改造Bean

②注入Sql拦截器

  1. package com.ciih.qbbs.config;
  2. import cn.hutool.core.util.ReUtil;
  3. import cn.hutool.core.util.StrUtil;
  4. import com.baomidou.mybatisplus.annotation.TableId;
  5. import com.ejlchina.searcher.SearchSql;
  6. import com.ejlchina.searcher.SqlInterceptor;
  7. import com.ejlchina.searcher.SqlSnippet;
  8. import com.ejlchina.searcher.param.FetchType;
  9. import org.springframework.stereotype.Component;
  10. import java.lang.reflect.Field;
  11. import java.util.List;
  12. import java.util.Map;
  13. /**
  14. * BeanSearcher的Sql拦截器:优化深度分页
  15. *
  16. * @author sunziwen
  17. */
  18. @Component
  19. public class SqlInterceptorImpl implements SqlInterceptor {
  20. @Override
  21. public <T> SearchSql<T> intercept(SearchSql<T> searchSql, Map<String, Object> paraMap, FetchType fetchType) {
  22. /**
  23. * 改造思路
  24. *
  25. * <>
  26. * 前:SELECT * FROM table1 t1 LIMIT 200000,20;
  27. * 后:SELECT * FROM table1 t1 JOIN ( SELECT id FROM table1 LIMIT 200000, 20 ) t99 ON t1.id = t99.id;
  28. * </>
  29. */
  30. Field[] fields = searchSql.getBeanMeta().getBeanClass().getDeclaredFields();
  31. String primaryColumnName = null;
  32. for (Field field : fields) {
  33. //这里使用了mybatis_plus的注解作为主键标识
  34. TableId tableId = field.getAnnotation(TableId.class);
  35. if (tableId != null) {
  36. if (!"".equals(tableId.value())) {
  37. primaryColumnName = tableId.value();
  38. } else {
  39. //驼峰转下划线
  40. primaryColumnName = StrUtil.toUnderlineCase(field.getName());
  41. }
  42. }
  43. }
  44. //如果没有主键标识,则不能进行SQL优化。
  45. if (primaryColumnName == null) {
  46. return searchSql;
  47. }
  48. //正则表达式获取where之后语句
  49. List<String> limits = ReUtil.findAll("where[\\s\\S]*limit[ ]+[?]{1}[ ]*,[ ]+[?]{1}", searchSql.getListSqlString(), 0);
  50. //如果不分页,则不进行SQL优化,即语句中没有limit关键字不优化。
  51. if (limits.size() == 0) {
  52. return searchSql;
  53. }
  54. //表名小片段
  55. SqlSnippet tableSnippet = searchSql.getBeanMeta().getTableSnippet();
  56. //合成子查询SQL
  57. String inSql = "JOIN ( SELECT " + primaryColumnName + " FROM " + tableSnippet.getSql() + " " + limits.get(0) + " ) t99 ON t1." + primaryColumnName + " = t99." + primaryColumnName + ";";
  58. //合成整条SQL
  59. String replace = searchSql.getListSqlString().replace(limits.get(0), inSql);
  60. //替换
  61. searchSql.setListSqlString(replace);
  62. return searchSql;
  63. }
  64. }

7.万能优化技巧:索引。

8. 如果对数据插入的要求很高,比如每秒插入十多条数据,还是每条逐个插入,时间久了必然数据量非常大。这里还有一个方法可以试一试,那就是牺牲事务而获取性能,对不重要的数据可以这么做。将InnoDB引擎改为MyISAM引擎

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/151818
推荐阅读
相关标签
  

闽ICP备14008679号