MySQL百万级分页查询优化

百万级数据做分页如何优化?

打卡日期(2019-07-15)

    数据量很大,分页查询很慢,有什么优化方案?
    当需要从数据库中查询的表有上百万条记录的时候,一次性查去所有结果会变的很慢,特别是随着数据量的增加更加明显,这时候如果需要分页的话会变的很慢很慢。

准备工作
  • 表明 test
  • 主键id自增,总共有11个字段,不包含text等大型数据,最大的字段为500 varchar
  • 数据量:2097152
select count(*) from test
返回结果:2097152
三次查询情况:
    26.27s
    27.01s
    26.12s

    从查询情况来看,单单统计表中有多少条数据就耗时26s,更别说分页了。

一般的分页查询

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

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

    Limit子句可以被用于指定select语句返回记录数,需要注意几点:

  • 第一个参数指返回记录的偏移量,注意从0开始
  • 第二个参数指定返回的最大数目
  • 如果只给定一个参数,表示返回的最大记录行数
  • 第二个参数为-1,表示检索某个偏移量到记录集结束所有的记录行
  • 初始记录偏移量是0而不是1

    接下来从分页情况来说明一下

select * from test LIMIT 1000,10;

select * from test LIMIT 10000,10;

select * from test LIMIT 100000,10;

select * from test LIMIT 1000000,10;

select * from test LIMIT 2097000,10;

    5次10条查询结果如下

第一条sql:0.172s, 0.002s, 0.001s, 0.001s, 0.001s
第二条sql:0.268s, 0.009s, 0.009s, 0.009s, 0.010s
第三条sql:1.865s, 0.073s, 0.072s, 0.074s, 0.073s,
第四条sql:11.396s, 11.569s, 11.942s, 11.565s, 11.354s
第五条sql:17.378s, 18.686s, 20.449s, 18.126s, 17.656s

    从查询结果来分析3个问题

-   1.前10万条的数据查询的时间都很短
-   2.前10万条每第一次查询的时间跟后面的查询时间相差几十倍
-   3.当数据量大于100万的时候,查询时间基本持平
mysql缓存机制

    为了解决上述困惑,首先了解一下mysql缓存机制,query_cache_type 缓存默认128M,默认查询缓存关闭。
    mysql缓存机制就是缓存SQL文本及缓存结果,以KV的形式保存到服务器的内存当中,如果运行的SQL相同则直接从缓存中获取数据,不需要再重新解析、优化、执行SQL。
    如果这个表被修改了或者表中的数据有任何变化(insert/delete/update/droptable…),这个表的缓存结果将不在有效。
    mysql的缓存针对的主要是那些表结构不变,数据不会频繁被修改的数据。

mysql缓冲区

     innodb_buffer_pool_size 默认也是128M,这个参数定义了InnoDB存储引擎的表数据和索引数据的最大内存缓冲区大小,适当增加这个参数的大小可以有效的减少InnoDB类型的表的磁盘IO。这个参数缓存了数据+索引

mysql命中条件

    缓存存在一个hash表中,通过查询SQL,查询数据库,客户端协议等作为key,在判断命中前,mysql不会解析SQL,而是去缓存中查询。QL上的热河符号不同,如空格,注释都会导致缓存不命中。
    如果在缓存中没有命中,则直接去mysql中查询
,以SQL和一些其他条件作为key查询缓存

mysql工作流程
  • 服务器接收到SQL,以SQL和一些其他条件作为key查询缓存
  • 如果在缓存中找到了,则直接返回缓存数据
  • 如果没有命中,则执行SQL查询,包括原来的SQL解析、优化、执行SQL。
  • 执行完SQL之后,将SQL结果写入缓存。

    了解到了mysql的上述几个概念,接下来开始一一解答上述问题。

1.前10万条的数据查询的时间都很短

    mysql有默认缓冲区innodb_buffer_pool_size,大小128M,可以看一下数据量,小于10万条的时候数据量基本上还没有达到128M,MySQL会将查询结果缓存到内存当中,当下一次查询的时候直接从内存当中查询。
    所以,第一次查询的时候,因为内存当中没有sql(key)对应的数据,所以查询时间慢。当第二次以后查询的时候,因为内存当中有缓存的数据,所以直接从内存当中查出来的时间非常短,每次几乎相同。这就是个问题的解析。
    至于为什么是innodb_buffer_poor_size而不是query_cache_type,因为mysql默认是关闭query_cache_type查询缓存的,至于为什么要关闭这个设置,网上说这个设置很鸡肋,但是我没找到具体的原因。

2.前10万条每第一次查询的时间跟后面的查询时间相差几十倍

    前10万条数据量没有超过mysql设置的缓冲区(innodb_buffer_poor_size)大小,所以前10万条数据直接存入了缓冲区,当第二次查找的时候直接从缓存中查询,所以第一次查询时间很长,后面查询时间短

3.当数据量大于100万的时候,查询时间基本持平

    innodb_buffer_poor_size 缓冲区默认设置大小128M,当数据量大于128M的时候,就不会缓存当前数据,会直接去数据库中查询。也就是说缓冲区中存的数据只有前10万条,或者前100万条,大于100万条的数据不会存入道缓冲区当中,所以直接查询数据库,时间基本一致。

那么如何设置mysql缓冲区大小?

show global variables like 'innodb_buffer_pool_size';
show global status like 'Innodb_buffer_pool_pages_data';
show global status like 'Innodb_page_size';

    调优计算方法:

  • val = Innodb_buffer_pool_pages_data / Innodb_buffer_pool_pages_total * 100%
  • val > 95% 则考虑增大 innodb_buffer_pool_size, 建议使用物理内存的75%
  • val < 95% 则考虑减小 innodb_buffer_pool_size, 建议设置为:Innodb_buffer_pool_pages_data * Innodb_page_size * 1.05 / (1024 * 1024 * 1024)
  • set global innodb_buffer_pool_size = 内存的1/4或者1/5为佳
优化的分页查询写法
select * from test where id > 500000 limit 10;

select * from test a inner join(select id from test  LIMIT 2000000 , 20000) b on a.id = b.id ;


select * from test where id >= (select id from test limit 2000000 , 1) LIMIT 20000;
  • 尽量给出查询的大致范围 id >= 范围
  • 子查询方法 id >= (select 子查询)
  • 利用inner join、left join…
  • 设置mysql缓冲区大小 set global innodb_buffer_pool_size = 内存的1/4或者1/5为佳

(缓冲区)innodb_buffer_pool_size 跟 (缓存区)query_cache_type区别

    区别:

  • buffer pool中缓存的是整张表中的数据(数据+索引),SQL无论怎么变化,只要数据在内存当中那么命中率就是100%
  • cache 缓存的是SQL语句对应的结果集,缓存在内存当中,最简单的是当SQL一直变化,那么缓存的命中率就是0%

猜你喜欢

转载自blog.csdn.net/u011291990/article/details/95980851