MySQL进阶【五】—— MySQL分页查询优化

导读:本篇文章讲解 MySQL进阶【五】—— MySQL分页查询优化,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

分页查询优化

分页查询在项目中非常常见,一般数据量越大,分页的速度越慢,mysql在limit时,不是先找到limit的起始行,而是将limit的全部数据检索出来,再去到需要的数据块,概念可能比较模糊,没关系,接下来案例中继续讲解

mysql> select * from employees limit 10000,10;
+-------+------------+-------+----------+---------------------+
| id    | name       | age   | position | hire_time           |
+-------+------------+-------+----------+---------------------+
| 10004 | zhuge9998  |  9998 | dev      | 2020-09-25 14:57:39 |
| 10005 | zhuge9999  |  9999 | dev      | 2020-09-25 14:57:39 |
| 10006 | zhuge10000 | 10000 | dev      | 2020-09-25 14:57:39 |
| 10007 | zhuge10001 | 10001 | dev      | 2020-09-25 14:57:39 |
| 10008 | zhuge10002 | 10002 | dev      | 2020-09-25 14:57:39 |
| 10009 | zhuge10003 | 10003 | dev      | 2020-09-25 14:57:39 |
| 10010 | zhuge10004 | 10004 | dev      | 2020-09-25 14:57:39 |
| 10011 | zhuge10005 | 10005 | dev      | 2020-09-25 14:57:39 |
| 10012 | zhuge10006 | 10006 | dev      | 2020-09-25 14:57:39 |
| 10013 | zhuge10007 | 10007 | dev      | 2020-09-25 14:57:39 |
+-------+------------+-------+----------+---------------------+
10 rows in set (0.04 sec)
  • 表示从表 employees 中取出从 10001 行开始的 10 行记录。看似只查询了 10 条记录,实际这条 SQL 是先读取 10010 条记录,然后抛弃前 10000 条记录,然后读到后面 10 条想要的数据。因此要查询一张大表比较靠后的数据,执行效率 是非常低的。

常见的优化技巧

1、主键连续自增的情况
mysql> select * from employees limit 90000,5;
+-------+------------+-------+----------+---------------------+
| id    | name       | age   | position | hire_time           |
+-------+------------+-------+----------+---------------------+
| 90004 | zhuge89998 | 89998 | dev      | 2020-09-25 14:57:49 |
| 90005 | zhuge89999 | 89999 | dev      | 2020-09-25 14:57:49 |
| 90006 | zhuge90000 | 90000 | dev      | 2020-09-25 14:57:49 |
| 90007 | zhuge90001 | 90001 | dev      | 2020-09-25 14:57:49 |
| 90008 | zhuge90002 | 90002 | dev      | 2020-09-25 14:57:49 |
+-------+------------+-------+----------+---------------------+
5 rows in set (0.23 sec)
- 查询时长 0.23s

mysql> select * from employees where id > 90000 limit 5;
+-------+------------+-------+----------+---------------------+
| id    | name       | age   | position | hire_time           |
+-------+------------+-------+----------+---------------------+
| 90001 | zhuge89995 | 89995 | dev      | 2020-09-25 14:57:49 |
| 90002 | zhuge89996 | 89996 | dev      | 2020-09-25 14:57:49 |
| 90003 | zhuge89997 | 89997 | dev      | 2020-09-25 14:57:49 |
| 90004 | zhuge89998 | 89998 | dev      | 2020-09-25 14:57:49 |
| 90005 | zhuge89999 | 89999 | dev      | 2020-09-25 14:57:49 |
+-------+------------+-------+----------+---------------------+
5 rows in set (0.01 sec)
- 查询时间0.1s
  • 解析:第二个SQL比第一个SQL提高性能23倍,效果显著
  • 但是这种情况只适用于主键连续的情况,这种实际生产当中比较少见。我们以这条SQL为基础,做个变形。select * from employees where id > #{id} limit 5; 这里#{id}作为一个是上一次查询结果的最大id,在下一次查询中入参进来即可
2、根据非主键字段排序的分页查询
mysql> explain select * from employees ORDER BY name limit 90000,5;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra          |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 100185 |   100.00 | Using filesort |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
1 row in set, 1 warning (0.00 sec)
  • 解析:虽然order by 字段是索引字段,因select * 需要回表,MySQL查询优化器判定全表扫描更快,故没有用到索引
  • 优化方向
mysql> explain select * from (select id from employees order by name limit 90000,5) t;
+----+-------------+------------+------------+-------+---------------+-----------------------+---------+------+-------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys | key                   | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+------------+------------+-------+---------------+-----------------------+---------+------+-------+----------+-------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL   | NULL          | NULL                  | NULL    | NULL | 90005 |   100.00 | NULL        |
|  2 | DERIVED     | employees  | NULL       | index | NULL          | idx_name_age_position | 140     | NULL | 90005 |   100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+-----------------------+---------+------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
  • 优化后的SQL,先取出id,生成一个临时表,临时表使用到了索引,重点原因:取id不需要回表

总结

  • 在查询中避免回表查询会大大提升效率
  • 减少回表的两种方式:
    • 使用覆盖索引
    • 转换成子查询,通过取出id的方式,再去查询

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83662.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!