在掌握了 Aerospike 的基本讀寫後,要進一步發揮其高效能潛力,就必須熟悉其豐富的寫入策略與進階操作指令。本文將以一個「購物車」應用為例,深入探討如何利用 WritePolicy 控制資料寫入行為,如何使用 touchexists 等輕量級操作提升效能,並最終展示如何透過強大的 operate() 命令,在單次請求中原子性地執行多個複雜操作。

第一部分:寫入策略 (WritePolicy)

WritePolicy 讓開發者能精確控制 put() 操作在遇到已存在或不存在的記錄時的行為。這對於防止資料意外覆蓋或確保資料的唯一性至關重要。

Java (RecordExistsAction)Python (policy['exists'])行為描述適用場景
CREATE_ONLYPOLICY_EXISTS_CREATE僅在記錄不存在時建立,若已存在則失敗。建立新使用者帳號,確保使用者名稱唯一。
UPDATEPOLICY_EXISTS_UPDATE若記錄存在則更新 Bins,若不存在則建立。(預設行為)大多數常規的資料更新。
REPLACEPOLICY_EXISTS_REPLACE若記錄存在則完全替換所有 Bins,若不存在則建立。覆蓋整個使用者設定檔。
UPDATE_ONLYPOLICY_EXISTS_UPDATE_ONLY僅在記錄存在時更新,若不存在則失敗。更新一個已確認存在的訂單狀態。
REPLACE_ONLYPOLICY_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 單記錄操作的瑞士軍刀,它允許我們在一次原子性的伺服器往返中,依序執行多個讀寫操作。

實戰:向購物車新增商品

我們的目標是,在向購物車新增一件商品時,原子性地完成三件事:

  1. 將商品名稱 appenditems List 中。
  2. 將商品總數 totalItems 的值 add 1。
  3. 將商品價格 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 在高效能場景下的優勢。