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

分页查询优化

分页查询在项目中非常常见,一般数据量越大,分页的速度越慢,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     |  | 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的方式,再去查询

你可能感兴趣的:(MySQL,mysql,sql,索引,数据库)