Timescale Cloud:性能、规模、企业级

自托管产品

MST

本页面包含来自 TimescaleDB 社区 关于如何解决常见问题的建议。使用这些代码示例作为指导,处理您自己的数据。

要遵循本页的步骤

本节包含关于超表的秘籍。

想要从现有超表中移除重复项?一种方法是运行 `PARTITION BY` 查询以获取 `ROW_NUMBER()`,然后获取 `row_number>1` 的行的 `ctid`。然后删除这些行。但是,您需要检查 `tableoid` 和 `ctid`。这是因为 `ctid` 并非唯一,并且可能在不同的块中重复。以下代码示例处理一个包含 4000 万行的表耗时 17 小时

CREATE OR REPLACE FUNCTION deduplicate_chunks(ht_name TEXT, partition_columns TEXT, bot_id INT DEFAULT NULL)
RETURNS TABLE
(
chunk_schema name,
chunk_name name,
deleted_count INT
)
AS
$$
DECLARE
chunk RECORD;
where_clause TEXT := '';
deleted_count INT;
BEGIN
IF bot_id IS NOT NULL THEN
where_clause := FORMAT('WHERE bot_id = %s', bot_id);
END IF;
FOR chunk IN
SELECT c.chunk_schema, c.chunk_name
FROM timescaledb_information.chunks c
WHERE c.hypertable_name = ht_name
LOOP
EXECUTE FORMAT('
WITH cte AS (
SELECT ctid,
ROW_NUMBER() OVER (PARTITION BY %s ORDER BY %s ASC) AS row_num,
*
FROM %I.%I
%s
)
DELETE FROM %I.%I
WHERE ctid IN (
SELECT ctid
FROM cte
WHERE row_num > 1
)
RETURNING 1;
', partition_columns, partition_columns, chunk.chunk_schema, chunk.chunk_name, where_clause, chunk.chunk_schema,
chunk.chunk_name)
INTO deleted_count;
RETURN QUERY SELECT chunk.chunk_schema, chunk.chunk_name, COALESCE(deleted_count, 0);
END LOOP;
END
$$ LANGUAGE plpgsql;
SELECT *
FROM deduplicate_chunks('nudge_events', 'bot_id, session_id, nudge_id, time', 2540);

特别感谢 **Mathias Ose** 和 **Christopher Piggott** 提供此秘籍。

假设有一个查询,它通过共享键将一个超表连接到另一个表

SELECT timestamp,
FROM hypertable as h
JOIN related_table as rt
ON rt.id = h.related_table_id
WHERE h.timestamp BETWEEN '2024-10-10 00:00:00' AND '2024-10-17 00:00:00'

如果您对此查询运行 `EXPLAIN`,您会看到查询规划器在这两个表之间执行 `NestedJoin`,这意味着多次查询超表。即使超表索引良好,如果它也很庞大,查询也会很慢。如何强制只执行一次查找?使用具象化的公共表表达式 (CTE)。

如果您使用 CTE 将查询分成两部分,您可以具象化超表查找并强制 PostgreSQL 只执行一次。

WITH cached_query AS materialized (
SELECT *
FROM hypertable
WHERE BETWEEN '2024-10-10 00:00:00' AND '2024-10-17 00:00:00'
)
SELECT *
FROM cached_query as c
JOIN related_table as rt
ON rt.id = h.related_table_id

现在,如果您再次运行 `EXPLAIN`,您会看到此查询只执行一次查找。根据您的超表大小,这可能会使一个耗时数小时的查询缩短到仅仅几秒钟。

特别感谢 **Rowan Molony** 提供此秘籍。

本节包含解决物联网问题的秘籍

窄表和中等宽度表是存储物联网数据的好方法。许多原因在《设计您的数据库模式:PostgreSQL 的宽表 vs. 窄表》中都有阐述。

窄表的主要优点之一是,当您添加新传感器时,模式不必更改。另一个重要优点是每个传感器可以以不同的速率和时间进行采样。这有助于支持诸如滞后现象,即除非值变化达到一定量,否则很少写入新值。

使用窄表数据结构会带来一些挑战。在物联网领域,一个问题是许多数据分析方法——包括机器学习和更传统的数据分析——都要求您的数据被重新采样并同步到一个共同的时间基准。幸运的是,TimescaleDB 为您提供了超函数和其他工具来帮助您处理这些数据。

一个窄表格式的示例如下

tssensor_idvalue
2024-10-31 11:17:30.000100723.45

通常,您可以将其与一个传感器表结合使用

sensor_idsensor_nameunits
1007温度摄氏度
1012加热模式开/关
1013冷却模式开/关
1041占用率房间人数

中等宽度的表保留了通用结构,但添加了各种类型的列,以便您可以使用同一张表存储浮点数、整数、布尔值甚至 JSON (jsonb) 数据

tssensor_iddibtj
2024-10-31 11:17:30.000100723.45nullnullnullnull
2024-10-31 11:17:47.0001012nullnullTRUEnullnull
2024-10-31 11:18:01.0001041null4nullnullnull

要移除所有空值条目,请使用可选约束,例如

CONSTRAINT at_least_one_not_null
CHECK ((d IS NOT NULL) OR (i IS NOT NULL) OR (b IS NOT NULL) OR (j IS NOT NULL) OR (t IS NOT NULL))

有几种方法可以获取每个传感器的最新值。以下示例使用窄表格式示例中定义的结构作为参考

`SELECT DISTINCT ON`

如果您有一个传感器列表,获取每个传感器最新值的简单方法是使用 `SELECT DISTINCT ON`

WITH latest_data AS (
SELECT DISTINCT ON (sensor_id) ts, sensor_id, d
FROM iot_data
WHERE d is not null
AND ts > CURRENT_TIMESTAMP - INTERVAL '1 week' -- important
ORDER BY sensor_id, ts DESC
)
SELECT
sensor_id, sensors.name, ts, d
FROM latest_data
LEFT OUTER JOIN sensors ON latest_data.sensor_id = sensors.id
WHERE latest_data.d is not null
ORDER BY sensor_id, ts; -- Optional, for displaying results ordered by sensor_id

上面使用的公共表表达式 (CTE) 并非严格必要。但是,它是一种优雅的方式来连接传感器列表,以在输出中获取传感器名称。如果您不关心这一点,可以将其省略。

SELECT DISTINCT ON (sensor_id) ts, sensor_id, d
FROM iot_data
WHERE d is not null
AND ts > CURRENT_TIMESTAMP - INTERVAL '1 week' -- important
ORDER BY sensor_id, ts DESC

在向下选择此数据时,务必小心。在前面的示例中,查询回溯的时间是有限的。但是,如果有些传感器长时间未报告,或者在最坏的情况下从未报告,则此查询将退化为全表扫描。
在一个拥有 1000 多个传感器和 4100 万行的数据库中,不受约束的查询需要一个多小时。

`SELECT DISTINCT ON` 的一个替代方法是使用 `JOIN LATERAL`。通过从传感器表中选择整个传感器列表,而不是使用 `SELECT DISTINCT` 提取 ID,`JOIN LATERAL` 可以在性能方面提供一些改进。

SELECT sensor_list.id, latest_data.ts, latest_data.d
FROM sensors sensor_list
-- Add a WHERE clause here to downselect the sensor list, if you wish
LEFT JOIN LATERAL (
SELECT ts, d
FROM iot_data raw_data
WHERE sensor_id = sensor_list.id
ORDER BY ts DESC
LIMIT 1
) latest_data ON true
WHERE latest_data.d is not null -- only pulling out float values ("d" column) in this example
AND latest_data.ts > CURRENT_TIMESTAMP - interval '1 week' -- important
ORDER BY sensor_list.id, latest_data.ts;

限制时间范围很重要,特别是当您拥有大量数据时。最佳实践是将这类查询用于仪表板和快速状态检查。要在更大的时间范围内进行查询,请将前面的示例封装到一个不经常刷新(例如每天一次)的具象化查询中。

特别感谢 **Christopher Piggott** 提供此秘籍。

在本页面上发现问题?报告问题 或 在 GitHub 上编辑此页面