我创建了一个物化 View ,以便输入仪表板。
我的目标是以尽可能最快的方式选择该表,但我不知道如何实现它。我希望如果我描述该表及其使用方式,有人可以提供一些指导。
上下文是一个具有漏斗步骤的网站。每一行都是用户触发漏斗步骤的一个实例,例如添加到购物车、结帐、付款详细信息,最后是交易。
由于该表用于分析目的,因此每天早上只会使用 cron 自动刷新一次,所以我不担心实时更新速度,只需使用各种 where 子句选择速度。
假设我有下面描述的字段:
(N = ~1300 万,预计到 1 月份将达到 ~20,每月增长数百万) 表是唯一的,包含 session ID、用户 ID 和漏斗步骤的组合。
- Session Id (Id, so some duplication but generally very very granular - Varchar)
- User Id (Id, so some duplication but generally very very granular - Varchar)
- Date (Date)
- Funnel Step (10 distinct value - Varchar)
- Device Category (3 distinct values - Varchar)
- Country (~ 100 distinct values - varchar)
- City (~1000+ distinct values - varchar)
- Source (several thousand distinct values, nevertheless, stakeholder would like a filter - varchar)
我会单独索引每个字段吗?或者,我应该索引 oneer 中的所有字段吗? Per the documentation ,我想我可以一次索引最多32个字段。但考虑到我的主要目标是选择查询速度而不是其他一切,这样做是否明智?
该表将输入仪表板,仪表板读取该表并将过滤器输入动态转换为 where 子句。每次用户调整过滤器时,都会根据过滤器/where 子句输入读取表并进行分组和聚合。
示例查询:
select
event_action,
count(distinct user_id) as users
from website_data.ecom_funnel
where date >= $input_start_date
and date <= $input_end_date
and device_category in ($mobile, $desktop, $tablet)
and country in ($list of all countries minus any not selected)
and source in ($list of all sources minus any not selected)
group by 1 order by users desc
这将产生一个漏斗形的数据表。
我无法预先聚合,因为关注的主要指标是用户,而不是 session 。必须从基础表中删除这些重复数据。经典示例...假设一个人一周每天访问一个网站一次。那么该周的独立访问者总数为 1,但是如果我按天对访问者进行求和,我会得到 7。与我的表类似,一些用户需要多次 session 才能完成漏斗。因此,这就是为什么我无法预聚合表,因为我需要对基础数据应用过滤器,然后count(distinct user id)
。
以下是对字段子集的解释(如果有用):
QUERY PLAN
Sort (cost=862194.66..862194.68 rows=9 width=24)
Sort Key: (count(DISTINCT client_id)) DESC
-> GroupAggregate (cost=847955.01..862194.51 rows=9 width=24)
Group Key: event_action
-> Sort (cost=847955.01..852701.48 rows=1898589 width=37)
Sort Key: event_action
-> Seq Scan on ecom_funnel (cost=0.00..589150.14 rows=1898589 width=37)
Filter: ((device_category = ANY ('{mobile,desktop}'::text[])) AND (source = 'google'::text))
我的首要具体问题是,考虑到我的用例,我应该单独索引每个字段还是应该创建一个索引?这有关系吗?
最重要的是,如果有任何关于优化此物化 View 以更快地运行选择查询的提示,我们将不胜感激。
最佳答案
查看您的过滤条件,您应该通过发布来检查 device_category 字段的基数
select device_category, count(*) from website_data.ecom_funnel group by device_category
并查看值以确定索引是否应首先包含此列。这里可能的索引(不知道基数)将是多列,包括:
(device_category, date)
话虽如此,在每个单独的列上创建索引没有任何好处,因为您的查询不会全部使用它们,所以它确实很重要。您会减慢非读取操作的其他 CRUD 操作。
在所有列上创建索引可能也不会让您加快太多速度,但这是基于底层(表中)的数据以及您的过滤器与没有它们的整体查询的比较(被过滤的列中值的基数)。这很可能会产生巨大的开销,需要遍历索引树,然后获取 rowids 以返回所需的数据。
总而言之,我会尝试将索引缩小到在过滤中最重要的列,这意味着它们会删除大部分正在检索的数据。如果您的查询旨在返回表中的大部分行,那么不幸的是,需要进行聚合,因为这不会加快速度。
希望有帮助。
编辑:我刚刚读到您已经发布了表格中不同值的计数。我不确定漏斗步骤在您的表中绑定(bind)到什么,但假设它是一个名为 event_action 的列,那么创建一个索引可能会更有益,该索引也有助于分组:
(date, event_action)
看来您根本省略了 GROUP BY
子句,该子句应该包含在内,并且应该按 event_action 进行分组,因为这就是您的选择部分正在做的事情。
如果每次执行选择查询时将日期范围缩小到几天/几个月,那么使用第一个 date
列创建索引可能会带来巨大的好处。
请记住,索引中列的位置很重要。
如果您查找几个月的值,比如说,您应该预先聚合并将每个月的预先计算值存储在另一个表中,然后将该数据UNION ALL
到当前查询,该查询只会从以下位置选择数据当前(仍在更新)时间。
关于postgresql - 专门为仪表板构建的表有几个过滤器......最好的索引方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59507438/