在掌握了 Aerospike 的基本讀寫後,要進一步發揮其高效能潛力,就必須熟悉其豐富的寫入策略與進階操作指令。本文將以一個「購物車」應用為例,深入探討如何利用 WritePolicy 控制資料寫入行為,如何使用 touch 和 exists 等輕量級操作提升效能,並最終展示如何透過強大的 operate() 命令,在單次請求中原子性地執行多個複雜操作。
第一部分:寫入策略 (WritePolicy)
WritePolicy 讓開發者能精確控制 put() 操作在遇到已存在或不存在的記錄時的行為。這對於防止資料意外覆蓋或確保資料的唯一性至關重要。
Java (RecordExistsAction) | Python (policy['exists']) | 行為描述 | 適用場景 |
|---|---|---|---|
CREATE_ONLY | POLICY_EXISTS_CREATE | 僅在記錄不存在時建立,若已存在則失敗。 | 建立新使用者帳號,確保使用者名稱唯一。 |
UPDATE | POLICY_EXISTS_UPDATE | 若記錄存在則更新 Bins,若不存在則建立。(預設行為) | 大多數常規的資料更新。 |
REPLACE | POLICY_EXISTS_REPLACE | 若記錄存在則完全替換所有 Bins,若不存在則建立。 | 覆蓋整個使用者設定檔。 |
UPDATE_ONLY | POLICY_EXISTS_UPDATE_ONLY | 僅在記錄存在時更新,若不存在則失敗。 | 更新一個已確認存在的訂單狀態。 |
REPLACE_ONLY | POLICY_EXISTS_REPLACE_ONLY | 僅在記錄存在時替換,若不存在則失敗。 | 同上,但為完全覆蓋。 |
Java 範例:建立一個新的購物車,防止覆蓋
WritePolicy createOnlyPolicy = new WritePolicy();
// 設定策略為僅建立
createOnlyPolicy.recordExistsAction = RecordExistsAction.CREATE_ONLY;
Key cartKey = new Key("test", "cart", "user123");
Bin initialItem = new Bin("items", new ArrayList<String>());
try {
client.put(createOnlyPolicy, cartKey, initialItem);
System.out.println("成功為 user123 建立新購物車。");
} catch (AerospikeException e) {
if (e.getResultCode() == ResultCode.KEY_EXISTS_ERROR) {
System.out.println("錯誤:user123 的購物車已存在,無法重複建立。");
}
}
第二部分:輕量級操作與批次操作
1. 輕量級操作 (touch & exists)
有時我們只想更新記錄的中繼資料或檢查其是否存在,而不想傳輸整個記錄的資料。
touch: 此操作僅用於更新記錄的 TTL (存活時間) 或 generation (世代) 數。例如,當使用者將商品加入購物車時,我們可以touch購物車記錄,將其過期時間延長 24 小時,這是一個非常高效的操作。WritePolicy touchPolicy = new WritePolicy(); touchPolicy.expiration = 86400; // 延長 TTL 為 1 天 client.touch(touchPolicy, cartKey);exists: 此操作僅回傳一個布林值,告知記錄是否存在,而不會讀取任何 Bin 的內容,網路開銷極小。
2. 批次操作 (get_many)
當需要一次性讀取多個記錄時,使用批次操作 (get 的多載版本或 Python 的 get_many) 可以將多次網路請求合併為一次,大幅提升效率。
# 一次性檢查多個使用者是否有購物車
keys = [
('test', 'cart', 'user123'),
('test', 'cart', 'user456'),
]
records = client.get_many(keys)
# records 將是一個列表,包含對應鍵的查詢結果
第三部分:強大的 operate() 原子命令
operate() 是 Aerospike 單記錄操作的瑞士軍刀,它允許我們在一次原子性的伺服器往返中,依序執行多個讀寫操作。
實戰:向購物車新增商品
我們的目標是,在向購物車新增一件商品時,原子性地完成三件事:
- 將商品名稱
append到itemsList 中。 - 將商品總數
totalItems的值add1。 - 將商品價格
add到總花費cost中。
Java 範例:
public static void addItem(IAerospikeClient client, Key key, String itemName, double itemCost) {
client.operate(null, key,
ListOperation.append("items", Value.get(itemName)), // 操作 1: 新增 List 元素
Operation.add(new Bin("totalItems", 1)), // 操作 2: 整數加 1
Operation.add(new Bin("cost", itemCost)) // 操作 3: 浮點數相加
);
}
// 使用
addItem(client, cartKey, "shoes", 59.25);
addItem(client, cartKey, "jeans", 29.95);
程式碼解說:
ListOperation.append(): 專門用於操作 List 型別的 CDT 操作。Operation.add(): 對數字型別的 Bin 進行原子性的加法操作。- 原子性保證: 這三個操作要麼全部成功,要麼全部失敗。不會出現
items增加了但cost沒變的情況。
圖表解說:operate() 命令執行流程
此循序圖展示了客戶端如何透過一次 operate() 呼叫,在伺服器端原子性地執行多個子操作。
@startuml
!theme _none_
skinparam dpi auto
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam minClassWidth 100
skinparam defaultFontSize 16
title operate() 原子操作循序圖
participant "Client" as Client
participant "Aerospike Server" as Server
Client -> Server : operate(ListOperation.append, Operation.add, Operation.add)
Server -> Server : <b>Transaction Start</b>
Server -> Server : 1. 執行 ListOperation.append
Server -> Server : 2. 執行 Operation.add (totalItems)
Server -> Server : 3. 執行 Operation.add (cost)
Server -> Server : <b>Transaction Commit</b>
Server --> Client : 回應成功
@enduml透過靈活運用 WritePolicy、輕量級操作以及強大的 operate() 命令,開發者可以建構出既高效又穩健的資料處理邏輯,充分發揮 Aerospike 在高效能場景下的優勢。