论count使用不当的罪名 和 分页的优化

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介:

分页是一个非常常见的应用场景,然而恐怕没有多少人想过其优化方法。
确一味的责怪为什么数据库用count(*)计算分页数是如此的慢。
很多开发人员喜欢用count先算一下结果集的大小,然后就知道需要排多少页。
然后再从数据库取出对应的数据,并展示给用户。
问题1
count会扫一遍数据,然后取数据又扫一遍数据。重复劳动。
问题2,很多人喜欢用order by offset limit来展示分页。
其实也是一个非常大的问题,因为扫描的数据也放大了,即使在order by 的列上用到了索引也会放大扫描的数据量。
因为offset的row也是需要扫的。

问题1的优化
使用评估行数,方法如下
创建一个函数,从explain中抽取返回的记录数

CREATE FUNCTION count_estimate(query text) RETURNS INTEGER AS
$func$
DECLARE
    rec   record;
    ROWS  INTEGER;
BEGIN
    FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
        ROWS := SUBSTRING(rec."QUERY PLAN" FROM ' rows=([[:digit:]]+)');
        EXIT WHEN ROWS IS NOT NULL;
    END LOOP;
 
    RETURN ROWS;
END
$func$ LANGUAGE plpgsql;

评估的行数和实际的行数相差不大,精度和柱状图有关。
PostgreSQL autovacuum进程会根据表的数据量变化比例自动对表进行统计信息的更新。
而且可以配置表级别的统计信息更新频率以及是否开启更新。

postgres=# select count_estimate('select * from sbtest1 where id between 100 and 100000');
 count_estimate 
----------------
         102166
(1 row)

postgres=# explain select * from sbtest1 where id between 100 and 100000;
                                      QUERY PLAN                                       
---------------------------------------------------------------------------------------
 Index Scan using sbtest1_pkey on sbtest1  (cost=0.43..17398.14 rows=102166 width=190)
   Index Cond: ((id >= 100) AND (id <= 100000))
(2 rows)

postgres=# select count(*) from sbtest1 where id between 100 and 100000;
 count 
-------
 99901
(1 row)

也就是说,应用程序完全可以使用评估的记录数来评估分页数。
这样做就不需要扫描表了,性能提升尤为可观。

问题2的优化
问题2其实表现在数据可能被多次扫描,使用游标就能解决。
未优化的情况,取前面的记录很快。

postgres=# explain analyze select * from sbtest1 where id between 100 and 1000000 order by id offset 0 limit 100;
                                                                QUERY PLAN                                                                
------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.43..9.74 rows=100 width=190) (actual time=0.019..0.088 rows=100 loops=1)
   ->  Index Scan using sbtest1_pkey on sbtest1  (cost=0.43..93450.08 rows=1003938 width=190) (actual time=0.018..0.051 rows=100 loops=1)
         Index Cond: ((id >= 100) AND (id <= 1000000))
 Planning time: 0.152 ms
 Execution time: 0.125 ms
(5 rows)

取后面的记录,因为前面的记录也要扫描,所以明显变慢。

postgres=# explain analyze select * from sbtest1 where id between 100 and 1000000 order by id offset 900000 limit 100;
                                                                  QUERY PLAN                                                                   
-----------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=83775.21..83784.52 rows=100 width=190) (actual time=461.941..462.009 rows=100 loops=1)
   ->  Index Scan using sbtest1_pkey on sbtest1  (cost=0.43..93450.08 rows=1003938 width=190) (actual time=0.025..308.865 rows=900100 loops=1)
         Index Cond: ((id >= 100) AND (id <= 1000000))
 Planning time: 0.179 ms
 Execution time: 462.053 ms
(5 rows)

如果有很多个分页,效率下降可想而知。

优化手段

postgres=# begin;
BEGIN
Time: 0.152 ms
postgres=# declare cur1 cursor for select * from sbtest1 where id between 100 and 1000000 order by id;
DECLARE CURSOR
Time: 0.422 ms
postgres=# fetch 100 from cur1;
。。。

获取到数据末尾时,效率也是一样的不会变化。

目录
相关文章
|
存储 缓存 NoSQL
利用Redis List实现数据库分页快速查询的有效方法
利用Redis List实现数据库分页快速查询的有效方法
|
C# Windows
.NET开源免费的Windows快速文件搜索和应用程序启动器
今天大姚给大家分享一款.NET开源(MIT License)、免费、功能强大的Windows快速文件搜索和应用程序启动器:Flow Launcher。
412 0
|
NoSQL 关系型数据库 MySQL
微服务架构下的数据库选择:MySQL、PostgreSQL 还是 NoSQL?
在微服务架构中,数据库的选择至关重要。不同类型的数据库适用于不同的需求和场景。在本文章中,我们将深入探讨传统的关系型数据库(如 MySQL 和 PostgreSQL)与现代 NoSQL 数据库的优劣势,并分析在微服务架构下的最佳实践。
|
算法 关系型数据库 数据库
德哥的PostgreSQL私房菜 - 史上最屌PG资料合集
看完并理解这些文章,相信你会和我一样爱上PostgreSQL,并成为PostgreSQL的布道者。 沉稳的外表无法掩饰PG炙热的内心 。 扩展阅读,用心感受PostgreSQL 内核扩展 《找对业务G点, 体验酸爽 - PostgreSQL内核扩展指南》https://yq.
59483 152
|
关系型数据库
PostgreSQL 百亿级数据范围查询, 分组排序窗口取值 极致优化 case
本文将对一个任意范围按ID分组查出每个ID对应的最新记录的CASE做一个极致的优化体验。优化后性能维持在可控范围内,任意数据量,毫秒级返回,性能平稳可控。比优化前性能提升1万倍。 CASE如下: 有一张数据表,结构: CREATE TABLE target_position
17239 0
|
关系型数据库 MySQL Linux
Linux下查看软件安装与安装路径
Linux下查看软件安装与安装路径
1982 0
|
SQL 数据可视化 数据挖掘
一文带你看懂Python数据分析利器——Pandas的前世今生
一文带你看懂Python数据分析利器——Pandas的前世今生
402 0
|
运维 Cloud Native Devops
产品交付双轮驱动下的研发工具思考与实践
产品交付的双轮驱动思维模型强调以&quot;业务价值&quot;和&quot;产品交付&quot;为核心,前者把握方向,后者提供动力。该模型通过理解需求、确定真北、团队探讨和方案精炼(价值轮)来确保业务价值,然后借助开发、测试、运维和反馈(交付轮)实现快速产品交付。根据不同的业务定位,如战略级、运营级或管理级,选择合适的研发效能工具,如PingCode、GitLab、简单云、阿里云云效和思码逸,以支持不同层次的需求。思码逸尤其以其研发效能度量和数据分析能力突出。
499 2
|
存储 JSON 算法
新手避坑:盘点使用ClickHouse最容易犯的12个错误
在这篇文章中,我们突出了新手用户遇到的最常见的12个问题,这些问题是由于在使用ClickHouse的过程中,不遵循最佳实践,甚至反最佳实践而导致的。对于每一个问题,我们都推荐了一个解决方案或正确的使用方法。
8998 18
新手避坑:盘点使用ClickHouse最容易犯的12个错误
|
缓存 监控 NoSQL
一文了解Redis 使用的规范和最佳实践
【2月更文挑战第6天】
1010 0
一文了解Redis 使用的规范和最佳实践