当需要从数据库查询的表有上万条记录的时候,一次性查询所有结果会变得很慢,特别是随着数据量的增加特别明显,这时需要使用分页查询。对于数据库分页查询,也有很多种方法和优化的点。下面简单说一下我知道的一些方法。

准备工作

为了对下面列举的一些优化进行测试,下面针对已有的一张表进行说明。

  • 表名:order_history

  • 描述:某个业务的订单历史表

  • 主要字段:unsigned int id,tinyint(4) int type

  • 字段情况:该表一共37个字段,不包含text等大型数据,最大为varchar(500),id字段为索引,且为递增。

  • 数据量:5709294

  • MySQL版本:5.7.16
    线下找一张百万级的测试表可不容易,如果需要自己测试的话,可以写shell脚本什么的插入数据进行测试。
    以下的 sql 所有语句执行的环境没有发生改变,下面是基本测试结果:

select count(*) from orders_history;

返回结果:5709294

三次查询时间分别为:

  • 8903 ms

  • 8323 ms

  • 8401 ms

一般分页查询

一般的分页查询使用简单的 limit 子句就可以实现。limit 子句声明如下:

SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset

LIMIT 子句可以被用于指定 SELECT 语句返回的记录数。需注意以下几点:

  • 第一个参数指定第一个返回记录行的偏移量,注意从0开始

  • 第二个参数指定返回记录行的最大数目

  • 如果只给定一个参数:它表示返回最大的记录行数目

  • 第二个参数为 -1 表示检索从某一个偏移量到记录集的结束所有的记录行

  • 初始记录行的偏移量是 0(而不是 1)

下面是一个应用实例:

select * from orders_history where type=8 limit 1000,10;

该条语句将会从表 orders_history 中查询offset: 1000开始之后的10条数据,也就是第1001条到第1010条数据(1001 <= id <= 1010)。

数据表中的记录默认使用主键(一般为id)排序,上面的结果相当于:

select * from orders_history where type=8 order by id limit 10000,10;

三次查询时间分别为:

  • 3040 ms

  • 3063 ms

  • 3018 ms

针对这种查询方式,下面测试查询记录量对时间的影响:

select * from orders_history where type=8 limit 10000,1;select * from orders_history where type=8 limit 10000,10;select * from orders_history where type=8 limit 10000,100;select * from orders_history where type=8 limit 10000,1000;select * from orders_history where type=8 limit 10000,10000;

三次查询时间如下:

  • 查询1条记录:3072ms 3092ms 3002ms

  • 查询10条记录:3081ms 3077ms 3032ms

  • 查询100条记录:3118ms 3200ms 3128ms

  • 查询1000条记录:3412ms 3468ms 3394ms

  • 查询10000条记录:3749ms 3802ms 3696ms

另外我还做了十来次查询,从查询时间来看,基本可以确定,在查询记录量低于100时,查询时间基本没有差距,随着查询记录量越来越大,所花费的时间也会越来越多。

针对查询偏移量的测试:

select * from orders_history where type=8 limit 100,100;select * from orders_history where type=8 limit 1000,100;select * from orders_history where type=8 limit 10000,100;select * from orders_history where type=8 limit 100000,100;select * from orders_history where type=8 limit 1000000,100;

三次查询时间如下:

  • 查询100偏移:25ms 24ms 24ms

  • 查询1000偏移:78ms 76ms 77ms

  • 查询10000偏移:3092ms 3212ms 3128ms

  • 查询100000偏移:3878ms 3812ms 3798ms

  • 查询1000000偏移:14608ms 14062ms 14700ms

随着查询偏移的增大,尤其查询偏移大于10万以后,查询时间急剧增加。

这种分页查询方式会从数据库第一条记录开始扫描,所以越往后,查询速度越慢,而且查询的数据越多,也会拖慢总查询速度。

使用子查询优化

这种方式先定位偏移位置的 id,然后往后查询,这种方式适用于 id 递增的情况。

select * from orders_history where type=8 limit 100000,1;select id from orders_history where type=8 limit 100000,1;select * from orders_history where type=8 and id>=(select id from orders_history where type=8 limit 100000,1)limit 100;select * from orders_history where type=8 limit 100000,100;

4条语句的查询时间如下:

  • 第1条语句:3674ms

  • 第2条语句:1315ms

  • 第3条语句:1327ms

  • 第4条语句:3710ms

针对上面的查询需要注意:

  • 比较第1条语句和第2条语句:使用 select id 代替 select * 速度增加了3倍

  • 比较第2条语句和第3条语句:速度相差几十毫秒

  • 比较第3条语句和第4条语句:得益于 select id 速度增加,第3条语句查询速度增加了3倍

这种方式相较于原始一般的查询方法,将会增快数倍。

使用 id 限定优化

这种方式假设数据表的id是连续递增的,则我们根据查询的页数和查询的记录数可以算出查询的id的范围,可以使用 id between and 来查询:

select * from orders_history where type=2 and id between 1000000 and 1000100 limit 100;

查询时间:15ms 12ms 9ms

这种查询方式能够极大地优化查询速度,基本能够在几十毫秒之内完成。限制是只能使用于明确知道id的情况,不过一般建立表的时候,都会添加基本的id字段,这为分页查询带来很多便利。

还可以有另外一种写法:

select * from orders_history where id >= 1000001 limit 100;

当然还可以使用 in 的方式来进行查询,这种方式经常用在多表关联的时候进行查询,使用其他表查询的id集合,来进行查询:

select * from orders_history where id in(select order_id from trade_2 where goods = 'pen')limit 100;

这种 in 查询的方式要注意:某些 mysql 版本不支持在 in 子句中使用 limit。

使用临时表优化

这种方式已经不属于查询优化,这儿附带提一下。

对于使用 id 限定优化中的问题,需要 id 是连续递增的,但是在一些场景下,比如使用历史表的时候,或者出现过数据缺失问题时,可以考虑使用临时存储的表来记录分页的id,使用分页的id来进行 in 查询。这样能够极大的提高传统的分页查询速度,尤其是数据量上千万的时候。

关于数据表的id说明

一般情况下,在数据库中建立表的时候,强制为每一张表添加 id 递增字段,这样方便查询。

如果像是订单库等数据量非常庞大,一般会进行分库分表。这个时候不建议使用数据库的 id 作为唯一标识,而应该使用分布式的高并发唯一 id 生成器来生成,并在数据表中使用另外的字段来存储这个唯一标识。

使用先使用范围查询定位 id (或者索引),然后再使用索引进行定位数据,能够提高好几倍查询速度。即先 select id,然后再 select *;

原文地址:http://uusama.com/458.html

MYSQL分页优化查询相关推荐

  1. MySQL 分页优化中的 “ INNER JOIN方式优化分页算法 ” 到底在什么情况下会生效?...

    最近无意间看到一个 MySQL 分页优化的测试案例,并没有非常具体地说明测试场景的情况下,给出了一种经典的方案.因为现实中很多情况都不是固定不变的,能总结出来通用性的做法或者说是规律,是要考虑非常多的 ...

  2. MySQL 分页优化中的 “ INNER JOIN方式优化分页算法 ” 到底在什么情况下会生效?

    最近无意间看到一个 MySQL 分页优化的测试案例,并没有非常具体地说明测试场景的情况下,给出了一种经典的方案.因为现实中很多情况都不是固定不变的,能总结出来通用性的做法或者说是规律,是要考虑非常多的 ...

  3. mysql分页 disti_MySql查询性能优化

    慢查询判定 1.开启慢查询日志记录执行时间超过long_query_time 秒的sql语句 2.通过show processlist命令查看线程执行状态 3.通过explain解析sql了解执行状态 ...

  4. mysql 分页优化

    以下分享一点我的经验 一般刚开始学SQL的时候,会这样写 复制代码代码如下: SELECT * FROM table ORDER BY id LIMIT 1000, 10;  但在数据达到百万级的时候 ...

  5. mysql 分页 order_Mysql查询使用limit分页,同时使用order by可能产生的问题

    昨天遇到一个比较诡异的问题,在使用MySQL分页查询数据的时候, 有的数据明明数据库里有,但是就是查不出来,有的数据却反而会 重复出现. 这里面就涉及到一个MySQL自身的问题. 具体现象大概是: 当 ...

  6. mysql 分页优化策略(一)

    前言 这里分享下最近学习到的mysql分页策略的一种方式,之后有新的优化方案会继续更新,欢迎讨论. 环境 系统 内存 MySQL版本 引擎 CentOS release 6.3 8G 5.6.34 i ...

  7. MySQL,优化查询的方法

    对于数据库,优化查询的方法 1.使用索引 使用索引时,应尽量避免全表扫描,首先应考虑在 where 及 order by ,group by 涉及的列上建立索引. 2.优化SQL语句 1)分析查询语句 ...

  8. 查询索引MySQL性能优化 查询索引

    时光紧张,先记一笔,后续优化与完善. MySQL能性优化 能性优化是通过某些有效的方法来高提MySQL的行运速度,增加占用的磁盘空间.能性优化含包很多方面,例如优化查询速度,优化新更速度和优化MySQ ...

  9. mysql分页案例_MySQL优化案例系列-mysql分页优化

    通常,我们会采用ORDER BY LIMIT start, offset 的方式来进行分页查询.例如下面这个SQL: SELECT * FROM `t1` WHERE ftype=1 ORDER BY ...

最新文章

  1. linux内核参数sem的说明
  2. 未排序正整数组中累加和为指定值的最长子数组长度
  3. Python之tkinter:动态演示调用python库的tkinter带你进入GUI世界(Button展示图片事件)
  4. angularjs 让当前路由重新加载_Spring Cloud Gateway的动态路由怎样做?集成Nacos实现很简单...
  5. Druid 在spring中的配置
  6. 怎样查找html概念,HTML一般概念_html
  7. C语言课程设计题库及答案,C语言课程设计题目.docx
  8. python视频 神经网络 Tensorflow
  9. byteofpython.info_A Byte of Python 笔记(7)数据结构:列表、元组、字典,序列
  10. 某集团BI决策系统建设方案分享
  11. 黑马vue实战项目-(六)商品列表组件的开发
  12. python找重复元素_Python笔记(二)查找重复元素
  13. bitblt与StretchDIBits
  14. 学模具设计应该掌握哪些内容?
  15. 下划线与连字符(中划线)
  16. logstash的dissect匹配字符串内置双引号时需要注意的问题
  17. ArcEngine 开发之ITopologicalOperator 处理奇葩问题解决
  18. 马科维茨模型的实例验证与思考(含Python代码)
  19. untiy 移除一个组件
  20. vue 重复点击菜单,路由重复报错

热门文章

  1. redhat Enterprise 5下安装中文输入法,
  2. DVI和HDMI中的TMDS接口协议
  3. 低功耗设计——基于UPF进行设计
  4. HALCON示例程序class_ndim_norm.hdev基于多通道图像的分类
  5. 第5章 Python 数字图像处理(DIP) - 图像复原与重建5 - 均匀噪声
  6. 备份数据 宝塔linux_宝塔面板旧版本升级教程汇总—升级宝塔面板
  7. dateframe行列插入和删除操作
  8. js json转xml(可自定义属性,区分大小写)
  9. find 是区分大小写的。对于不区分大小写的写法(转载)
  10. HDU ACM 1728 逃离迷宫 (广搜BFS)