即使有大量数据,Timescaledb 内存消耗仍然很低

Timescaledb memory consumption remains very low even when there is a lot of data

我有一个 timescaledb 设置,我会定期转储数据(每 10 分钟左右)。它是使用 docker-compose.

部署的

数据由一个 entity_id 和一个测量值以及其他一些东西组成,例如区域等。

一个典型的查询是get per day minimum for last 30 days for an entity_id

    SELECT
        time_bucket_gapfill('1 day', time, date_trunc('day', now() - interval '30 days'), date_trunc('day', now())) AS one_day,
        country,
        type_id,
        min(measurement)
    FROM hypertable
    WHERE
        entity_id='XYZ' AND
        country='US' AND
        time > time_bucket('1 day', now() - interval '30 days') AND 
        time < time_bucket('1 day', now())
    GROUP BY one_day, country, type_id

数据库当前有大约 24 个块(24 天的数据)。

我一直在使用 wrk 进行一些基准测试和负载测试,发现大量查询需要几秒钟才能完成。 我看到的问题是 timescaledb 的 ram 使用率保持在大约 50MB。并在负载测试期间达到约 100MB 运行 上述查询,而 CPU 使用率和磁盘 IO 显着增加。

我的预期是大约一大块(最近的一块)数据会在 RAM 中。 即使该假设是错误的,在发出 30 天查询时,ram 使用率也不会增加。

我想了解这是预期行为还是设置存在问题。

我尝试为 docker 容器设置内存预留和内存限制,但没有效果。

PostgreSQL的内存消耗主要与:

shared_buffers:整个 PostgreSQL 实例的恒定内存量,在所有会话之间共享。 work_mem 会话中 sort/hash 操作可用的内存量。这可以在每个会话中多次使用。

如果您的数据库服务器上有空闲内存,增加 shared_buffers 是有意义的,这样更多的数据就会保存在内存中。传统观点认为 25% 的可用 RAM 是一个很好的起点。当您使用 docker-compose 时,您可能与其他进程共享此服务器,因此您可能需要调整它以考虑到这一点。

您 could/should 还增加 work_mem 以便更多 hash/sort 操作使用内存而不是磁盘。 work_mem 是一个可以在您的会话中设置的变量,因此您可以尝试一些操作,例如比较以下内容:

feike=# EXPLAIN (ANALYZE ON, BUFFERS ON) SELECT * FROM pg_class ORDER BY relfilenode;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Sort  (cost=230.82..234.95 rows=1652 width=782) (actual time=3.149..3.477 rows=1680 loops=1)
   Sort Key: relfilenode
   Sort Method: quicksort  Memory: 1246kB
   Buffers: shared hit=126
   ->  Seq Scan on pg_class  (cost=0.00..142.52 rows=1652 width=782) (actual time=0.015..0.627 rows=1680 loops=1)
         Buffers: shared hit=126
 Planning Time: 0.193 ms
 Execution Time: 3.908 ms
(8 rows)

feike=# set work_mem to '64kB';
SET
feike=# EXPLAIN (ANALYZE ON, BUFFERS ON) SELECT * FROM pg_class ORDER BY relfilenode;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Sort  (cost=1371.82..1375.95 rows=1652 width=782) (actual time=6.675..8.102 rows=1680 loops=1)
   Sort Key: relfilenode
   Sort Method: external merge  Disk: 832kB
   Buffers: shared hit=126, temp read=239 written=268
   ->  Seq Scan on pg_class  (cost=0.00..142.52 rows=1652 width=782) (actual time=0.015..0.654 rows=1680 loops=1)
         Buffers: shared hit=126
 Planning Time: 0.192 ms
 Execution Time: 8.993 ms
(8 rows)

计划的主要区别在于:

   Sort Method: external merge  Disk: 832kB
   Sort Method: quicksort  Memory: 1246kB

确定 work_mem 是否有用的最佳方法是:

EXPLAIN (ANALYZE ON, BUFFERS ON)
    SELECT
        time_bucket_gapfill('1 day', time, date_trunc('day', now() - interval '30 days'), date_trunc('day', now())) AS one_day,
        country,
        type_id,
        min(measurement)
    FROM hypertable
    WHERE
        entity_id='XYZ' AND
        country='US' AND
        time > time_bucket('1 day', now() - interval '30 days') AND 
        time < time_bucket('1 day', now())
    GROUP BY one_day, country, type_id

并查找任何磁盘操作。

行看起来像这样:

Buffers: shared hit=96 read=44152

通知您有关 shared_buffers 的信息,它找到所需内容的次数 (hit) 以及它必须多久从磁盘中获取一些内容 (read)。

答案有点太长,但要点是您需要根据工作负载调整 PostgreSQL 实例的大小; PostgreSQL 的默认值(非常)保守,这使得它几乎可以在任何地方 运行 。然而,您似乎想认真使用数据库,因此需要进行一些调整。

一些工具可以帮助您做到这一点: