deno.com
本頁內容

鍵空間

Deno KV 是一個鍵值儲存區。鍵空間是鍵+值+版本戳記配對的扁平命名空間。鍵是鍵部分的序列,允許對階層式資料進行建模。值是任意 JavaScript 物件。版本戳記代表值何時被插入/修改。

跳到標題

Deno KV 中的鍵是鍵部分的序列,可以是 stringnumberbooleanUint8Arraybigint

使用部分序列而不是單一字串,消除了分隔符號注入攻擊的可能性,因為沒有可見的分隔符號。

鍵注入攻擊發生在攻擊者透過將鍵編碼方案中使用的分隔符號注入到使用者控制的變數中,從而操縱鍵值儲存區的結構,導致非預期的行為或未經授權的存取時。例如,考慮一個使用斜線 (/) 作為分隔符號的鍵值儲存區,鍵類似於 "users/alice/settings" 和 "users/bob/settings"。攻擊者可以建立一個名為 "alice/settings/hacked" 的新使用者,以形成鍵 "users/alice/settings/hacked/settings",注入分隔符號並操縱鍵結構。在 Deno KV 中,注入將導致鍵 ["users", "alice/settings/hacked", "settings"],這沒有危害。

在鍵部分之間,使用不可見的分隔符號來分隔部分。這些分隔符號永遠不可見,但確保一個部分不會與另一個部分混淆。例如,鍵部分 ["abc", "def"]["ab", "cdef"]["abc", "", "def"] 都是不同的鍵。

鍵區分大小寫,並依其部分按字典順序排序。第一部分是最重要的,最後一部分是最不重要的。部分的順序由部分的型別和值決定。

鍵部分排序 跳到標題

鍵部分依其型別按字典順序排序,在給定型別內,依其值排序。型別的順序如下

  1. Uint8Array
  2. string
  3. number
  4. bigint
  5. boolean

在給定型別內,排序為

  • Uint8Array:陣列的位元組排序
  • string:字串 UTF-8 編碼的位元組排序
  • number:-Infinity < -1.0 < -0.5 < -0.0 < 0.0 < 0.5 < 1.0 < Infinity < NaN
  • bigint:數學排序,最大的負數在前,最大的正數在後
  • boolean:false < true

這表示部分 1.0 (數字) 在部分 2.0 (也是數字) 之前排序,但大於部分 0n (bigint),因為 1.0 是數字,而 0n 是 bigint,並且型別排序優先於型別內的值排序。

鍵範例 跳到標題

["users", 42, "profile"]; // User with ID 42's profile
["posts", "2023-04-23", "comments"]; // Comments for all posts on 2023-04-23
["products", "electronics", "smartphones", "apple"]; // Apple smartphones in the electronics category
["orders", 1001, "shipping", "tracking"]; // Tracking information for order ID 1001
["files", new Uint8Array([1, 2, 3]), "metadata"]; // Metadata for a file with Uint8Array identifier
["projects", "openai", "tasks", 5]; // Task with ID 5 in the OpenAI project
["events", "2023-03-31", "location", "san_francisco"]; // Events in San Francisco on 2023-03-31
["invoices", 2023, "Q1", "summary"]; // Summary of Q1 invoices for 2023
["teams", "engineering", "members", 1n]; // Member with ID 1n in the engineering team

通用唯一詞典排序識別碼 (ULID) 跳到標題

鍵部分排序允許按時間順序列出由時間戳記和 ID 部分組成的鍵。通常,您可以使用以下方式產生鍵:Date.now()crypto.randomUUID()

async function setUser(user) {
  await kv.set(["users", Date.now(), crypto.randomUUID()], user);
}

連續多次執行,這會產生以下鍵

["users", 1691377037923, "8c72fa25-40ad-42ce-80b0-44f79bc7a09e"]; // First user
["users", 1691377037924, "8063f20c-8c2e-425e-a5ab-d61e7a717765"]; // Second user
["users", 1691377037925, "35310cea-58ba-4101-b09a-86232bf230b2"]; // Third user

但是,在某些情況下,將時間戳記和 ID 表示在單一鍵部分中可能更直接。您可以使用通用唯一詞典排序識別碼 (ULID) 來執行此操作。這種型別的識別碼編碼 UTC 時間戳記,依詞典順序排序,並且預設為密碼學隨機

import { ulid } from "jsr:@std/ulid";

const kv = await Deno.openKv();

async function setUser(user) {
  await kv.set(["users", ulid()], user);
}
["users", "01H76YTWK3YBV020S6MP69TBEQ"]; // First user
["users", "01H76YTWK4V82VFET9YTYDQ0NY"]; // Second user
["users", "01H76YTWK5DM1G9TFR0Y5SCZQV"]; // Third user

此外,您可以使用 monotonicUlid 函數以單調遞增的方式產生 ULID

import { monotonicUlid } from "jsr:@std/ulid";

async function setUser(user) {
  await kv.set(["users", monotonicUlid()], user);
}
// Strict ordering for the same timestamp by incrementing the least-significant random bit by 1
["users", "01H76YTWK3YBV020S6MP69TBEQ"]; // First user
["users", "01H76YTWK3YBV020S6MP69TBER"]; // Second user
["users", "01H76YTWK3YBV020S6MP69TBES"]; // Third user

跳到標題

Deno KV 中的值可以是與結構化複製演算法相容的任意 JavaScript 值。這包括

  • undefined
  • null
  • boolean
  • number
  • string
  • bigint
  • Uint8Array
  • Array
  • Object
  • Map
  • Set
  • Date
  • RegExp

物件和陣列可以包含上述任何型別,包括其他物件和陣列。MapSet 可以包含上述任何型別,包括其他 MapSet

支援值內的循環參考。

不支援具有非原始原型的物件 (例如類別實例或 Web API 物件)。函數和符號也不能序列化。

Deno.KvU64 型別 跳到標題

除了結構化可序列化值之外,特殊值 Deno.KvU64 也支援作為值。此物件表示 64 位元無號整數,表示為 bigint。它可以與 summinmax KV 操作一起使用。它不能儲存在物件或陣列中。它必須儲存為最上層值。

可以使用 Deno.KvU64 建構函式建立它

const u64 = new Deno.KvU64(42n);

值範例 跳到標題

undefined;
null;
true;
false;
42;
-42.5;
42n;
"hello";
new Uint8Array([1, 2, 3]);
[1, 2, 3];
{ a: 1, b: 2, c: 3 };
new Map([["a", 1], ["b", 2], ["c", 3]]);
new Set([1, 2, 3]);
new Date("2023-04-23");
/abc/;

// Circular references are supported
const a = {};
const b = { a };
a.b = b;

// Deno.KvU64 is supported
new Deno.KvU64(42n);

版本戳記 跳到標題

Deno KV 鍵空間中的所有資料都已版本化。每次插入或修改值時,都會為其指派版本戳記。版本戳記是單調遞增、非循序的 12 位元組值,表示值被修改的時間。版本戳記不代表實際時間,而是代表值被修改的順序。

由於版本戳記是單調遞增的,因此它們可用於判斷給定值比另一個值更新還是更舊。這可以透過比較兩個值的版本戳記來完成。如果版本戳記 A 大於版本戳記 B,則值 A 比值 B 更晚修改。

versionstampA > versionstampB;
"000002fa526aaccb0000" > "000002fa526aacc90000"; // true

單一交易修改的所有資料都會被指派相同的版本戳記。這表示如果在相同的原子操作中執行兩個 set 操作,則新值的版本戳記將會相同。

版本戳記用於實作樂觀並行控制。原子操作可以包含檢查,以確保它們正在操作的資料的版本戳記與傳遞給操作的版本戳記相符。如果資料的版本戳記與傳遞給操作的版本戳記不同,則交易將會失敗,並且操作將不會套用。

您找到需要的資訊了嗎?

隱私權政策