PostgreSQL 13 - 从具有日期范围的 table 获取不同数据的性能改进
PostgreSQL 13 - Performance Improvement to get distinct data from table with date range
我正在使用 PostgreSQL 13 并且具有 PostgreSQL 的中级经验。
我有一个名为 audit_log
的 table。每当用户登录系统时,我都会将 activity 存储在此 table.
中
下面是我的table结构,后面是数据类型和索引访问方法
Column | Data Type | Index name | Idx Access Type
-------------+-----------------------------+---------------------------+---------------------------
id | bigint | |
log_time | timestamp with time zone | idx_log_time | btree
user_id | text | idx_user_id | btree
customer_id | bigint | idx_customer_id | btree
is_active | boolean | idx_is_active | btree
is_delete | boolean | idx_is_delete | btree
我想获取在过去 30 天内为给定客户登录系统的唯一用户。
有两个客户cust1
& cust2
audit_log
table 中的总记录是 2,24,11,490
对于 cust1
,当我执行以下查询时,我得到 4,374 过去 30 天内登录系统的唯一用户,执行时间为 792 毫秒.
SELECT COUNT(DISTINCT user_id) FROM audio_log WHERE is_active=TRUE AND is_delete=FALSE AND customer_id=1 AND log_time BETWEEN '2022-01-01 00:00:00' AND '2022-01-31 00:00:00'
请注意,过去 30 天 cust1
的登录用户总数为 9,747
。
问题区域
对于 cust2
,当我执行以下查询时,我得到 88,568 过去 30 天内登录系统的唯一用户,执行时间为 3秒.
SELECT COUNT(DISTINCT user_id) FROM audio_log WHERE is_active=TRUE AND is_delete=FALSE AND customer_id=2 AND log_time BETWEEN '2022-01-01 00:00:00' AND '2022-01-31 00:00:00'
请注意,过去 30 天内 cust2
的登录用户总数为 4,86,519
。
问题
我担心的是 cust2
此查询的时间应该少于 1 秒。那么有没有什么办法可以优化和减少执行时间呢?
下面是解释查询计划
下面是我的 pg_settings
尝试过的解决方案
BRIN 指数
我为 log_time
列应用了 BRIN 索引,但执行查询花费了更多时间
分组依据选项
我按照少数解决方案中的建议使用了 GROUP BY
子句,但这也没有提高性能。
请指导。谢谢
更新
我必须添加新的条件,它不会考虑给定的 user_id 列表的非重复计数。
以下查询执行需要 1.2 秒。
SELECT COUNT(DISTINCT user_id) FROM audio_log WHERE is_active=TRUE AND is_delete=FALSE AND customer_id=2 AND log_time BETWEEN '2022-01-01 00:00:00' AND '2022-01-31 00:00:00'
当我在上面的查询中使用 not 时,它需要更多的时间来执行。下面是我更新的查询。
SELECT COUNT(DISTINCT user_id) FROM audio_log WHERE is_active=TRUE AND is_delete=FALSE AND customer_id=2 AND log_time BETWEEN '2022-01-01 00:00:00' AND '2022-01-31 00:00:00' AND user_id not in ('sndka__sjkd_jask_ldkl','eydu_iehc_jksd_hcjk_hsdk')
已添加索引
使用 BTREE 在 audio_log 上创建索引 cust_time_user
(customer_id, is_active, is_delete, log_time, user_id);
有什么办法可以优化吗??
这是一个针对多列 BTREE 索引的作业。您的查询,已注释,是这样的。
SELECT COUNT(DISTINCT user_id) -- item to deduplicate
FROM audio_log
WHERE is_active=TRUE -- equality filter
AND is_delete=FALSE -- equality filter
AND customer_id=1 -- most selective equality filter
AND log_time BETWEEN -- range filter
'2022-01-01 00:00:00' AND '2022-01-31 00:00:00'
在 BTREE 索引中,首先是相等过滤器,然后是范围过滤器,最后是要删除重复项的项目。所以,使用这个索引。我将其命名为 cust_time_user
,但您可以随意命名。
CREATE INDEX cust_time_user ON audio_log USING BTREE
(customer_id, is_active, is_delete, log_time, user_id);
Postgres 可以通过此 BTREE 索引满足您的查询,方法是随机访问第一个符合条件的行,然后按顺序扫描它以找到最后一个符合条件的行。
您的范围过滤器可能有误:BETWEEN
通常不适合 time-range 过滤器。为获得最佳结果,请使用 >=
作为范围的开头,使用 <
作为范围的结尾。你想要,我相信,
AND log_time >= '2022-01-01 00:00:00' -- earliest day
AND log_time < '2022-02-01 00:00:00' -- day after the latest day
专业提示 通常向表中添加大量 single-column 索引是没有意义的。索引在设计用于帮助满足特定形状的查询时最有用。阅读本文以获得更多精彩信息 https://use-the-index-luke.com/
我正在使用 PostgreSQL 13 并且具有 PostgreSQL 的中级经验。
我有一个名为 audit_log
的 table。每当用户登录系统时,我都会将 activity 存储在此 table.
下面是我的table结构,后面是数据类型和索引访问方法
Column | Data Type | Index name | Idx Access Type
-------------+-----------------------------+---------------------------+---------------------------
id | bigint | |
log_time | timestamp with time zone | idx_log_time | btree
user_id | text | idx_user_id | btree
customer_id | bigint | idx_customer_id | btree
is_active | boolean | idx_is_active | btree
is_delete | boolean | idx_is_delete | btree
我想获取在过去 30 天内为给定客户登录系统的唯一用户。
有两个客户cust1
& cust2
audit_log
table 中的总记录是 2,24,11,490
对于 cust1
,当我执行以下查询时,我得到 4,374 过去 30 天内登录系统的唯一用户,执行时间为 792 毫秒.
SELECT COUNT(DISTINCT user_id) FROM audio_log WHERE is_active=TRUE AND is_delete=FALSE AND customer_id=1 AND log_time BETWEEN '2022-01-01 00:00:00' AND '2022-01-31 00:00:00'
请注意,过去 30 天 cust1
的登录用户总数为 9,747
。
问题区域
对于 cust2
,当我执行以下查询时,我得到 88,568 过去 30 天内登录系统的唯一用户,执行时间为 3秒.
SELECT COUNT(DISTINCT user_id) FROM audio_log WHERE is_active=TRUE AND is_delete=FALSE AND customer_id=2 AND log_time BETWEEN '2022-01-01 00:00:00' AND '2022-01-31 00:00:00'
请注意,过去 30 天内 cust2
的登录用户总数为 4,86,519
。
问题
我担心的是 cust2
此查询的时间应该少于 1 秒。那么有没有什么办法可以优化和减少执行时间呢?
下面是解释查询计划
尝试过的解决方案
BRIN 指数 我为
log_time
列应用了 BRIN 索引,但执行查询花费了更多时间分组依据选项 我按照少数解决方案中的建议使用了
GROUP BY
子句,但这也没有提高性能。
请指导。谢谢
更新
我必须添加新的条件,它不会考虑给定的 user_id 列表的非重复计数。
以下查询执行需要 1.2 秒。
SELECT COUNT(DISTINCT user_id) FROM audio_log WHERE is_active=TRUE AND is_delete=FALSE AND customer_id=2 AND log_time BETWEEN '2022-01-01 00:00:00' AND '2022-01-31 00:00:00'
当我在上面的查询中使用 not 时,它需要更多的时间来执行。下面是我更新的查询。
SELECT COUNT(DISTINCT user_id) FROM audio_log WHERE is_active=TRUE AND is_delete=FALSE AND customer_id=2 AND log_time BETWEEN '2022-01-01 00:00:00' AND '2022-01-31 00:00:00' AND user_id not in ('sndka__sjkd_jask_ldkl','eydu_iehc_jksd_hcjk_hsdk')
已添加索引
使用 BTREE 在 audio_log 上创建索引 cust_time_user (customer_id, is_active, is_delete, log_time, user_id);
有什么办法可以优化吗??
这是一个针对多列 BTREE 索引的作业。您的查询,已注释,是这样的。
SELECT COUNT(DISTINCT user_id) -- item to deduplicate
FROM audio_log
WHERE is_active=TRUE -- equality filter
AND is_delete=FALSE -- equality filter
AND customer_id=1 -- most selective equality filter
AND log_time BETWEEN -- range filter
'2022-01-01 00:00:00' AND '2022-01-31 00:00:00'
在 BTREE 索引中,首先是相等过滤器,然后是范围过滤器,最后是要删除重复项的项目。所以,使用这个索引。我将其命名为 cust_time_user
,但您可以随意命名。
CREATE INDEX cust_time_user ON audio_log USING BTREE
(customer_id, is_active, is_delete, log_time, user_id);
Postgres 可以通过此 BTREE 索引满足您的查询,方法是随机访问第一个符合条件的行,然后按顺序扫描它以找到最后一个符合条件的行。
您的范围过滤器可能有误:BETWEEN
通常不适合 time-range 过滤器。为获得最佳结果,请使用 >=
作为范围的开头,使用 <
作为范围的结尾。你想要,我相信,
AND log_time >= '2022-01-01 00:00:00' -- earliest day
AND log_time < '2022-02-01 00:00:00' -- day after the latest day
专业提示 通常向表中添加大量 single-column 索引是没有意义的。索引在设计用于帮助满足特定形状的查询时最有用。阅读本文以获得更多精彩信息 https://use-the-index-luke.com/