選擇資料庫

本頁內容

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

概覽

DatabaseUse it whenRuntime
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" }),
		}),
	],
});

設定

OptionTypeDescription
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" }),
		}),
	],
});

設定

OptionTypeDefaultDescription
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",
			}),
		}),
	],
});

工作階段模式

ModeBehavior
"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,
			}),
		}),
	],
});

設定

OptionTypeDescription
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,
});
OptionTypeDescription
connectionStringstringPostgreSQL 連線 URL
hoststring資料庫主機
portnumber資料庫連接埠
databasestring資料庫名稱
userstring資料庫使用者
passwordstring資料庫密碼
sslboolean啟用 SSL
pool.minnumber最小集區連線數(預設 0)
pool.maxnumber最大集區連線數(預設 10)
pool.connectionTimeoutMillisnumber最大連線等待(pg 預設:0,無逾時)
pool.idleTimeoutMillisnumber閒置用戶端壽命(pg 預設:10,000 ms)
migrationConnectionStringEnvstring遷移連線字串變數名稱(預設 DATABASE_URL)

將 pool.connectionTimeoutMillis 設為非零值,以限制 PostgreSQL 不可達或無可用集區連線時請求等待的時間。將 pool.idleTimeoutMillis 設為 0,以在集區關閉前保持閒置用戶端開啟。省略任一選項會保留 pg 預設值。

資料庫角色要求

EmDash 建立並更新自己的 PostgreSQL 資料表。核心遷移建立和變更系統與集合資料表,內容類型建立 ec_* 資料表,新增或移除欄位會變更其集合資料表。因此,設定的 PostgreSQL 角色需要在網站整個生命週期內擁有結構描述權限,而不僅僅是初始設定期間。

為 EmDash 使用一個規範角色。它需要:

  • 資料庫上的 CONNECT;
  • 作用中結構描述上的 USAGE 和 CREATE;
  • 每個 EmDash 資料表和函式的擁有權,直接擁有或透過對擁有角色的帶 INHERIT 的成員關係;以及
  • 這些資料表上的 SELECT、INSERT、UPDATE 和 DELETE。

它不必是超級使用者,不必有 CREATEDB 或 CREATEROLE,也不必建立擴充。PostgreSQL 不提供資料表的 ALTER 或 DROP 授權:這些操作屬於物件擁有者以及繼承其特權的角色。向不同角色授予資料表上的 ALL 不會使其成為擁有者。EmDash 不執行 SET ROLE,因此無繼承的成員關係不夠。

大多數安裝可以使用資料庫現有的結構描述,通常是 public。當資料庫專用於 EmDash 時,這是最簡單的選項。在下面的範例中,emdash_app 是 EmDash 連線字串中的登入角色;使用現有提供者角色或建立專用登入。用管理連線授予存取權限,並替換你的資料庫、結構描述和角色名稱:

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

這些授權讓角色可以建立新物件。它們不變更現有資料表的擁有者;當現有網站有混合擁有者時,使用 PostgreSQL 擁有權修復操作手冊。

EmDash 使用 PostgreSQL 的作用中 current_schema()。它不建立結構描述,也不設定 search_path,因此在部署前驗證連線:

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

可選:使用專用結構描述

當 EmDash 與另一應用程式共用資料庫,或希望將其物件與 public 隔離時,使用專用結構描述。這是可選的,最好在首次 EmDash 設定之前設定。專用於 EmDash 的資料庫不需要單獨結構描述。

假設規範的 emdash_app 角色已存在,用管理連線建立並選擇其結構描述:

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 遷出,也不會修復混合擁有權。現有網站應保留目前結構描述,並改用 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>"

設定

OptionTypeDefaultDescription
bindingstring"HYPERDRIVE"主(停用快取)Hyperdrive 繫結名稱
cachedBindingstring—用於匿名讀取的可選啟用快取繫結(見下文)
preferUncachedAfterWriteMsnumber60000*內容發佈後,在匿名公開讀取上優先使用 binding 這麼多毫秒(符合 Hyperdrive max_age)
migrationConnectionStringEnvstringCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING>包含供 emdash migrate 使用的直接 PostgreSQL 來源 URL 的環境變數
maxnumber5Worker 內到 Hyperdrive 的連線集區最大大小

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

從快取提供匿名讀取

預設完全停用 Hyperdrive 快取,因為管理和寫入需要 read-after-write 一致性。但使用 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 的單獨角色不需要結構描述擁有權或 CREATE,但需要 CONNECT、結構描述 USAGE,以及對公開網站使用的每張資料表的 SELECT。

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

在 EmDash 完成初始遷移後新增快取角色。下面的範例使用可選的 emdash 結構描述;替換為你的作用中結構描述,例如 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;

然後以結構描述和資料表擁有者 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()。在共用結構描述上,GRANT SELECT ON ALL TABLES 也會暴露無關資料表。改為授予各個 EmDash 資料表的存取權限,並在新增集合或其他結構描述物件時更新這些授權。

核心遷移

EmDash 預設對每種受支援的方言自動執行核心遷移。Astro 建置和同步還會發出經過驗證、不含金鑰的 .emdash/migrations.json,emdash migrate 可在部署前套用它。SQLite、libSQL、PostgreSQL、D1 以及 Hyperdrive 背後的直接 PostgreSQL 來源都有部署執行器。

目標認證、CI 序列化、auto/check/manual 執行階段原則,以及從未知記錄或模糊 D1 寫入中復原,見管理核心資料庫遷移。

對於 PostgreSQL,執行階段遷移透過設定的連線執行;Hyperdrive 執行階段遷移始終使用其主繫結。部署管理的 Hyperdrive 遷移直接連線到 PostgreSQL 來源。核心遷移可能建立資料表、索引和函式,變更或刪除欄和條件約束,並更新現有列。能夠連線並修改列但不擁有現有 EmDash 物件的角色不夠。設定精靈無法修復缺失的資料庫特權,因為執行階段遷移在設定之前執行。

如果資料庫為空(無集合)且設定精靈尚未完成,EmDash 還會在首次啟動時套用種子檔案。種子從 .emdash/seed.json、package.json#emdash.seed 中的路徑或 seed/seed.json — 以先找到的為準 — 讀取,並在編譯時內嵌到建置中。若都不存在,則使用內建預設種子。針對現有資料庫的後續啟動會保留其內容。

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

為開發、預覽、預備和生產各自提供自己的資料庫。指向生產的預覽部署可能對線上資料執行核心遷移或破壞性內容模型命令。

對於 Cloudflare,在符合的 Wrangler 環境下定義每個 D1 或 Hyperdrive 繫結,並向 Wrangler 命令傳遞 --env。對於 Node.js,向每個執行階段環境注入不同的資料庫 URL。將認證放在執行階段密碼中,而不是 astro.config.mjs。