Loading...
Loading...
Compare original and translation side by side
| Pattern | Examples |
|---|---|
| migrations/create_hypertable.sql |
| models/timeseries.ts |
| services/metricsService.ts |
| db/events.sql |
| tables/logs.sql |
| iot/sensor_data.sql |
| views/hourly_stats.sql |
| policies/compression.sql |
-- These patterns trigger this skill
create_hypertable
continuous aggregate
compress_chunk
add_compression_policy| 模式 | 示例 |
|---|---|
| migrations/create_hypertable.sql |
| models/timeseries.ts |
| services/metricsService.ts |
| db/events.sql |
| tables/logs.sql |
| iot/sensor_data.sql |
| views/hourly_stats.sql |
| policies/compression.sql |
-- 这些模式会触发本指南的应用
create_hypertable
continuous aggregate
compress_chunk
add_compression_policyrecompress := true-- OLD (2.23 and earlier): Decompress entire chunk, update, recompress
-- Could take minutes for large chunks
-- NEW (2.24.0): Update compressed data directly
UPDATE sensor_data
SET value = corrected_value
WHERE time BETWEEN '2026-01-01' AND '2026-01-02';
-- 100x faster for compressed chunks
-- Enable recompression mode (automatic in 2.24.0)
-- Updates to compressed chunks now:
-- 1. Identify affected segments
-- 2. Decompress only those segments
-- 3. Apply updates
-- 4. Recompress immediately
-- Verify recompression is happening
SELECT * FROM timescaledb_information.job_stats
WHERE job_id IN (
SELECT job_id FROM timescaledb_information.jobs
WHERE proc_name = 'policy_recompression'
);recompress := true-- 旧版本(2.23及更早):解压缩整个数据块,更新后重新压缩
-- 对于大数据块可能需要数分钟
-- 新版本(2.24.0):直接更新压缩数据
UPDATE sensor_data
SET value = corrected_value
WHERE time BETWEEN '2026-01-01' AND '2026-01-02';
-- 压缩数据块的更新速度提升100倍
-- 启用重压缩模式(2.24.0中默认自动启用)
-- 现在对压缩数据块的更新流程:
-- 1. 识别受影响的分段
-- 2. 仅解压缩这些分段
-- 3. 应用更新
-- 4. 立即重新压缩
-- 验证重压缩是否正在运行
SELECT * FROM timescaledb_information.job_stats
WHERE job_id IN (
SELECT job_id FROM timescaledb_information.jobs
WHERE proc_name = 'policy_recompression'
);-- Create continuous aggregate with direct compression
CREATE MATERIALIZED VIEW hourly_metrics
WITH (timescaledb.continuous, timescaledb.compress = true) AS
SELECT
time_bucket('1 hour', time) AS bucket,
device_id,
avg(temperature) AS avg_temp,
min(temperature) AS min_temp,
max(temperature) AS max_temp,
count(*) AS sample_count
FROM sensor_readings
GROUP BY bucket, device_id
WITH NO DATA;
-- Add compression policy directly to continuous aggregate
SELECT add_compression_policy('hourly_metrics', INTERVAL '7 days');
-- Refresh policy
SELECT add_continuous_aggregate_policy('hourly_metrics',
start_offset => INTERVAL '1 month',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour'
);-- 创建带直接压缩的连续聚合
CREATE MATERIALIZED VIEW hourly_metrics
WITH (timescaledb.continuous, timescaledb.compress = true) AS
SELECT
time_bucket('1 hour', time) AS bucket,
device_id,
avg(temperature) AS avg_temp,
min(temperature) AS min_temp,
max(temperature) AS max_temp,
count(*) AS sample_count
FROM sensor_readings
GROUP BY bucket, device_id
WITH NO DATA;
-- 直接为连续聚合添加压缩策略
SELECT add_compression_policy('hourly_metrics', INTERVAL '7 days');
-- 添加刷新策略
SELECT add_continuous_aggregate_policy('hourly_metrics',
start_offset => INTERVAL '1 month',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour'
);-- Hypertable with UUIDv7 primary key (PostgreSQL 18)
CREATE TABLE events (
id uuid DEFAULT uuidv7(),
time timestamptz NOT NULL,
event_type text NOT NULL,
payload jsonb,
PRIMARY KEY (id, time)
);
SELECT create_hypertable('events', 'time');
-- Continuous aggregate can now reference UUIDv7 columns
CREATE MATERIALIZED VIEW event_counts
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS bucket,
event_type,
count(*) AS event_count,
count(DISTINCT id) AS unique_events -- UUIDv7 works here now
FROM events
GROUP BY bucket, event_type
WITH NO DATA;-- 带UUIDv7主键的Hypertable(PostgreSQL 18)
CREATE TABLE events (
id uuid DEFAULT uuidv7(),
time timestamptz NOT NULL,
event_type text NOT NULL,
payload jsonb,
PRIMARY KEY (id, time)
);
SELECT create_hypertable('events', 'time');
-- 连续聚合现在可以引用UUIDv7列
CREATE MATERIALIZED VIEW event_counts
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS bucket,
event_type,
count(*) AS event_count,
count(DISTINCT id) AS unique_events -- UUIDv7现在可在此处正常使用
FROM events
GROUP BY bucket, event_type
WITH NO DATA;-- Bloom filters for sparse data patterns
-- Useful for columns with many NULLs or low cardinality
CREATE TABLE logs (
time timestamptz NOT NULL,
level text,
message text,
error_code text, -- Often NULL, sparse
trace_id uuid -- Often NULL, sparse
);
SELECT create_hypertable('logs', 'time');
-- Configure compression with bloom filter for sparse columns
ALTER TABLE logs SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'level',
timescaledb.compress_orderby = 'time DESC',
-- Bloom filter helps find rare non-NULL values
timescaledb.compress_bloomfilter = 'error_code, trace_id'
);
-- Query efficiency: Bloom filter skips segments without matches
SELECT * FROM logs
WHERE error_code = 'E500'
AND time > now() - INTERVAL '1 day';
-- Scans only segments where bloom filter indicates possible match-- 针对稀疏数据模式的布隆过滤器
-- 适用于包含大量NULL值或低基数的列
CREATE TABLE logs (
time timestamptz NOT NULL,
level text,
message text,
error_code text, -- 通常为NULL,属于稀疏列
trace_id uuid -- 通常为NULL,属于稀疏列
);
SELECT create_hypertable('logs', 'time');
-- 为稀疏列配置带布隆过滤器的压缩
ALTER TABLE logs SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'level',
timescaledb.compress_orderby = 'time DESC',
-- 布隆过滤器有助于查找罕见的非NULL值
timescaledb.compress_bloomfilter = 'error_code, trace_id'
);
-- 查询效率:布隆过滤器会跳过无匹配的分段
SELECT * FROM logs
WHERE error_code = 'E500'
AND time > now() - INTERVAL '1 day';
-- 仅扫描布隆过滤器指示可能存在匹配的分段-- Standard time-series table
CREATE TABLE metrics (
time timestamptz NOT NULL,
device_id uuid NOT NULL,
metric_name text NOT NULL,
value double precision,
metadata jsonb DEFAULT '{}'
);
-- Convert to hypertable
SELECT create_hypertable('metrics', 'time',
chunk_time_interval => INTERVAL '1 day', -- Chunk size
create_default_indexes => true
);
-- With space partitioning (for high-cardinality dimensions)
SELECT create_hypertable('metrics', 'time',
partitioning_column => 'device_id',
number_partitions => 4,
chunk_time_interval => INTERVAL '1 day'
);-- 标准时间序列表
CREATE TABLE metrics (
time timestamptz NOT NULL,
device_id uuid NOT NULL,
metric_name text NOT NULL,
value double precision,
metadata jsonb DEFAULT '{}'
);
-- 转换为Hypertable
SELECT create_hypertable('metrics', 'time',
chunk_time_interval => INTERVAL '1 day', -- 数据块大小
create_default_indexes => true
);
-- 带空间分区(针对高基数维度)
SELECT create_hypertable('metrics', 'time',
partitioning_column => 'device_id',
number_partitions => 4,
chunk_time_interval => INTERVAL '1 day'
);| Data Volume | Suggested Interval | Rationale |
|---|---|---|
| < 1GB/day | 1 week | Fewer chunks, simpler management |
| 1-10 GB/day | 1 day | Balance between size and granularity |
| 10-100 GB/day | 6 hours | Faster compression, better parallelism |
| > 100 GB/day | 1 hour | Maximum parallelism, fast drops |
-- Adjust chunk interval
SELECT set_chunk_time_interval('metrics', INTERVAL '6 hours');
-- View current chunks
SELECT show_chunks('metrics', older_than => INTERVAL '1 day');| 数据量 | 建议间隔 | 理由 |
|---|---|---|
| < 1GB/天 | 1周 | 数据块数量更少,管理更简单 |
| 1-10 GB/天 | 1天 | 在大小和粒度之间取得平衡 |
| 10-100 GB/天 | 6小时 | 压缩速度更快,并行性更好 |
| > 100 GB/天 | 1小时 | 最大化并行性,快速删除旧数据 |
-- 调整数据块间隔
SELECT set_chunk_time_interval('metrics', INTERVAL '6 hours');
-- 查看当前数据块
SELECT show_chunks('metrics', older_than => INTERVAL '1 day');-- CORRECT: Time column in primary key for efficient chunk pruning
CREATE TABLE events (
id uuid DEFAULT uuidv7(),
time timestamptz NOT NULL,
event_type text NOT NULL,
PRIMARY KEY (id, time) -- time included
);
-- WRONG: Time not in primary key (inefficient queries)
CREATE TABLE events_bad (
id uuid PRIMARY KEY DEFAULT uuidv7(),
time timestamptz NOT NULL -- Not in PK
);-- 正确做法:主键中包含时间列,以实现高效的数据块裁剪
CREATE TABLE events (
id uuid DEFAULT uuidv7(),
time timestamptz NOT NULL,
event_type text NOT NULL,
PRIMARY KEY (id, time) -- 包含time列
);
-- 错误做法:主键中不包含时间列(查询效率低)
CREATE TABLE events_bad (
id uuid PRIMARY KEY DEFAULT uuidv7(),
time timestamptz NOT NULL -- 不在主键中
);-- Configure compression
ALTER TABLE metrics SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'device_id', -- Group by this
timescaledb.compress_orderby = 'time DESC', -- Sort order
timescaledb.compress_chunk_time_interval = '1 day' -- Recompress interval
);
-- Manual compression
SELECT compress_chunk(c)
FROM show_chunks('metrics', older_than => INTERVAL '7 days') c;
-- Automatic compression policy
SELECT add_compression_policy('metrics', INTERVAL '7 days');-- 配置压缩
ALTER TABLE metrics SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'device_id', -- 按此字段分组
timescaledb.compress_orderby = 'time DESC', -- 排序方式
timescaledb.compress_chunk_time_interval = '1 day' -- 重压缩间隔
);
-- 手动压缩
SELECT compress_chunk(c)
FROM show_chunks('metrics', older_than => INTERVAL '7 days') c;
-- 自动压缩策略
SELECT add_compression_policy('metrics', INTERVAL '7 days');-- GOOD: Segment by commonly filtered dimension
-- Queries filter on device_id get excellent performance
ALTER TABLE metrics SET (
timescaledb.compress_segmentby = 'device_id'
);
-- GOOD: Multiple segment columns for flexible queries
ALTER TABLE metrics SET (
timescaledb.compress_segmentby = 'device_id, metric_name'
);
-- BAD: High cardinality segment (too many segments)
-- Don't segment by user_id if you have millions of users
ALTER TABLE events SET (
timescaledb.compress_segmentby = 'user_id' -- Too many segments!
);
-- BETTER for high cardinality: Include in orderby instead
ALTER TABLE events SET (
timescaledb.compress_segmentby = 'event_type',
timescaledb.compress_orderby = 'user_id, time DESC'
);-- 推荐:按常用过滤维度进行分段
-- 按device_id过滤的查询将获得极佳性能
ALTER TABLE metrics SET (
timescaledb.compress_segmentby = 'device_id'
);
-- 推荐:多列分段以支持灵活查询
ALTER TABLE metrics SET (
timescaledb.compress_segmentby = 'device_id, metric_name'
);
-- 不推荐:高基数分段(分段数量过多)
-- 如果你有数百万用户,不要按user_id分段
ALTER TABLE events SET (
timescaledb.compress_segmentby = 'user_id' -- 分段数量过多!
);
-- 针对高基数的更好方案:改为在orderby中包含该列
ALTER TABLE events SET (
timescaledb.compress_segmentby = 'event_type',
timescaledb.compress_orderby = 'user_id, time DESC'
);-- Time descending for "most recent" queries
ALTER TABLE metrics SET (
timescaledb.compress_orderby = 'time DESC'
);
-- Composite order for specific query patterns
ALTER TABLE logs SET (
timescaledb.compress_orderby = 'level, time DESC'
);
-- Benefits: WHERE level = 'error' ORDER BY time DESC
-- Include frequently filtered columns
ALTER TABLE events SET (
timescaledb.compress_orderby = 'device_id, time DESC'
);-- 按时间降序排列,适用于“最新数据”查询
ALTER TABLE metrics SET (
timescaledb.compress_orderby = 'time DESC'
);
-- 复合排序,适用于特定查询模式
ALTER TABLE logs SET (
timescaledb.compress_orderby = 'level, time DESC'
);
-- 优势:优化WHERE level = 'error' ORDER BY time DESC这类查询
-- 包含常用过滤列
ALTER TABLE events SET (
timescaledb.compress_orderby = 'device_id, time DESC'
);-- Basic continuous aggregate
CREATE MATERIALIZED VIEW hourly_stats
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS bucket,
device_id,
avg(value) AS avg_value,
min(value) AS min_value,
max(value) AS max_value,
count(*) AS sample_count
FROM metrics
GROUP BY bucket, device_id
WITH NO DATA;
-- Hierarchical aggregates (aggregate of aggregate)
CREATE MATERIALIZED VIEW daily_stats
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 day', bucket) AS bucket,
device_id,
avg(avg_value) AS avg_value,
min(min_value) AS min_value,
max(max_value) AS max_value,
sum(sample_count) AS sample_count
FROM hourly_stats
GROUP BY 1, device_id
WITH NO DATA;-- 基础连续聚合
CREATE MATERIALIZED VIEW hourly_stats
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS bucket,
device_id,
avg(value) AS avg_value,
min(value) AS min_value,
max(value) AS max_value,
count(*) AS sample_count
FROM metrics
GROUP BY bucket, device_id
WITH NO DATA;
-- 分层聚合(聚合的聚合)
CREATE MATERIALIZED VIEW daily_stats
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 day', bucket) AS bucket,
device_id,
avg(avg_value) AS avg_value,
min(min_value) AS min_value,
max(max_value) AS max_value,
sum(sample_count) AS sample_count
FROM hourly_stats
GROUP BY 1, device_id
WITH NO DATA;-- Add refresh policy
SELECT add_continuous_aggregate_policy('hourly_stats',
start_offset => INTERVAL '3 days', -- Refresh this far back
end_offset => INTERVAL '1 hour', -- Don't refresh latest (incomplete)
schedule_interval => INTERVAL '1 hour'
);
-- Real-time aggregates (include unrefreshed data)
ALTER MATERIALIZED VIEW hourly_stats SET (
timescaledb.materialized_only = false -- Include real-time data
);
-- Force refresh
CALL refresh_continuous_aggregate('hourly_stats',
'2026-01-01'::timestamptz,
'2026-01-02'::timestamptz
);-- 添加刷新策略
SELECT add_continuous_aggregate_policy('hourly_stats',
start_offset => INTERVAL '3 days', -- 刷新此时间范围之前的数据
end_offset => INTERVAL '1 hour', -- 不刷新最新的数据(未完成)
schedule_interval => INTERVAL '1 hour'
);
-- 实时聚合(包含未刷新的数据)
ALTER MATERIALIZED VIEW hourly_stats SET (
timescaledb.materialized_only = false -- 包含实时数据
);
-- 强制刷新
CALL refresh_continuous_aggregate('hourly_stats',
'2026-01-01'::timestamptz,
'2026-01-02'::timestamptz
);-- Continuous aggregate with built-in compression
CREATE MATERIALIZED VIEW hourly_metrics
WITH (
timescaledb.continuous,
timescaledb.compress = true -- New in 2.24.0
) AS
SELECT
time_bucket('1 hour', time) AS bucket,
device_id,
avg(temperature) AS avg_temp,
percentile_agg(temperature) AS temp_pct -- For percentiles later
FROM sensor_readings
GROUP BY bucket, device_id
WITH NO DATA;
-- Add compression policy for the aggregate
SELECT add_compression_policy('hourly_metrics', INTERVAL '30 days');
-- Combined with refresh policy
SELECT add_continuous_aggregate_policy('hourly_metrics',
start_offset => INTERVAL '7 days',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour'
);-- 内置压缩的连续聚合
CREATE MATERIALIZED VIEW hourly_metrics
WITH (
timescaledb.continuous,
timescaledb.compress = true -- 2.24.0新特性
) AS
SELECT
time_bucket('1 hour', time) AS bucket,
device_id,
avg(temperature) AS avg_temp,
percentile_agg(temperature) AS temp_pct -- 后续用于计算百分位数
FROM sensor_readings
GROUP BY bucket, device_id
WITH NO DATA;
-- 为聚合添加压缩策略
SELECT add_compression_policy('hourly_metrics', INTERVAL '30 days');
-- 结合刷新策略
SELECT add_continuous_aggregate_policy('hourly_metrics',
start_offset => INTERVAL '7 days',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour'
);-- Drop old raw data (keep aggregates)
SELECT add_retention_policy('metrics', INTERVAL '90 days');
-- View retention policies
SELECT * FROM timescaledb_information.jobs
WHERE proc_name = 'policy_retention';
-- Remove retention policy
SELECT remove_retention_policy('metrics');-- 删除旧的原始数据(保留聚合数据)
SELECT add_retention_policy('metrics', INTERVAL '90 days');
-- 查看保留策略
SELECT * FROM timescaledb_information.jobs
WHERE proc_name = 'policy_retention';
-- 删除保留策略
SELECT remove_retention_policy('metrics');-- Pattern: Raw → Hourly → Daily → Archive
-- 1. Raw data: Keep 7 days uncompressed
-- 2. Raw data: Keep 30 days compressed
-- 3. Raw data: Drop after 90 days
-- 4. Hourly aggregates: Keep 1 year
-- 5. Daily aggregates: Keep forever
-- Implementation:
-- Raw data policies
SELECT add_compression_policy('metrics', INTERVAL '7 days');
SELECT add_retention_policy('metrics', INTERVAL '90 days');
-- Hourly aggregate policies
SELECT add_compression_policy('hourly_stats', INTERVAL '30 days');
SELECT add_retention_policy('hourly_stats', INTERVAL '1 year');
-- Daily stats: No retention (keep forever)
SELECT add_compression_policy('daily_stats', INTERVAL '90 days');-- 模式:原始数据 → 小时级聚合 → 天级聚合 → 归档
-- 1. 原始数据:保留7天未压缩数据
-- 2. 原始数据:保留30天压缩数据
-- 3. 原始数据:90天后删除
-- 4. 小时级聚合:保留1年
-- 5. 天级聚合:永久保留
-- 实现方式:
-- 原始数据策略
SELECT add_compression_policy('metrics', INTERVAL '7 days');
SELECT add_retention_policy('metrics', INTERVAL '90 days');
-- 小时级聚合策略
SELECT add_compression_policy('hourly_stats', INTERVAL '30 days');
SELECT add_retention_policy('hourly_stats', INTERVAL '1 year');
-- 天级聚合:不设置保留策略(永久保留)
SELECT add_compression_policy('daily_stats', INTERVAL '90 days');-- Recent data (uses index)
SELECT * FROM metrics
WHERE time > now() - INTERVAL '1 hour'
AND device_id = $1
ORDER BY time DESC
LIMIT 100;
-- Time range with aggregation
SELECT
time_bucket('5 minutes', time) AS bucket,
avg(value) AS avg_value
FROM metrics
WHERE time BETWEEN $1 AND $2
AND device_id = $3
GROUP BY bucket
ORDER BY bucket;
-- Last value per device
SELECT DISTINCT ON (device_id)
device_id,
time,
value
FROM metrics
WHERE time > now() - INTERVAL '1 day'
ORDER BY device_id, time DESC;-- 近期数据(使用索引)
SELECT * FROM metrics
WHERE time > now() - INTERVAL '1 hour'
AND device_id = $1
ORDER BY time DESC
LIMIT 100;
-- 带聚合的时间范围查询
SELECT
time_bucket('5 minutes', time) AS bucket,
avg(value) AS avg_value
FROM metrics
WHERE time BETWEEN $1 AND $2
AND device_id = $3
GROUP BY bucket
ORDER BY bucket;
-- 每个设备的最新值
SELECT DISTINCT ON (device_id)
device_id,
time,
value
FROM metrics
WHERE time > now() - INTERVAL '1 day'
ORDER BY device_id, time DESC;-- Query aggregate instead of raw data
SELECT * FROM hourly_stats
WHERE bucket > now() - INTERVAL '7 days'
AND device_id = $1
ORDER BY bucket DESC;
-- Real-time aggregate (includes unrefreshed data)
SELECT * FROM hourly_stats
WHERE bucket > now() - INTERVAL '1 hour';
-- Automatically combines materialized + real-time data-- 查询聚合数据而非原始数据
SELECT * FROM hourly_stats
WHERE bucket > now() - INTERVAL '7 days'
AND device_id = $1
ORDER BY bucket DESC;
-- 实时聚合查询(包含未刷新的数据)
SELECT * FROM hourly_stats
WHERE bucket > now() - INTERVAL '1 hour';
-- 自动结合物化数据和实时数据-- Use percentile_agg for continuous aggregates
CREATE MATERIALIZED VIEW metrics_percentiles
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS bucket,
device_id,
percentile_agg(value) AS value_pct, -- Aggregate percentile state
stats_agg(value) AS value_stats -- Statistical aggregates
FROM metrics
GROUP BY bucket, device_id;
-- Query percentiles from aggregate
SELECT
bucket,
device_id,
approx_percentile(0.50, value_pct) AS median,
approx_percentile(0.95, value_pct) AS p95,
approx_percentile(0.99, value_pct) AS p99,
average(value_stats) AS avg,
stddev(value_stats) AS stddev
FROM metrics_percentiles
WHERE bucket > now() - INTERVAL '24 hours';-- 使用percentile_agg实现连续聚合的百分位数计算
CREATE MATERIALIZED VIEW metrics_percentiles
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS bucket,
device_id,
percentile_agg(value) AS value_pct, -- 聚合百分位数状态
stats_agg(value) AS value_stats -- 统计聚合
FROM metrics
GROUP BY bucket, device_id;
-- 从聚合数据中查询百分位数
SELECT
bucket,
device_id,
approx_percentile(0.50, value_pct) AS median,
approx_percentile(0.95, value_pct) AS p95,
approx_percentile(0.99, value_pct) AS p99,
average(value_stats) AS avg,
stddev(value_stats) AS stddev
FROM metrics_percentiles
WHERE bucket > now() - INTERVAL '24 hours';-- create_hypertable creates this by default:
-- CREATE INDEX ON metrics (time DESC);
-- Add composite indexes for common queries
CREATE INDEX idx_metrics_device_time ON metrics (device_id, time DESC);
-- Partial indexes for specific patterns
CREATE INDEX idx_metrics_errors ON metrics (time DESC)
WHERE value > threshold;-- create_hypertable默认会创建以下索引:
-- CREATE INDEX ON metrics (time DESC);
-- 为常见查询添加复合索引
CREATE INDEX idx_metrics_device_time ON metrics (device_id, time DESC);
-- 为特定模式添加部分索引
CREATE INDEX idx_metrics_errors ON metrics (time DESC)
WHERE value > threshold;-- Indexes are not used on compressed chunks
-- Query planner uses:
-- 1. Chunk exclusion (time range)
-- 2. Segment filtering (compress_segmentby columns)
-- 3. Orderby optimization (compress_orderby columns)
-- Design compression settings for query patterns, not indexes
ALTER TABLE metrics SET (
timescaledb.compress_segmentby = 'device_id', -- Filter column
timescaledb.compress_orderby = 'time DESC' -- Sort column
);-- 索引在压缩数据块上不生效
-- 查询优化器会使用:
-- 1. 数据块排除(时间范围)
-- 2. 分段过滤(compress_segmentby列)
-- 3. 排序优化(compress_orderby列)
-- 针对查询模式设计压缩设置,而非依赖索引
ALTER TABLE metrics SET (
timescaledb.compress_segmentby = 'device_id', -- 过滤列
timescaledb.compress_orderby = 'time DESC' -- 排序列
);-- 1. Ensure time column exists and is NOT NULL
ALTER TABLE legacy_metrics ALTER COLUMN time SET NOT NULL;
-- 2. Convert to hypertable
SELECT create_hypertable('legacy_metrics', 'time',
migrate_data => true,
chunk_time_interval => INTERVAL '1 day'
);
-- 3. Add compression
ALTER TABLE legacy_metrics SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'device_id',
timescaledb.compress_orderby = 'time DESC'
);
-- 4. Add policies
SELECT add_compression_policy('legacy_metrics', INTERVAL '7 days');
SELECT add_retention_policy('legacy_metrics', INTERVAL '90 days');-- 1. 确保时间列存在且不为NULL
ALTER TABLE legacy_metrics ALTER COLUMN time SET NOT NULL;
-- 2. 转换为Hypertable
SELECT create_hypertable('legacy_metrics', 'time',
migrate_data => true,
chunk_time_interval => INTERVAL '1 day'
);
-- 3. 添加压缩配置
ALTER TABLE legacy_metrics SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'device_id',
timescaledb.compress_orderby = 'time DESC'
);
-- 4. 添加策略
SELECT add_compression_policy('legacy_metrics', INTERVAL '7 days');
SELECT add_retention_policy('legacy_metrics', INTERVAL '90 days');-- 1. Install extension
CREATE EXTENSION IF NOT EXISTS timescaledb;
-- 2. Verify version
SELECT extversion FROM pg_extension WHERE extname = 'timescaledb';
-- Should show 2.24.0
-- 3. Check PostgreSQL compatibility
SELECT timescaledb_information.version();-- 1. 安装扩展
CREATE EXTENSION IF NOT EXISTS timescaledb;
-- 2. 验证版本
SELECT extversion FROM pg_extension WHERE extname = 'timescaledb';
-- 应显示2.24.0
-- 3. 检查PostgreSQL兼容性
SELECT timescaledb_information.version();<!-- TIMESCALEDB_IMPLEMENTATION:START --><!-- TIMESCALEDB_IMPLEMENTATION:START -->| Table | Chunk Interval | Space Partitions | Compression |
|---|---|---|---|
| metrics | 1 day | device_id (4) | Yes |
| events | 6 hours | None | Yes |
| logs | 1 hour | level (2) | Yes |
| 表名 | 数据块间隔 | 空间分区 | 压缩 |
|---|---|---|---|
| metrics | 1天 | device_id(4个分区) | 是 |
| events | 6小时 | 无 | 是 |
| logs | 1小时 | level(2个分区) | 是 |
| Table | Segment By | Order By | Bloom Filter |
|---|---|---|---|
| metrics | device_id | time DESC | None |
| logs | level | time DESC | error_code, trace_id |
| 表名 | Segment By | Order By | 布隆过滤器 |
|---|---|---|---|
| metrics | device_id | time DESC | 无 |
| logs | level | time DESC | error_code, trace_id |
| Aggregate | Source | Interval | Compression |
|---|---|---|---|
| hourly_metrics | metrics | 1 hour | Yes (30d) |
| daily_metrics | hourly_metrics | 1 day | Yes (90d) |
| 聚合名称 | 数据源 | 时间间隔 | 压缩 |
|---|---|---|---|
| hourly_metrics | metrics | 1小时 | 是(30天后压缩) |
| daily_metrics | hourly_metrics | 1天 | 是(90天后压缩) |
| Table/Aggregate | Compression | Retention | Refresh |
|---|---|---|---|
| metrics | 7 days | 90 days | N/A |
| hourly_metrics | 30 days | 1 year | 1 hour |
| daily_metrics | 90 days | Never | 1 day |
| 表/聚合 | 压缩策略 | 保留策略 | 刷新策略 |
|---|---|---|---|
| metrics | 7天后压缩 | 90天后删除 | 无 |
| hourly_metrics | 30天后压缩 | 1年后删除 | 每小时刷新 |
| daily_metrics | 90天后压缩 | 永久保留 | 每天刷新 |
undefinedundefineddatabase-architecturepostgres-rlspostgisdatabase-architecturepostgres-rlspostgis