CloudSQL 与 PostgreSQL 性能非常慢

问题描述 投票:0回答:6

我想从 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 相同的查询速度

postgresql query-optimization google-cloud-sql
6个回答
1
投票

对于任何来到这里想知道如何在云 sql 上调整他们的 postgres 机器的人,他们称之为标志,您可以从 UI 执行此操作,尽管并非所有配置选项都可以编辑。

https://cloud.google.com/sql/docs/postgres/flags#console


1
投票

最初的查询看起来过于复杂。它可以重写为:

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;

db<>小提琴演示


0
投票

PostGreSQL 在涉及 COUNT 聚合函数的每个查询上设计得非常慢,除了物化视图来强制执行性能之外,绝对没有什么可做的。

我在 48 个核心的机器上进行的关于 从 PostGreSQL 到 MS SQL Server 的 COUNT 性能比较 的测试很清楚:在所有情况下,SQL Server 的速度都快 61 到 561 倍,而使用列存储索引时,SQL Server 可以快 1,533 倍更快...

使用任何其他 RDBMS 时可以达到相同的速度。解释显然是 PG MVCC 在表和索引页中维护幽灵行,需要浏览每一行以了解它是活动行还是幽灵行...在所有其他 RDBMS 中,计数是通过仅读取一个来完成的页面顶部的信息(页面中的行数),还可以使用并行访问或在 SQL Server 中批量访问而不是行访问...

在存储引擎不会被完全重写以避免页面内出现鬼槽之前,没有什么可以加快PG中的计数...


0
投票

我相信你需要使用全文搜索和特殊的 GIN 索引。步骤:

  1. 为索引创建辅助函数:

    CREATE OR REPLACE FUNCTION immutable_array_to_string(text[]) RETURNS text as $$ SELECT array_to_string($1, ','); $$ LANGUAGE sql IMMUTABLE;

  2. 创建索引本身:

    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


0
投票

除了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服务器使其生效。

为了进一步优化,

  1. 减少连接,增加work_mem。 200* 9830 大约为所有连接提供 2GB 内存。如果您的连接数较少(例如 100 个),您可以获得更多内存用于查询工作。

====================================== 关于使用文本数组类型和取消嵌套。您可以尝试添加适当的索引。

仅此而已,祝你好运。

王勇


0
投票

您可以在

Flags
部分修改性能参数设置 例如

© www.soinside.com 2019 - 2024. All rights reserved.