我想从 BigQuery 迁移到 CloudSQL 以节省成本。 我的问题是,与 BigQuery 相比,带有 PostgreSQL 的 CloudSQL 非常慢。 在 BigQuery 中需要 1.5 秒的查询在带有 PostgreSQL 的 CloudSQL 上需要近 4.5 分钟(!)。
我有带有 PostgreSQL 服务器的 CloudSQL,具有以下配置:
我的数据库有一个包含 16M 行的主表(RAM 约为 14GB)。
示例查询:
EXPLAIN ANALYZE
SELECT
"title"
FROM
public.videos
WHERE
EXISTS (SELECT
*
FROM (
SELECT
COUNT(DISTINCT CASE WHEN LOWER(param) LIKE '%thriller%' THEN '0'
WHEN LOWER(param) LIKE '%crime%' THEN '1' END) AS count
FROM
UNNEST(categories) AS param
) alias
WHERE count = 2)
ORDER BY views DESC
LIMIT 12 OFFSET 0
该表是一个
videos
表,其中 categories
列为 text[]
。
这里的搜索条件是查找有类似 '%thriller%'
和类似 '%crime%'
两次的类别
此查询的 EXPLAIN ANALYZE 给出以下输出 (CSV):link。 此查询的 EXPLAIN (BUFFERS) 给出以下输出 (CSV):link。
查询洞察图:
内存简介:
相同表大小上相同查询的 BigQuery 参考:
服务器配置:链接。
表格描述:链接。
我的目标是让 Cloud SQL 具有与 Big Query 相同的查询速度
对于任何来到这里想知道如何在云 sql 上调整他们的 postgres 机器的人,他们称之为标志,您可以从 UI 执行此操作,尽管并非所有配置选项都可以编辑。
最初的查询看起来过于复杂。它可以重写为:
SELECT v."title"
FROM public.videos v
WHERE array_to_string(v.categories, '^') ILIKE ALL (ARRAY['%thriller%', '%crime%'])
ORDER BY views DESC
LIMIT 12 OFFSET 0;
PostGreSQL 在涉及 COUNT 聚合函数的每个查询上设计得非常慢,除了物化视图来强制执行性能之外,绝对没有什么可做的。
我在 48 个核心的机器上进行的关于 从 PostGreSQL 到 MS SQL Server 的 COUNT 性能比较 的测试很清楚:在所有情况下,SQL Server 的速度都快 61 到 561 倍,而使用列存储索引时,SQL Server 可以快 1,533 倍更快...
使用任何其他 RDBMS 时可以达到相同的速度。解释显然是 PG MVCC 在表和索引页中维护幽灵行,需要浏览每一行以了解它是活动行还是幽灵行...在所有其他 RDBMS 中,计数是通过仅读取一个来完成的页面顶部的信息(页面中的行数),还可以使用并行访问或在 SQL Server 中批量访问而不是行访问...
在存储引擎不会被完全重写以避免页面内出现鬼槽之前,没有什么可以加快PG中的计数...
我相信你需要使用全文搜索和特殊的 GIN 索引。步骤:
为索引创建辅助函数:
CREATE OR REPLACE FUNCTION immutable_array_to_string(text[]) RETURNS text as $$ SELECT array_to_string($1, ','); $$ LANGUAGE sql IMMUTABLE;
创建索引本身:
CREATE INDEX videos_cats_fts_idx ON videos USING gin(to_tsvector('english', LOWER(immutable_array_to_string(categories))));
使用以下查询:
SELECT title FROM videos WHERE (to_tsvector('english', immutable_array_to_string(categories)) @@ (to_tsquery('english', 'thriller & crime'))) limit 12 offset 0;
请注意,此查询对于“犯罪”和“惊悚片”具有不同的含义。它们不仅仅是子串。它们是英语短语中的标记。但看起来实际上它更适合您的任务。此外,该索引不适用于频繁更改的数据。当您的数据大部分为只读时,它应该可以正常工作。
PS 这个答案的灵感来自于答案和评论:https://stackoverflow.com/a/29640418/159923
除了sql语法优化,你尝试过Postgresql调优吗?
我查了解释发现只有两个worker并行,排序用了25KMemory。
计划工人:2" 排序方式:快速排序内存:25kB"
对于您的查询,这是典型的OLAP查询。它的性能通常与内存(使用的内存和CPU核心(workers)有关。默认的postgres使用KB级内存和很少的workers。您可以调整postgresql.conf以优化它作为OLAP类型数据库的工作。
====================================================== == 这是我的建议:使用更多内存(9MB 作为工作内存)和更多 cpu(最多 16)
# DB Version: 13
# OS Type: linux
# DB Type: dw
# Total Memory (RAM): 24 GB
# CPUs num: 16
# Data Storage: ssd
max_connections = 40
shared_buffers = 6GB
effective_cache_size = 18GB
maintenance_work_mem = 2GB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 500
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 9830kB
min_wal_size = 4GB
max_wal_size = 16GB
max_worker_processes = 16
max_parallel_workers_per_gather = 8
max_parallel_workers = 16
max_parallel_maintenance_workers = 4
您可以将其添加到 postgresql.conf 最后一行。并重启你的postgresql服务器使其生效。
为了进一步优化,
仅此而已,祝你好运。
王勇