選擇資料庫

本頁內容

每次部署只選擇一個資料庫轉接器。資料庫保存內容模型、條目、使用者、設定和外掛資料。媒體二進位檔屬於獨立的儲存後端。

概覽

資料庫適用情境執行階段
SQLite單一 Node.js 程序擁有持久化磁碟Node.js 或本機開發
D1站台執行在 Cloudflare Workers 上,並且應當使用 Cloudflare SQLCloudflare Workers
Hyperdrive站台執行在 Workers 上,並且必須使用既有的 PostgreSQL 來源Cloudflare Workers
PostgreSQL多個 Node.js 程序需要共用一個資料庫Node.js
libSQLNode.js 部署需要一個與 SQLite 相容的遠端資料庫Node.js

D1 是 Cloudflare 範本的預設選項。SQLite 是最簡單的 Node.js 選項,但它需要一個可寫入的持久化磁碟區,並且需要維運層面的資料庫備份。

SQLite

SQLite 使用 Node.js 內建的資料庫驅動程式,是 Node.js 部署中最簡單的選項。

import { sqlite } from "emdash/db";

export default defineConfig({
	integrations: [
		emdash({
			database: sqlite({ url: "file:./data.db" }),
		}),
	],
});

設定

選項型別說明
urlstring帶 file: 前綴的檔案路徑

檔案路徑

url 必須以 file: 開頭:

// Relative path
database: sqlite({ url: "file:./data/emdash.db" });

// Absolute path
database: sqlite({ url: "file:/var/data/emdash.db" });

// From environment variable
database: sqlite({ url: `file:${process.env.DATABASE_PATH}` });

Write-ahead logging

EmDash 以 write-ahead logging(WAL)模式開啟 SQLite 資料庫。站台執行期間,SQLite 會在資料庫檔案旁保留兩個額外的檔案,例如 emdash.db-wal 和 emdash.db-shm。程序需要對資料庫所在目錄有寫入權限才能建立它們。

-wal 檔案中可能保存著已提交、但尚未寫入主資料庫檔案的變更。請使用 SQLite 的備份指令進行備份,而不是只複製 .db 檔案。請參閱 SQLite 備份與復原。

WAL 需要共用記憶體,因此請把資料庫放在本機磁碟或區塊儲存磁碟區上,而不是 NFS、SMB 之類的網路檔案系統上。

Cloudflare D1

D1 是 Cloudflare 的無伺服器 SQLite 資料庫。部署到 Cloudflare Workers 時使用它。

import { d1 } from "@emdash-cms/cloudflare";

export default defineConfig({
	integrations: [
		emdash({
			database: d1({ binding: "DB" }),
		}),
	],
});

設定

選項型別預設值說明
bindingstring—wrangler.jsonc 中的 D1 繫結名稱
sessionstring"disabled"讀取複寫模式(見下文)
bookmarkCookiestring"__em_d1_bookmark"工作階段書籤使用的 Cookie 名稱

Wrangler 繫結

wrangler.jsonc

{
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "emdash-db"
    }
  ]
}

wrangler.toml

[[d1_databases]]
binding = "DB"
database_name = "emdash-db"

部署期間,Wrangler 可以根據這個繫結建立缺少的 D1 資料庫。EmDash 遷移是獨立的一個步驟。完整的繫結集合請參閱部署到 Cloudflare,遷移操作手冊請參閱管理核心資料庫遷移。

讀取複本

D1 支援讀取複寫,以降低全球分散式站台的讀取延遲。啟用後,讀取查詢會被路由到附近的複本,而不是總是存取主資料庫。

EmDash 使用 D1 Sessions API 透明地管理這一過程。透過 session 選項啟用它:

import { d1 } from "@emdash-cms/cloudflare";

export default defineConfig({
	integrations: [
		emdash({
			database: d1({
				binding: "DB",
				session: "auto",
			}),
		}),
	],
});

工作階段模式

模式行為
"disabled"不使用工作階段。所有查詢都送往主資料庫。預設值。
"auto"匿名請求從最近的複本讀取。已驗證的使用者透過書籤 Cookie 獲得讀己之寫(read-your-writes)一致性。
"primary-first"與 "auto" 類似,但第一次查詢總是送往主資料庫。適用於寫入非常頻繁的站台。

運作原理

  • 匿名訪客使用 first-unconstrained——讀取送往最近的複本,延遲最低。匿名使用者從不寫入,因此不需要一致性保證。
  • 已驗證的使用者(編輯、作者)使用以書籤為基礎的工作階段。寫入之後,書籤 Cookie 會確保下一個請求至少能看到該狀態。
  • 寫入請求(POST、PUT、DELETE)總是從主資料庫開始。
  • 建置階段的查詢(Astro 內容集合)完全繞過工作階段,直接使用主資料庫。

libSQL

libSQL 是 SQLite 的一個分支,支援遠端連線。當你需要一個不依賴 Cloudflare D1 的遠端資料庫時使用它。

import { libsql } from "emdash/db";

export default defineConfig({
	integrations: [
		emdash({
			database: libsql({
				url: process.env.LIBSQL_DATABASE_URL,
				authToken: process.env.LIBSQL_AUTH_TOKEN,
			}),
		}),
	],
});

設定

選項型別說明
urlstring資料庫 URL(libsql://... 或 file:...)
authTokenstring遠端資料庫的執行階段驗證權杖(本機可選)
migrationAuthTokenEnvstring遷移權杖的變數名稱(預設 TURSO_AUTH_TOKEN)

本機開發

開發期間使用本機 libSQL 檔案:

database: libsql({ url: "file:./data.db" });

PostgreSQL

PostgreSQL 支援需要完整關聯式資料庫的 Node.js 部署。

import { postgres } from "emdash/db";

export default defineConfig({
	integrations: [
		emdash({
			database: postgres({
				connectionString: process.env.DATABASE_URL,
			}),
		}),
	],
});

設定

你可以使用連線字串或個別的參數進行連線:

// Connection string
database: postgres({
	connectionString: "postgres://user:password@localhost:5432/emdash",
});

// Individual parameters
database: postgres({
	host: "localhost",
	port: 5432,
	database: "emdash",
	user: "emdash",
	password: process.env.DB_PASSWORD,
	ssl: true,
});
選項型別說明
connectionStringstringPostgreSQL 連線 URL
hoststring資料庫主機
portnumber資料庫連接埠
databasestring資料庫名稱
userstring資料庫使用者
passwordstring資料庫密碼
sslboolean啟用 SSL
pool.minnumber連線集區最小連線數(預設 0)
pool.maxnumber連線集區最大連線數(預設 10)
pool.connectionTimeoutMillisnumber最長連線等待時間(pg 預設值:0,即不逾時)
pool.idleTimeoutMillisnumber閒置用戶端的存活時間(pg 預設值:10,000 毫秒)
migrationConnectionStringEnvstring遷移連線字串的變數名稱(預設 DATABASE_URL)

把 pool.connectionTimeoutMillis 設為非零值,可以限制 PostgreSQL 無法存取或連線集區中沒有可用連線時請求的最長等待時間。把 pool.idleTimeoutMillis 設為 0,可以讓閒置用戶端一直保持開啟,直到連線集區關閉。省略任一個選項都會保留 pg 的預設值。

資料庫角色要求

EmDash 會建立並更新自己的 PostgreSQL 資料表。核心遷移會建立並修改系統資料表和集合資料表,內容型別會建立 ec_* 資料表,新增或移除欄位會修改它所屬的集合資料表。因此,所設定的 PostgreSQL 角色在站台的整個生命週期中都需要 schema 權限,而不僅僅是在初始設定期間。

請為 EmDash 使用一個固定的角色。它需要:

  • 對資料庫的 CONNECT 權限;
  • 對目前 schema 的 USAGE 和 CREATE 權限;
  • 擁有每一個 EmDash 資料表和函式的擁有權,可以是直接擁有,也可以透過以 INHERIT 方式成為所屬角色的成員;以及
  • 對這些資料表的 SELECT、INSERT、UPDATE 和 DELETE 權限。

它不需要是超級使用者,不需要 CREATEDB 或 CREATEROLE,也不需要建立擴充功能。PostgreSQL 沒有提供 ALTER 或 DROP 資料表的授權:這些操作屬於物件的擁有者以及繼承其權限的角色。把資料表上的 ALL 授予另一個角色,並不會讓該角色成為擁有者。EmDash 不會執行 SET ROLE,因此不帶繼承設定的成員關係是不夠的。

大多數安裝可以使用資料庫既有的 schema,通常是 public。當資料庫專用於 EmDash 時,這是最簡單的選項。在下面的範例中,emdash_app 是 EmDash 連線字串中的登入角色;請使用現有的服務供應商角色,或建立一個專用登入角色。使用管理員連線授予它存取權限,並替換成你自己的資料庫、schema 和角色名稱:

GRANT CONNECT ON DATABASE app TO emdash_app;
GRANT USAGE, CREATE ON SCHEMA public TO emdash_app;

這些授權讓該角色可以建立新物件,但不會變更既有資料表的擁有者;當既有站台存在混合擁有者時,請使用 PostgreSQL 擁有權修復手冊。

EmDash 使用 PostgreSQL 目前生效的 current_schema()。它不會建立 schema,也不會設定 search_path,所以請在部署之前驗證連線:

SELECT
  current_database(),
  session_user,
  current_user,
  current_schema(),
  current_setting('search_path');

可選:使用專用 schema

當 EmDash 與其他應用程式共用一個資料庫,或者你希望把它的物件與 public 隔離開時,請使用專用 schema。這是可選的,最好在首次 EmDash 設定之前設定。專用於 EmDash 的資料庫不需要獨立的 schema。

假設標準的 emdash_app 角色已經存在,使用管理員連線建立並選定它的 schema:

GRANT CONNECT ON DATABASE app TO emdash_app;
CREATE SCHEMA emdash AUTHORIZATION emdash_app;
ALTER ROLE emdash_app IN DATABASE app SET search_path = emdash;

這不會把既有安裝從 public 遷走,也不會修復混合擁有權。既有站台應保留目前的 schema,並改用 PostgreSQL 擁有權修復手冊。

連線集區

轉接器使用 pg.Pool。請根據你的部署情況調整集區大小:

database: postgres({
	connectionString: process.env.DATABASE_URL,
	pool: { min: 2, max: 20 },
});

Hyperdrive

使用 hyperdrive() 轉接器,可以讓 EmDash 在 Cloudflare Workers 上執行,並由既有的 PostgreSQL(或與 Postgres 相容,例如 PlanetScale Postgres)資料庫提供支援。Hyperdrive 透過 Cloudflare 的網路對連線進行集區化和加速;查詢由 EmDash 的 PostgreSQL 方言執行。

import { hyperdrive, r2 } from "@emdash-cms/cloudflare";

export default defineConfig({
	integrations: [
		emdash({
			database: hyperdrive({ binding: "HYPERDRIVE" }),
			storage: r2({ binding: "MEDIA" }),
		}),
	],
});

要求

  • 在你的站台中安裝 pg >= 8.16.3(pnpm add pg)
  • compatibility_flags: ["nodejs_compat"]
  • compatibility_date >= "2024-09-23"

設定

首先準備 PostgreSQL 角色。然後使用該角色的連線字串建立 Hyperdrive 設定,並把繫結加入你的 Wrangler 設定中:

wrangler hyperdrive create emdash-db \
  --connection-string "postgres://user:password@host/db?sslmode=verify-full" \
  --caching-disabled

wrangler.jsonc

{
  "hyperdrive": [
    {
      "binding": "HYPERDRIVE",
      "id": "<your-hyperdrive-id>"
    }
  ]
}

wrangler.toml

[[hyperdrive]]
binding = "HYPERDRIVE"
id = "<your-hyperdrive-id>"

設定

選項型別預設值說明
bindingstring"HYPERDRIVE"主(已停用快取)Hyperdrive 繫結的名稱
cachedBindingstring—用於匿名讀取的、可選的已啟用快取的繫結(見下文)
preferUncachedAfterWriteMsnumber60000*內容發佈之後,在這麼多毫秒內,匿名公開讀取優先使用 binding(與 Hyperdrive 的 max_age 保持一致)
migrationConnectionStringEnvstringCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING>包含 emdash migrate 所用的 PostgreSQL 直連來源 URL 的環境變數
maxnumber5Worker 內到 Hyperdrive 的連線集區的最大大小

*預設值 60000 僅在設定了 cachedBinding 時適用;否則會被忽略。

從快取提供匿名讀取

預設情況下,你會完全停用 Hyperdrive 快取,因為管理後台和寫入操作需要寫後讀一致性。但是,使用 GET 或 HEAD 的匿名公開請求可以容忍一個較短的過期視窗。如果這種取捨可以接受,請在同一個資料庫上執行兩個 Hyperdrive 設定:一個關閉快取(主 binding),一個開啟快取(cachedBinding)。EmDash 會把這些匿名公開請求路由到啟用快取的繫結,並把其他所有請求路由到未快取的主繫結。

# Primary — caching OFF (used by admin, auth'd requests, writes, migrations)
wrangler hyperdrive create emdash-db \
  --connection-string "postgres://user:password@host/db?sslmode=verify-full" \
  --caching-disabled

# Cached — SAME database role and connection string, caching ON
wrangler hyperdrive create emdash-db-cached \
  --connection-string "postgres://user:password@host/db?sslmode=verify-full"
{
	"hyperdrive": [
		{ "binding": "HYPERDRIVE", "id": "<caching-disabled-id>" },
		{ "binding": "HYPERDRIVE_CACHED", "id": "<caching-enabled-id>" }
	]
}
database: hyperdrive({ binding: "HYPERDRIVE", cachedBinding: "HYPERDRIVE_CACHED" });

這就是 Cloudflare 為快取文件化的雙設定模式。EmDash 會針對每個請求決定使用哪個繫結:

  • 對公開站台路徑的匿名讀取(GET/HEAD,沒有工作階段,不在 /_emdash 之下)→ 啟用快取的 cachedBinding,但內容發佈之後的一小段時間視窗內除外(預設 60 秒;把 preferUncachedAfterWriteMs 設為你的 Hyperdrive max_age),此時 EmDash 優先使用未快取的 binding,以免重新建置時用仍然過期的 Hyperdrive 結果重新填充邊緣/物件快取。
  • 已驗證的請求(編輯、作者)→ 未快取的 binding。
  • 變更請求(POST、PUT、PATCH、DELETE,包括匿名請求)→ 未快取的 binding。
  • /_emdash 之下的任何請求(管理後台、設定、驗證、內部 API),即使是匿名的 GET → 未快取的 binding。
  • 執行階段遷移和冷啟動 → 始終使用主 binding。
  • 由部署管理的遷移 → 使用 migrationConnectionStringEnv 直接連線 PostgreSQL 來源;它們從不使用任何一個 Hyperdrive 繫結。

可選:使用單獨的快取角色

遷移、設定、已驗證的請求和明確的寫入請求始終使用主 binding。cachedBinding 使用的單獨角色不需要 schema 擁有權或 CREATE 權限,但它需要 CONNECT、對 schema 的 USAGE,以及對公開站台所用每個資料表的 SELECT 權限。

匿名公開的 GET 和 HEAD 請求還可能記錄重新導向命中和 404。為了保留這些功能,快取角色還需要對 _emdash_redirects 的 UPDATE 權限,以及對 _emdash_404_log 的 SELECT、INSERT、UPDATE 和 DELETE 權限。在公開的 GET 或 HEAD 期間執行寫入的外掛或應用程式碼可能需要更多權限。除非你已經在受限的快取角色下測試過站台,否則兩個繫結請使用同一個角色。

請在 EmDash 完成初始遷移之後再新增快取角色。下面的範例使用可選的 emdash schema;請替換為你目前生效的 schema,例如 public。使用你的服務供應商提供的管理員角色建立登入角色和資料庫設定:

CREATE ROLE emdash_cached LOGIN PASSWORD 'replace-with-a-secret';
GRANT CONNECT ON DATABASE app TO emdash_cached;
ALTER ROLE emdash_cached IN DATABASE app SET search_path = emdash;

然後以 schema 和資料表的擁有者 emdash_app 身分連線,向既有和未來的資料表授予存取權限:

GRANT USAGE ON SCHEMA emdash TO emdash_cached;
GRANT SELECT ON ALL TABLES IN SCHEMA emdash TO emdash_cached;
GRANT UPDATE ON emdash._emdash_redirects TO emdash_cached;
GRANT SELECT, INSERT, UPDATE, DELETE ON emdash._emdash_404_log TO emdash_cached;

ALTER DEFAULT PRIVILEGES IN SCHEMA emdash
  GRANT SELECT ON TABLES TO emdash_cached;

在啟用 cachedBinding 之前,請用這兩個角色分別連線,並確認它們回報的 current_database() 和 current_schema() 相同。在共用的 schema 上,GRANT SELECT ON ALL TABLES 還會暴露無關的資料表。請改為對各個 EmDash 資料表逐一授權,並在新增集合或其他 schema 物件時更新這些授權。

核心遷移

預設情況下,EmDash 會為每種受支援的方言自動執行核心遷移。Astro 的建置和同步還會產生一份經過校驗、不含密鑰的 .emdash/migrations.json,emdash migrate 可以在部署之前套用它。SQLite、libSQL、PostgreSQL、D1,以及 Hyperdrive 背後的 PostgreSQL 直連來源都有部署執行器。

目標憑證、CI 序列化、auto/check/manual 執行階段策略,以及從未知記錄或含糊不清的 D1 寫入中復原,請參閱管理核心資料庫遷移。

對於 PostgreSQL,執行階段遷移透過所設定的連線執行;Hyperdrive 的執行階段遷移始終使用它的主繫結。由部署管理的 Hyperdrive 遷移則直接連線 PostgreSQL 來源。核心遷移可能建立資料表、索引和函式,修改或刪除資料行和限制條件,並更新既有的資料列。一個能夠連線並修改資料列、但並不擁有既有 EmDash 物件的角色是不夠的。設定精靈無法修復缺少的資料庫權限,因為執行階段遷移在設定之前就已經執行。

在設定精靈完成之前,EmDash 還會在第一次請求時一次性套用種子檔案的 schema 和結構。種子中的範例內容只有在管理員於設定精靈中選擇它時才會被套用。種子從 .emdash/seed.json、package.json#emdash.seed 中的路徑或 seed/seed.json 中讀取,以先找到的為準,並在編譯時內嵌到建置中。如果都不存在,則使用內建的預設種子。之後針對既有資料庫的啟動不會更動其內容。

為不同環境使用單獨的資料庫

請為開發、預覽、預備環境和正式環境各自提供獨立的資料庫。指向正式環境的預覽部署可能會對線上資料執行核心遷移或破壞性的內容模型指令。

對於 Cloudflare,請在對應的 Wrangler 環境下定義每個 D1 或 Hyperdrive 繫結,並在 Wrangler 指令中傳入 --env。對於 Node.js,請向每個執行階段環境注入不同的資料庫 URL。請把憑證保存在執行階段密鑰中,而不要放在 astro.config.mjs 裡。