TimescaleDB 2.29.0 已发布。相较 2.28.3,此版本集中带来了性能改进和错误修复,其中最值得高并发时序业务关注的变化,是 DML 操作现在可以利用 Chunk exclusion,减少超表执行 UPDATE 和 DELETE 时需要触及的 Chunk 及其锁范围。官方建议用户尽快升级,但生产环境仍应先完成兼容性检查和基准测试。
为什么超表上的 DML 容易引发锁竞争
TimescaleDB 的超表在逻辑上是一张表,底层数据则按时间等维度分布到多个 Chunk。查询历史指标时,这种结构允许数据库跳过不相关的 Chunk;但在以往的某些 DML 场景中,即使一条语句只修改很窄的时间范围,也可能产生覆盖面较大的锁操作。
2.29.0 将 Chunk exclusion 应用于 DML。对于带有明确过滤条件的 UPDATE 或 DELETE,数据库可以只处理实际可能包含目标行的 Chunk,并仅对正在修改的特定块获取独占锁,而不是让锁操作波及整个超表。
这项优化主要带来两类收益:
- 缩短执行路径:跳过与时间条件无关的 Chunk,减少无效扫描和锁管理开销。
- 降低并发冲突:不同事务修改不同时间区间时,更不容易因为超表级别的广泛锁竞争互相阻塞。
它尤其适合数据修正、保留策略之外的定向清理、状态回填等工作负载。不过,优化效果仍取决于谓词是否能够定位 Chunk。缺少时间条件的大范围修改,依然可能触及大量数据和锁。
把 UPDATE 和 DELETE 写成可裁剪的形式
可以这样实践:让 DML 语句显式包含超表的时间分区列,并尽量使用边界清晰的半开区间。下面的 SQL 假设本机已经安装 TimescaleDB 扩展,可直接在测试数据库中运行。
CREATE EXTENSION IF NOT EXISTS timescaledb;
DROP TABLE IF EXISTS sensor_readings;
CREATE TABLE sensor_readings (
recorded_at timestamptz NOT NULL,
device_id bigint NOT NULL,
temperature double precision NOT NULL,
status text NOT NULL DEFAULT 'raw'
);
SELECT create_hypertable(
'sensor_readings',
by_range('recorded_at', INTERVAL '1 day')
);
INSERT INTO sensor_readings (recorded_at, device_id, temperature)
SELECT
timestamp '2025-01-01 00:00:00+00' + n * INTERVAL '5 minutes',
1001,
20 + (n % 10) * 0.1
FROM generate_series(0, 863) AS n;
UPDATE sensor_readings
SET status = 'validated'
WHERE recorded_at >= timestamptz '2025-01-02 00:00:00+00'
AND recorded_at < timestamptz '2025-01-03 00:00:00+00'
AND device_id = 1001;
DELETE FROM sensor_readings
WHERE recorded_at >= timestamptz '2025-01-01 00:00:00+00'
AND recorded_at < timestamptz '2025-01-01 06:00:00+00'
AND device_id = 1001;
这里的关键不是某个固定日期,而是 recorded_at >= ... AND recorded_at < ...。这种写法明确给出下界和上界,也避免相邻批次在边界时间点重复处理数据。
业务允许时,可以把大规模修正拆成按小时或按天提交的小事务:
BEGIN;
UPDATE sensor_readings
SET temperature = temperature - 0.4,
status = 'corrected'
WHERE recorded_at >= timestamptz '2025-01-02 00:00:00+00'
AND recorded_at < timestamptz '2025-01-02 01:00:00+00'
AND device_id = 1001;
COMMIT;
Chunk exclusion 能缩小候选 Chunk,但不能消除行更新本身产生的 WAL、索引维护和版本膨胀。批量越大,事务回滚成本和复制延迟风险通常也越高,因此仍需控制每批处理的数据量。
升级前后如何验证
升级不应只检查扩展版本,还要验证真实 DML 的耗时、等待事件和受影响行数。可以先记录当前环境:
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'timescaledb';
SELECT version();
包管理方式因操作系统和部署平台而异。完成数据库快照或备份,并通过对应的软件仓库安装 2.29.0 后,可以这样更新数据库中的扩展;执行前应确认 TimescaleDB 2.29.0 与当前 PostgreSQL 大版本及其他扩展兼容:
ALTER EXTENSION timescaledb UPDATE;
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'timescaledb';
在预发布环境中,可以对代表性语句执行 EXPLAIN (ANALYZE, BUFFERS)。由于 ANALYZE 会真实修改数据,应放进事务并在测试后回滚:
BEGIN;
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
UPDATE sensor_readings
SET status = 'rechecked'
WHERE recorded_at >= timestamptz '2025-01-02 08:00:00+00'
AND recorded_at < timestamptz '2025-01-02 09:00:00+00'
AND device_id = 1001;
ROLLBACK;
并发压测时,还可以从另一个会话观察锁等待:
SELECT
pid,
wait_event_type,
wait_event,
state,
query
FROM pg_stat_activity
WHERE datname = current_database()
AND pid <> pg_backend_pid()
ORDER BY query_start;
重点比较升级前后的执行时间、读取 Buffer 数量、锁等待时长,以及多个事务修改不同时间窗口时是否仍然互相阻塞。不要只用一条短语句得出结论,生产收益通常在 Chunk 数量多、并发高的环境里更明显。
上线时需要保留的边界
2.29.0 的 DML Chunk exclusion 解决的是不相关 Chunk 带来的扫描和锁竞争,并不意味着所有 UPDATE、DELETE 都会自动变成低成本操作。上线前建议完成以下检查:
- 备份数据库,并验证恢复流程,而不只是确认备份任务成功。
- 核对 PostgreSQL、TimescaleDB Toolkit 以及其他扩展的版本兼容性。
- 使用生产数据分布复现典型的定向修正和批量删除语句。
- 确保 DML 包含可用于 Chunk exclusion 的时间范围条件。
- 观察锁等待、WAL 增长、复制延迟、死元组和磁盘空间。
- 为升级准备维护窗口、回滚条件和明确的性能基线。
对于经常在超表上执行定向更新和删除的系统,2.29.0 的价值不只是让单条 SQL 更快,更重要的是减少这些维护操作对并发写入的干扰。升级后再配合范围明确的谓词和小批次事务,才能真正把 Chunk exclusion 转化为稳定的吞吐量。