要高效地使用 Aerospike,必須深入理解其核心的資料操作 (CRUD) 流程,以及如何利用其豐富的資料型別和策略 (Policy) 來管理資料的完整生命週期。本文將引導您走過一筆記錄從「誕生」到「消亡」的全過程,並在其中詳細解析資料型別的應用、寫入策略的控制,以及如何實現安全的併發更新。
第一部分:資料的誕生 - 建立 (Create)
在 Aerospike 中建立一筆記錄,就是使用 put 方法將一或多個「Bin」寫入一個指定的「Key」。
1. 豐富的資料型別
Aerospike 的 Bin 支援多種資料型別,且客戶端驅動程式會自動進行轉換,實現語言無關的資料儲存。
| 資料型別 | 描述與範例 |
|---|---|
| Integer | 64 位元有符號整數。42, -12 |
| Double | 64 位元浮點數。3.14159 |
| String | UTF-8 編碼字串。"Hello, Aerospike!" |
| Boolean | true 或 false。 |
| List | 有序集合,可包含不同型別的元素。[1, "apple", true] |
| Map | 鍵值對集合,鍵必須是純量型別。{"name": "John", "age": 30} |
| Blob | 二進位原始位元組,可用於儲存序列化物件或圖片。 |
| GeoJSON | 用於地理空間查詢的幾何物件。 |
| HyperLogLog | 用於高效計算集合基數的機率性資料結構。 |
2. 寫入資料
Java 範例:建立一個包含多種資料型別的記錄
// 定義記錄的唯一 Key
Key key = new Key("test", "demo", "user123");
// 建立多個不同型別的 Bin
Bin nameBin = new Bin("name", "玄貓");
Bin ageBin = new Bin("age", 5);
Bin skillsBin = new Bin("skills", Arrays.asList("Python", "Aerospike")); // List
Map<String, Object> metadataMap = new HashMap<>();
metadataMap.put("isActive", true);
Bin metadataBin = new Bin("metadata", metadataMap); // Map
// 使用 put 方法寫入
client.put(null, key, nameBin, ageBin, skillsBin, metadataBin);
第二部分:資料的檢索 - 讀取 (Read)
使用 get 方法可以根據 Key 讀取記錄。為了提升效能,我們可以只讀取需要的 Bins。
Java 範例:只讀取 name 和 skills 兩個 Bin
// 第二個之後的參數為要讀取的 Bin 名稱列表
Record record = client.get(null, key, "name", "skills");
String name = record.getString("name");
List<Object> skills = (List<Object>) record.getList("skills");
System.out.println("Name: " + name); // -> Name: 玄貓
System.out.println("Skills: " + skills); // -> Skills: [Python, Aerospike]
第三部分:資料的演變 - 更新 (Update)
Aerospike 的 put 操作預設行為是「更新或插入」(Upsert),即如果記錄存在,則更新指定的 Bins;如果不存在,則建立新記錄。然而,在多執行緒環境下,直接的「讀取-修改-寫入」操作可能導致競爭條件 (Race Condition)。
安全更新:檢查並設定 (Check-And-Set, CAS)
為了解決這個問題,Aerospike 提供了基於 generation (世代) 計數的 CAS 機制。
- 讀取: 首先
get記錄,並獲取其中繼資料中的generation值。 - 寫入: 在執行
put操作時,設定WritePolicy,要求伺服器只有在資料庫中記錄的generation與我們提供的值相同時,才執行寫入。如果在此期間有其他執行緒修改了該記錄,其generation會增加,導致我們的寫入操作失敗。
Java 範例:安全地為 age 加 1
// 1. 讀取記錄及其 generation
Record record = client.get(null, key);
int currentGeneration = record.generation;
// 2. 在客戶端計算新值
int newAge = record.getInt("age") + 1;
// 3. 設定 CAS 寫入策略
WritePolicy casPolicy = new WritePolicy();
casPolicy.generationPolicy = GenerationPolicy.EXPECT_GEN_EQUAL;
casPolicy.generation = currentGeneration;
// 4. 執行帶有 CAS 策略的寫入
try {
client.put(casPolicy, key, new Bin("age", newAge));
System.out.println("年齡更新成功!");
} catch (AerospikeException e) {
if (e.getResultCode() == ResultCode.GENERATION_ERROR) {
System.out.println("寫入失敗:資料已被其他程序修改,請重試。");
}
}
圖表解說:CAS 安全更新流程
此循序圖展示了如何利用 generation 值來防止更新衝突。
@startuml
!theme _none_
skinparam dpi auto
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam minClassWidth 100
skinparam defaultFontSize 16
title CAS (Check-And-Set) 安全更新流程
participant "Client" as C
participant "Aerospike Server" as S
C -> S : get(key)
S --> C : Record (..., generation=5)
C -> C : 計算新值 (age = age + 1)
C -> S : put(key, new_age, policy(EXPECT_GEN_EQUAL, generation=5))
S -> S : 比較客戶端 generation (5)\n與伺服器端 generation (5)
alt generation 相符
S -> S : 執行寫入,並將 generation 更新為 6
S --> C : 回應成功
else generation 不符 (例如已被更新為 6)
S --> C : 回應失敗 (GENERATION_ERROR)
end
@enduml第四部分:資料的消亡 - TTL 與刪除
- TTL (Time-To-Live): 我們可以在寫入時透過
WritePolicy設定expiration欄位,讓記錄在指定的秒數後自動被伺服器刪除。這對於管理快取或有時效性的資料非常有用。WritePolicy ttlPolicy = new WritePolicy(); ttlPolicy.expiration = 3600; // 記錄將在 1 小時後過期 client.put(ttlPolicy, key, ...); - 主動刪除: 也可以使用
client.delete(null, key)來立即刪除一筆記錄。
透過掌握資料的完整生命週期操作,並善用 Aerospike 提供的策略控制,開發者可以建構出既高效又穩健的應用程式。