mysql分页查询优化,大数据量优化

传统的mysql分页查询

select * from table limit n , m
MySQL 执行此类SQL时需要先分页(默认一页1000条数据)通过全表扫描到N行,然后再去取M行。对于此类操作,获取前面少数几行数据会很快,但是随着扫描的记录数越多,SQL的性能就会越差,因为N的值越大,MySQL需要扫描越多的数据来定位到具体的N行,这样耗费大量的 IO 成本和时间成本。
特别是上线后数据量积累比较快,必须重视SQL优化,否则影响系统运行和用户使用体验
性能实验

  1. 直接用limit start, count分页语句, 也是我程序中用的方法:
select * from table limit start, count

当起始页较小时,查询没有性能问题,我们分别看下从10, 100, 1000, 10000,
30000开始分页的执行时间(每页取10条)。
如下:

select * from table limit 10, 10   0.000秒
select * from table limit 100, 10   0.000秒
select * from table limit 1000, 10   0.005秒
select * from table limit 10000,10   0.038秒
select * from table limit 30000, 10   1.160

可以告诉大家,做性能试验的表数据在30W+条,大家可以看出随着查询条数的增加,相对查询所需时间也增加了,这说明分页语句limit跟起始页码是有很大关系的,同样大家也看到查询起始页小时,查询时间为0秒,为什么会出现这样的情况?

这需要引入innoDB的缓存池概念

查看InnoDB缓存池大小:
show global variables like ‘innodb_buffer_pool_size’;
-------------------------±------------+
| Variable_name | Value |
±------------------------±------------+
| innodb_buffer_pool_size | 34359738368 |
±------------------------±------------+
默认InnoDB缓存池大小为128M
InnoDB缓存池用来存储各种数据的缓存,包括:
数据页,插入缓存,自适应索引哈希,索引页,锁信息,数据字典信息等。
InnoDB缓存池会把查询过的热点数据资源加载到缓存池,只要加载过到缓存池的数据,再次查询的时候直接从缓存池获取而不需要通过mysql查询,所以查询时间会出现0秒的情况.
相关的缓存知识,会单独开辟一篇博客

言归正传,在查询数据起始页越多的情况下,查询效率越慢,我们从缓存池配置知道,通过增加缓存池大小,把经常查询的数据加载到缓存池,可以起到优化查询的作用,但是数据本身的查询效率慢是否可以优化呢?

利用表的覆盖索引来加速分页查询

查看表索引:
show index from table;

+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| lv_seal |          0 | PRIMARY     |            1 | id          | A         |      318834 |     NULL | NULL   |      | BTREE      |         |               |
| lv_seal |          0 | seal_no     |            1 | seal_no     | A         |      322299 |     NULL | NULL   |      | BTREE      |         |               |
| lv_seal |          1 | name        |            1 | name        | A         |      292690 |     NULL | NULL   | YES  | BTREE      |         |               |
| lv_seal |          1 | lv_seal_ik1 |            1 | uuid        | A         |      305924 |     NULL | NULL   | YES  | BTREE      |         |               |
| lv_seal |          1 | lv_seal_ik2 |            1 | status      | A         |           9 |     NULL | NULL   | YES  | BTREE      |         |               |
| lv_seal |          1 | lv_seal_ik3 |            1 | create_date | A         |      248352 |     NULL | NULL   | YES  | BTREE      |         |               |
| lv_seal |          1 | lv_seal_ik4 |            1 | update_date | A         |      245174 |     NULL | NULL   | YES  | BTREE      |         |               |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

我们都知道,利用了索引查询的语句中如果只包含了那个索引列(覆盖索引),那么这种情况会查询很快。
因为利用索引查找有优化算法,且数据就在查询索引上面,不用再去找相关的数据地址了,这样节省了很多时间。加上Mysql中的索引缓存,在并发高的时候利用缓存就效果更好了。
在我们的例子中,我们知道id字段是主键,自然就包含了默认的主键索引。现在让我们看看利用覆盖索引的查询效果如何。
这次我们之间查询最后一页的数据(利用覆盖索引,只包含id列),如下:

select id from table limit 300000,10   0.09

相对于查询了所有列的1.16秒,提升了很多
那么如果我们也要查询所有列,有两种方法,
方式一:d>=的形式,

SELECT * FROM table WHERE ID > =(select id from table limit 300000, 1) limit 10

查询时间为0.09秒!
方式二:通过内链接查询

SELECT * FROM table a JOIN (select id from table limit 300000, 10) b ON a.ID = b.id

查询时间0.10也很短!

发布了32 篇原创文章 · 获赞 53 · 访问量 2468

猜你喜欢

转载自blog.csdn.net/qq_41714882/article/details/104406171