TimescaleDB 時序資料庫完全指南:從 Hypertable 到連續聚合與壓縮策略 | PostgreSQL

2026/07/21
TimescaleDB 時序資料庫完全指南:從 Hypertable 到連續聚合與壓縮策略 | PostgreSQL

TimescaleDB 是建構在 PostgreSQL 之上的時序資料庫擴展套件,讓你用熟悉的 SQL 語法處理大規模時間序列資料。從 Hypertable 自動分區、time_bucket 聚合函數到 Continuous Aggregates 即時物化與自動 壓縮 策略,本文帶你完整掌握 TimescaleDB 的核心功能與實戰技巧。

為什麼需要 TimescaleDB?

時間序列資料(Time-Series Data)無處不在——IoT 感測器每秒回傳溫濕度、應用程式每分鐘記錄回應時間、金融市場每毫秒產生價格變動。這類資料有幾個共同特徵:

特徵說明
以時間為主軸資料依時間戳排序,幾乎只有 INSERT,極少 UPDATE
寫入量極大每秒數千到數十萬筆
查詢以時間範圍為主「最近 1 小時」「過去 7 天」是典型查詢模式
需要降取樣聚合原始資料太多,需聚合成分鐘/小時/天粒度
舊資料逐漸降級熱資料保留原始精度,冷資料壓縮或刪除

原生 PostgreSQL 可以存時序資料,但當資料量達到數億筆時,單一大表的索引維護、VACUUM 成本、查詢延遲都會急劇上升。TimescaleDB 解決了這些問題:

┌─────────────────────────────────────────────┐
│            TimescaleDB 架構                  │
│                                             │
│  ┌─────────────────────────────────┐        │
│  │        Hypertable(虛擬表)      │        │
│  │   CREATE TABLE metrics (...)    │        │
│  └────────────┬────────────────────┘        │
│               │ 自動分區                     │
│  ┌────────┬───┴────┬────────┬────────┐      │
│  │Chunk 1 │Chunk 2 │Chunk 3 │Chunk 4 │      │
│  │ 1月    │ 2月    │ 3月    │ 4月    │      │
│  │(熱資料) │(溫資料) │(壓縮)  │(壓縮)  │      │
│  └────────┴────────┴────────┴────────┘      │
│                                             │
│  + Continuous Aggregates(即時物化視圖)       │
│  + 自動壓縮(90%+ 壓縮比)                    │
│  + 資料保留策略(自動刪除過期資料)              │
└─────────────────────────────────────────────┘

安裝與啟用

Ubuntu/Debian

# 加入 TimescaleDB APT 來源
sudo apt install -y gnupg postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo sh -c "echo 'deb https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -cs) main' > /etc/apt/sources.list.d/timescaledb.list"
wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/timescaledb.gpg
sudo apt update

# 安裝(PostgreSQL 16 版本)
sudo apt install -y timescaledb-2-postgresql-16

# 執行調校精靈
sudo timescaledb-tune --quiet --yes

# 重啟 PostgreSQL
sudo systemctl restart postgresql

Docker

docker run -d --name timescaledb \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=password \
  timescale/timescaledb:latest-pg16

啟用擴展

-- 在目標資料庫中啟用
CREATE EXTENSION IF NOT EXISTS timescaledb;

-- 確認版本
SELECT extversion FROM pg_extension WHERE extname = 'timescaledb';
-- 2.17.2

Hypertable:自動分區的核心

Hypertable 是 TimescaleDB 的核心概念——它看起來像一張普通的 PostgreSQL 表,但底層會自動按時間維度切割成多個 Chunk(分區):

-- Step 1:建立普通表
CREATE TABLE sensor_data (
    time        TIMESTAMPTZ NOT NULL,
    device_id   TEXT        NOT NULL,
    temperature DOUBLE PRECISION,
    humidity    DOUBLE PRECISION,
    battery     DOUBLE PRECISION
);

-- Step 2:轉換為 Hypertable
-- chunk_time_interval 決定每個 Chunk 涵蓋的時間範圍
SELECT create_hypertable(
    'sensor_data',
    by_range('time', INTERVAL '1 day')
);

Chunk 時間間隔選擇

資料寫入速率建議 chunk_time_interval理由
< 1,000 筆/秒1 week減少 Chunk 數量,降低管理成本
1,000-10,000 筆/秒1 day平衡查詢效能與管理成本
> 10,000 筆/秒數小時確保單一 Chunk 不會太大

多維度分區

除了時間維度,還可以加入空間維度(如 device_id)做 Hash 分區:

SELECT create_hypertable(
    'sensor_data',
    by_range('time', INTERVAL '1 day'),
    by_hash('device_id', 4)  -- 4 個 Hash 分區
);

查看 Chunk 資訊

-- 列出所有 Chunk
SELECT chunk_name, range_start, range_end,
       pg_size_pretty(total_bytes) AS size
FROM timescaledb_information.chunks
WHERE hypertable_name = 'sensor_data'
ORDER BY range_start DESC
LIMIT 10;

time_bucket:時間聚合利器

time_bucket 是 TimescaleDB 最常用的函數,將時間戳對齊到指定的時間桶:

-- 每 5 分鐘平均溫度
SELECT
    time_bucket('5 minutes', time) AS bucket,
    device_id,
    AVG(temperature) AS avg_temp,
    MAX(temperature) AS max_temp,
    MIN(temperature) AS min_temp,
    COUNT(*) AS readings
FROM sensor_data
WHERE time > NOW() - INTERVAL '1 hour'
GROUP BY bucket, device_id
ORDER BY bucket DESC;

進階用法

-- 月度統計(使用 INTERVAL)
SELECT
    time_bucket('1 month', time) AS month,
    COUNT(*) AS total_readings,
    AVG(temperature) AS avg_temp
FROM sensor_data
GROUP BY month
ORDER BY month;

-- 帶偏移量的時間桶(工廠三班制:08:00 開始)
SELECT
    time_bucket('8 hours', time, '2026-01-01 08:00:00'::timestamptz) AS shift,
    AVG(temperature) AS avg_temp
FROM sensor_data
GROUP BY shift
ORDER BY shift;

其他實用 Hyperfunctions

-- 近似行數(比 COUNT(*) 快 10-100 倍)
SELECT approximate_row_count('sensor_data');

-- 第一筆與最後一筆
SELECT
    device_id,
    first(temperature, time) AS first_temp,
    last(temperature, time)  AS last_temp
FROM sensor_data
GROUP BY device_id;

-- 時間加權平均(處理不規則取樣間隔)
SELECT
    time_bucket('1 hour', time) AS bucket,
    average(time_weight('linear', time, temperature)) AS tw_avg
FROM sensor_data
WHERE time > NOW() - INTERVAL '24 hours'
GROUP BY bucket
ORDER BY bucket;

Continuous Aggregates:即時物化視圖

當你反覆查詢相同的聚合(如「每小時平均溫度」),每次都掃描原始資料非常浪費。Continuous Aggregates(連續聚合)會自動將聚合結果物化,並在有新資料時增量更新:

-- 建立連續聚合:每小時設備統計
CREATE MATERIALIZED VIEW sensor_hourly
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 hour', time) AS bucket,
    device_id,
    AVG(temperature)   AS avg_temp,
    MAX(temperature)   AS max_temp,
    MIN(temperature)   AS min_temp,
    AVG(humidity)      AS avg_humidity,
    COUNT(*)           AS readings
FROM sensor_data
GROUP BY bucket, device_id
WITH NO DATA;  -- 先不回填歷史資料

設定自動刷新策略

-- 自動刷新:每 30 分鐘刷新,範圍為 3 小時前到 1 小時前
SELECT add_continuous_aggregate_policy(
    'sensor_hourly',
    start_offset    => INTERVAL '3 hours',
    end_offset      => INTERVAL '1 hour',
    schedule_interval => INTERVAL '30 minutes'
);
時間線:
                    3h前        1h前       現在
──────────────────────┤───────────┤──────────┤──→
                      │← 刷新範圍 →│
                      │  (物化)   │ (即時)   │

end_offset 的意義:最近 1 小時的資料可能還有延遲到達的寫入,暫不物化——查詢時 TimescaleDB 會自動合併物化結果與即時資料(Real-Time Aggregation)。

手動回填歷史

-- 回填過去 30 天
CALL refresh_continuous_aggregate(
    'sensor_hourly',
    NOW() - INTERVAL '30 days',
    NOW() - INTERVAL '1 hour'
);

階層式聚合(Hierarchical)

可以在連續聚合之上再建連續聚合:

-- 日粒度(基於小時粒度的 sensor_hourly)
CREATE MATERIALIZED VIEW sensor_daily
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 day', bucket) AS day,
    device_id,
    AVG(avg_temp)    AS avg_temp,
    MAX(max_temp)    AS max_temp,
    MIN(min_temp)    AS min_temp,
    SUM(readings)    AS total_readings
FROM sensor_hourly
GROUP BY day, device_id
WITH NO DATA;

SELECT add_continuous_aggregate_policy(
    'sensor_daily',
    start_offset    => INTERVAL '3 days',
    end_offset      => INTERVAL '1 day',
    schedule_interval => INTERVAL '1 hour'
);

壓縮:節省 90%+ 儲存空間

TimescaleDB 的 列式壓縮(Columnar Compression) 可以將冷資料壓縮 90-95%,同時保持 SQL 查詢能力:

-- 啟用壓縮
ALTER TABLE sensor_data SET (
    timescaledb.compress,
    timescaledb.compress_segmentby = 'device_id',
    timescaledb.compress_orderby = 'time DESC'
);

參數說明

參數用途選擇指引
compress_segmentby壓縮段的分組欄位選擇 WHERE 條件常用的低基數欄位(如 device_id)
compress_orderby段內的排序方式通常為 time DESC,加速 ORDER BY 查詢

自動壓縮策略

-- 超過 7 天的 Chunk 自動壓縮
SELECT add_compression_policy(
    'sensor_data',
    compress_after => INTERVAL '7 days'
);

查看壓縮效果

-- 壓縮統計
SELECT
    pg_size_pretty(before_compression_total_bytes) AS before,
    pg_size_pretty(after_compression_total_bytes)  AS after,
    ROUND(
        (1 - after_compression_total_bytes::numeric /
             before_compression_total_bytes::numeric) * 100, 1
    ) AS compression_ratio_pct
FROM hypertable_compression_stats('sensor_data');

典型結果:

 before  | after  | compression_ratio_pct
---------+--------+-----------------------
 12 GB   | 980 MB | 91.8

手動壓縮特定 Chunk

-- 壓縮特定時間範圍
SELECT compress_chunk(c.chunk_name)
FROM timescaledb_information.chunks c
WHERE c.hypertable_name = 'sensor_data'
  AND c.range_end < NOW() - INTERVAL '7 days'
  AND NOT c.is_compressed;

資料保留策略

時序資料不需要永久保存——90 天前的原始秒級資料通常可以刪除(聚合結果保留在 Continuous Aggregate 中):

-- 自動刪除 90 天前的原始資料
SELECT add_retention_policy(
    'sensor_data',
    drop_after => INTERVAL '90 days'
);

-- 手動刪除(需要時)
SELECT drop_chunks(
    'sensor_data',
    older_than => INTERVAL '90 days'
);

完整的資料生命週期

原始資料 sensor_data
  │
  ├── 0-7 天:未壓縮(熱資料,高速寫入/查詢)
  ├── 7-90 天:已壓縮(溫資料,壓縮 90%+)
  └── 90 天後:自動刪除
        ↓
聚合資料 sensor_hourly
  │
  ├── 0-180 天:小時粒度物化結果
  └── 180 天後:自動刪除
        ↓
聚合資料 sensor_daily
  │
  └── 永久保留:天粒度物化結果
-- 為聚合也設定保留策略
SELECT add_retention_policy('sensor_hourly', drop_after => INTERVAL '180 days');
-- sensor_daily 永久保留,不設保留策略

實戰場景一:IoT 感測器監控

-- 完整的 IoT 資料模型
CREATE TABLE devices (
    device_id   TEXT PRIMARY KEY,
    location    TEXT,
    device_type TEXT,
    installed   TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE sensor_readings (
    time        TIMESTAMPTZ NOT NULL,
    device_id   TEXT        NOT NULL REFERENCES devices(device_id),
    temperature DOUBLE PRECISION,
    humidity    DOUBLE PRECISION,
    pressure    DOUBLE PRECISION,
    battery     DOUBLE PRECISION
);

SELECT create_hypertable('sensor_readings', by_range('time', INTERVAL '1 day'));

-- 建立索引(Chunk 內的局部索引)
CREATE INDEX idx_readings_device_time
    ON sensor_readings (device_id, time DESC);

-- 啟用壓縮
ALTER TABLE sensor_readings SET (
    timescaledb.compress,
    timescaledb.compress_segmentby = 'device_id',
    timescaledb.compress_orderby = 'time DESC'
);

SELECT add_compression_policy('sensor_readings', compress_after => INTERVAL '7 days');
SELECT add_retention_policy('sensor_readings', drop_after => INTERVAL '90 days');

異常偵測查詢

-- 偵測溫度異常:超過該設備近 24 小時平均值 3 個標準差
WITH device_stats AS (
    SELECT
        device_id,
        AVG(temperature) AS avg_temp,
        STDDEV(temperature) AS std_temp
    FROM sensor_readings
    WHERE time > NOW() - INTERVAL '24 hours'
    GROUP BY device_id
)
SELECT
    r.time,
    r.device_id,
    r.temperature,
    s.avg_temp,
    ROUND((r.temperature - s.avg_temp) / NULLIF(s.std_temp, 0), 2) AS z_score
FROM sensor_readings r
JOIN device_stats s ON r.device_id = s.device_id
WHERE r.time > NOW() - INTERVAL '1 hour'
  AND ABS(r.temperature - s.avg_temp) > 3 * s.std_temp
ORDER BY ABS(r.temperature - s.avg_temp) DESC;

設備電量預警

-- 電量低於 20% 且持續下降的設備
SELECT
    device_id,
    last(battery, time) AS current_battery,
    first(battery, time) AS battery_1h_ago,
    last(battery, time) - first(battery, time) AS battery_change
FROM sensor_readings
WHERE time > NOW() - INTERVAL '1 hour'
GROUP BY device_id
HAVING last(battery, time) < 20
   AND last(battery, time) < first(battery, time)
ORDER BY current_battery ASC;

實戰場景二:APM 應用效能監控

-- APM 指標表
CREATE TABLE app_metrics (
    time         TIMESTAMPTZ NOT NULL,
    service      TEXT        NOT NULL,
    endpoint     TEXT        NOT NULL,
    status_code  INT,
    response_ms  DOUBLE PRECISION,
    error        BOOLEAN DEFAULT FALSE
);

SELECT create_hypertable('app_metrics', by_range('time', INTERVAL '1 day'));

-- 連續聚合:每分鐘的服務健康摘要
CREATE MATERIALIZED VIEW service_health_1m
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 minute', time)  AS bucket,
    service,
    COUNT(*)                       AS total_requests,
    COUNT(*) FILTER (WHERE error)  AS error_count,
    ROUND(
        100.0 * COUNT(*) FILTER (WHERE error) / COUNT(*), 2
    )                              AS error_rate,
    PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY response_ms) AS p50,
    PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY response_ms) AS p95,
    PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY response_ms) AS p99
FROM app_metrics
GROUP BY bucket, service
WITH NO DATA;

SELECT add_continuous_aggregate_policy(
    'service_health_1m',
    start_offset    => INTERVAL '10 minutes',
    end_offset      => INTERVAL '1 minute',
    schedule_interval => INTERVAL '1 minute'
);

SLI/SLO 監控儀表板

-- 過去 1 小時的服務 SLI 摘要
SELECT
    service,
    SUM(total_requests) AS requests,
    ROUND(AVG(error_rate), 2) AS avg_error_rate,
    ROUND(AVG(p50), 1) AS avg_p50_ms,
    ROUND(AVG(p95), 1) AS avg_p95_ms,
    ROUND(AVG(p99), 1) AS avg_p99_ms,
    CASE
        WHEN AVG(error_rate) < 0.1 AND AVG(p99) < 500 THEN 'HEALTHY'
        WHEN AVG(error_rate) < 1.0 AND AVG(p99) < 1000 THEN 'DEGRADED'
        ELSE 'CRITICAL'
    END AS status
FROM service_health_1m
WHERE bucket > NOW() - INTERVAL '1 hour'
GROUP BY service
ORDER BY avg_error_rate DESC;

實戰場景三:金融 OHLCV K 線

-- 逐筆交易表
CREATE TABLE trades (
    time    TIMESTAMPTZ      NOT NULL,
    symbol  TEXT             NOT NULL,
    price   DOUBLE PRECISION NOT NULL,
    volume  DOUBLE PRECISION NOT NULL
);

SELECT create_hypertable('trades', by_range('time', INTERVAL '1 day'));

-- 1 分鐘 K 線連續聚合
CREATE MATERIALIZED VIEW ohlcv_1m
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 minute', time) AS bucket,
    symbol,
    first(price, time)  AS open,
    MAX(price)          AS high,
    MIN(price)          AS low,
    last(price, time)   AS close,
    SUM(volume)         AS volume
FROM trades
GROUP BY bucket, symbol
WITH NO DATA;

SELECT add_continuous_aggregate_policy(
    'ohlcv_1m',
    start_offset    => INTERVAL '5 minutes',
    end_offset      => INTERVAL '1 minute',
    schedule_interval => INTERVAL '1 minute'
);

-- 在 1 分鐘 K 線上建構 1 小時 K 線
CREATE MATERIALIZED VIEW ohlcv_1h
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 hour', bucket)  AS bucket,
    symbol,
    first(open, bucket)  AS open,
    MAX(high)            AS high,
    MIN(low)             AS low,
    last(close, bucket)  AS close,
    SUM(volume)          AS volume
FROM ohlcv_1m
GROUP BY time_bucket('1 hour', bucket), symbol
WITH NO DATA;

SELECT add_continuous_aggregate_policy(
    'ohlcv_1h',
    start_offset    => INTERVAL '3 hours',
    end_offset      => INTERVAL '1 hour',
    schedule_interval => INTERVAL '30 minutes'
);

移動平均線

-- 5 日與 20 日移動平均線
SELECT
    bucket AS date,
    symbol,
    close,
    AVG(close) OVER (
        PARTITION BY symbol ORDER BY bucket
        ROWS BETWEEN 4 PRECEDING AND CURRENT ROW
    ) AS ma5,
    AVG(close) OVER (
        PARTITION BY symbol ORDER BY bucket
        ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
    ) AS ma20
FROM ohlcv_1h
WHERE symbol = 'AAPL'
  AND bucket > NOW() - INTERVAL '30 days'
ORDER BY bucket;

TimescaleDB vs 原生 PostgreSQL 分區

面向原生 PARTITIONTimescaleDB Hypertable
分區建立手動 CREATE TABLE ... PARTITION OF自動按 chunk_time_interval 切割
分區數量管理需手動建立未來分區自動建立,無需維護
壓縮無內建壓縮列式壓縮 90%+
連續聚合需自行實作 ETL pipeline內建 CREATE MATERIALIZED VIEW ... WITH (timescaledb.continuous)
資料保留手動 DROP TABLE partition_nameadd_retention_policy 一行搞定
時間聚合函數date_trunc 有限粒度time_bucket 任意間隔
查詢最佳化手動確保分區消除自動 Chunk exclusion
索引管理每個分區各自管理自動在新 Chunk 建立索引

Community vs Licensed 版本

功能Community(Apache 2)Licensed(TSL)
Hypertable
time_bucket
壓縮
Continuous Aggregates
資料保留策略
多節點分散式
列式掃描 Skip Scan
Tiered Storage(S3)

大部分功能在 Community 版即可使用,只有多節點分散式和進階儲存分層需要 Licensed 版本。

效能調校要點

PostgreSQL 參數調整

-- 記憶體配置(以 64GB RAM 為例)
ALTER SYSTEM SET shared_buffers = '16GB';
ALTER SYSTEM SET effective_cache_size = '48GB';
ALTER SYSTEM SET work_mem = '256MB';
ALTER SYSTEM SET maintenance_work_mem = '2GB';

-- WAL 調整(高寫入場景)
ALTER SYSTEM SET wal_buffers = '64MB';
ALTER SYSTEM SET max_wal_size = '8GB';
ALTER SYSTEM SET checkpoint_completion_target = 0.9;

-- TimescaleDB 專用
ALTER SYSTEM SET timescaledb.max_background_workers = 8;

SELECT pg_reload_conf();

批次寫入最佳化

import psycopg2
from psycopg2.extras import execute_values

conn = psycopg2.connect("dbname=iot user=admin")
cur = conn.cursor()

# 使用 execute_values 批次插入(比逐筆 INSERT 快 10-50 倍)
data = [
    ('2026-07-21 10:00:00+08', 'sensor-001', 25.3, 60.1, 1013.2, 95.0),
    ('2026-07-21 10:00:01+08', 'sensor-002', 24.8, 62.5, 1013.1, 87.0),
    # ... 數千筆
]

execute_values(
    cur,
    """INSERT INTO sensor_readings
       (time, device_id, temperature, humidity, pressure, battery)
       VALUES %s""",
    data,
    page_size=1000  # 每批 1000 筆
)
conn.commit()

COPY 高速載入

# 從 CSV 高速載入(最快的方式)
timescaledb-parallel-copy \
    --connection "host=localhost dbname=iot user=admin" \
    --table sensor_readings \
    --file sensor_data.csv \
    --workers 4 \
    --batch-size 10000

監控查詢

-- 各 Hypertable 的 Chunk 統計
SELECT
    hypertable_name,
    COUNT(*) AS num_chunks,
    COUNT(*) FILTER (WHERE is_compressed) AS compressed_chunks,
    pg_size_pretty(SUM(total_bytes)) AS total_size
FROM timescaledb_information.chunks
GROUP BY hypertable_name;

-- 背景排程任務狀態
SELECT *
FROM timescaledb_information.jobs
WHERE application_name LIKE 'User-Defined%'
   OR proc_name IN ('policy_compression', 'policy_retention', 'policy_refresh_continuous_aggregate')
ORDER BY next_start;

-- 連續聚合刷新狀態
SELECT
    view_name,
    completed_threshold,
    next_start
FROM timescaledb_information.continuous_aggregate_stats;

常見問題與解決方案

問題原因解決方案
寫入變慢單一連線瓶頸使用連線池(PgBouncer)+ 批次寫入
查詢慢(大範圍)未使用 Continuous Aggregate建立適當粒度的連續聚合
磁碟空間暴增未啟用壓縮add_compression_policy 並回壓歷史 Chunk
壓縮後查詢慢segmentby 選擇不當確保 WHERE 條件欄位在 compress_segmentby
Chunk 過多chunk_time_interval 太短調大間隔(不影響既有 Chunk)
連續聚合延遲end_offset 太大縮小 end_offset,接受輕微的 real-time 開銷
UPDATE/DELETE 壓縮 Chunk壓縮 Chunk 不支援就地修改decompress_chunk,修改後重新壓縮

總結

TimescaleDB 讓 PostgreSQL 成為一個高效能的時序資料庫,核心優勢在於:

  1. Hypertable 自動分區——不需手動管理分區表,按時間自動切割 Chunk
  2. time_bucket 聚合——任意時間粒度的降取樣,比 date_trunc 更靈活
  3. Continuous Aggregates——增量更新的物化視圖,查詢速度提升 100-1000 倍
  4. 列式壓縮——冷資料壓縮 90%+,大幅節省儲存成本
  5. 資料保留策略——一行 SQL 搞定資料生命週期管理
  6. 完整 SQL 相容——所有 PostgreSQL 功能、工具、生態系都能直接使用

無論是 IoT 感測器、APM 應用監控還是金融數據分析,TimescaleDB 都是在 PostgreSQL 生態中處理時序資料的首選方案。

下一篇,我們將探討 資料庫遷移工具——從 Flyway、Liquibase 到 golang-migrate,掌握 Schema 版本控制與零停機遷移的實戰技巧。

BenZ Software Developer

熱愛技術的軟體開發者,在這裡分享程式開發經驗與學習筆記。

本週主打

AI 自動化入門包

你每天手動在做的那些煩事,其實 AI 可以自己跑。這份給你 10 個照著做就會的自動化工作流 + 50 個複製即用的提示詞,不用會寫程式。

看看這個產品 →