• 解决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 SearchSql intercept(SearchSql searchSql, Map 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 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引擎

  • 相关阅读:
    MySQL如何高效实现刷脏页,了解原理并学会配置
    java8新特性,Lambda 表达式
    前端人员不要只知道KFC,你应该了解 BFC、IFC、GFC 和 FFC
    Ubuntu-Docker-STF(devicefarmer)环境搭建 (android 12)
    使用docker搭建mongodb
    Spring Boot的启动流程
    pycharm安装第三方库
    ctf中ping命令执行绕过
    MySQL 定时备份的几种方式(非常全面)
    wpf 定义ContextMenu样式
  • 原文地址:https://blog.csdn.net/wenxingchen/article/details/126540876