Store 存儲系統分析
請關注微信公眾號:阿呆-bot
概述
本文檔分析 spring-ai-alibaba-graph-core 模塊中的 Store 存儲系統,包括接口設計、實現模式、支持的存儲類型和關鍵設計點。
入口類説明
Store - 存儲接口
Store 是長期內存存儲的接口,提供跨會話的持久化存儲能力,與 CheckpointSaver(短期狀態持久化)不同,Store 專注於長期數據管理。
核心職責:
- 提供層次化命名空間支持
- 支持結構化數據存儲(Map 格式)
- 提供搜索和過濾功能
- 支持分頁查詢
- 跨會話數據持久化
關鍵代碼:
public interface Store {
/**
* Store an item in the specified namespace with the given key. If an item with the
* same namespace and key already exists, it will be updated.
* @param item the item to store
* @throws IllegalArgumentException if item is null or invalid
*/
void putItem(StoreItem item);
/**
* Retrieve an item from the specified namespace with the given key.
* @param namespace the hierarchical namespace path
* @param key the item key
* @return Optional containing the item if found, empty otherwise
* @throws IllegalArgumentException if namespace or key is null/invalid
*/
Optional<StoreItem> getItem(List<String> namespace, String key);
/**
* Delete an item from the specified namespace with the given key.
* @param namespace the hierarchical namespace path
* @param key the item key
* @return true if the item was deleted, false if it didn't exist
* @throws IllegalArgumentException if namespace or key is null/invalid
*/
boolean deleteItem(List<String> namespace, String key);
/**
* Search for items based on the provided search criteria.
* @param searchRequest the search parameters
* @return search results with matching items
* @throws IllegalArgumentException if searchRequest is null
*/
StoreSearchResult searchItems(StoreSearchRequest searchRequest);
/**
* List available namespaces based on the provided criteria.
* @param namespaceRequest the namespace listing parameters
* @return list of namespace paths
* @throws IllegalArgumentException if namespaceRequest is null
*/
List<String> listNamespaces(NamespaceListRequest namespaceRequest);
/**
* Clear all items from the store.
* <p>
* <strong>WARNING:</strong> This operation is irreversible and will remove all stored
* data.
* </p>
*/
void clear();
/**
* Get the total number of items in the store.
* @return the number of items stored
*/
long size();
/**
* Check if the store is empty.
* @return true if the store contains no items, false otherwise
*/
boolean isEmpty();
}
BaseStore - 抽象基類
BaseStore 為所有 Store 實現提供通用的驗證和工具方法,實現了模板方法模式。
核心職責:
- 提供參數驗證方法
- 提供鍵編碼/解碼工具
- 提供搜索匹配邏輯
- 提供排序比較器
關鍵代碼:
public abstract class BaseStore implements Store {
/**
* Validates the putItem parameters.
* @param item the item to validate
*/
protected void validatePutItem(StoreItem item) {
if (item == null) {
throw new IllegalArgumentException("item cannot be null");
}
if (item.getNamespace() == null) {
throw new IllegalArgumentException("namespace cannot be null");
}
if (item.getKey() == null || item.getKey().trim().isEmpty()) {
throw new IllegalArgumentException("key cannot be null or empty");
}
}
/**
* Validates the getItem parameters.
* @param namespace namespace
* @param key key
*/
protected void validateGetItem(List<String> namespace, String key) {
if (namespace == null) {
throw new IllegalArgumentException("namespace cannot be null");
}
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
if (key.trim().isEmpty()) {
throw new IllegalArgumentException("key cannot be empty");
}
}
/**
* Validates the deleteItem parameters.
* @param namespace namespace
* @param key key
*/
protected void validateDeleteItem(List<String> namespace, String key) {
if (namespace == null) {
throw new IllegalArgumentException("namespace cannot be null");
}
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
if (key.trim().isEmpty()) {
throw new IllegalArgumentException("key cannot be empty");
}
}
/**
* Validates the searchItems parameters.
* @param searchRequest search request
*/
protected void validateSearchItems(StoreSearchRequest searchRequest) {
if (searchRequest == null) {
throw new IllegalArgumentException("searchRequest cannot be null");
}
}
/**
* Validates the listNamespaces parameters.
* @param namespaceRequest namespace request
*/
protected void validateListNamespaces(NamespaceListRequest namespaceRequest) {
if (namespaceRequest == null) {
throw new IllegalArgumentException("namespaceRequest cannot be null");
}
}
/**
* Create store key from namespace and key Uses safe encoding to avoid conflicts from
* special characters.
* @param namespace namespace
* @param key key
* @return store key
*/
protected String createStoreKey(List<String> namespace, String key) {
try {
Map<String, Object> keyData = new HashMap<>();
keyData.put("namespace", namespace);
keyData.put("key", key);
return Base64.getEncoder().encodeToString(new ObjectMapper().writeValueAsBytes(keyData));
}
catch (Exception e) {
throw new RuntimeException("Failed to create store key", e);
}
}
/**
* Parse store key to namespace and key.
* @param storeKey store key
* @return array containing [namespace, key]
*/
@SuppressWarnings("unchecked")
protected Object[] parseStoreKey(String storeKey) {
try {
byte[] decoded = Base64.getDecoder().decode(storeKey);
Map<String, Object> keyData = new ObjectMapper().readValue(decoded, Map.class);
List<String> namespace = (List<String>) keyData.get("namespace");
String key = (String) keyData.get("key");
return new Object[] { namespace, key };
}
catch (Exception e) {
throw new RuntimeException("Failed to parse store key: " + storeKey, e);
}
}
StoreItem - 存儲項
StoreItem 表示存儲中的單個數據項,包含命名空間、鍵、值和時間戳。
關鍵代碼:
public class StoreItem implements Serializable {
/**
* The hierarchical namespace path for organizing items. For example: ["users",
* "user123", "preferences"]
*/
private List<String> namespace;
/**
* The unique key within the namespace.
*/
private String key;
/**
* The item value as a structured Map.
*/
private Map<String, Object> value;
/**
* Timestamp when the item was created (milliseconds since epoch).
*/
private long createdAt;
/**
* Timestamp when the item was last updated (milliseconds since epoch).
*/
private long updatedAt;
/**
* Default constructor for serialization frameworks.
*/
public StoreItem() {
}
/**
* Constructs a StoreItem with the specified namespace, key, and value. Timestamps are
* set to the current time.
* @param namespace the hierarchical namespace path
* @param key the item key
* @param value the item value
*/
public StoreItem(List<String> namespace, String key, Map<String, Object> value) {
this.namespace = namespace;
this.key = key;
this.value = value;
long now = System.currentTimeMillis();
this.createdAt = now;
this.updatedAt = now;
}
/**
* Constructs a StoreItem with full parameters including timestamps.
* @param namespace the hierarchical namespace path
* @param key the item key
* @param value the item value
* @param createdAt creation timestamp (milliseconds since epoch)
* @param updatedAt last update timestamp (milliseconds since epoch)
*/
public StoreItem(List<String> namespace, String key, Map<String, Object> value, long createdAt, long updatedAt) {
this.namespace = namespace;
this.key = key;
this.value = value;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
/**
* Static factory method to create a StoreItem.
* @param namespace the hierarchical namespace path
* @param key the item key
* @param value the item value
* @return a new StoreItem instance
*/
public static StoreItem of(List<String> namespace, String key, Map<String, Object> value) {
return new StoreItem(namespace, key, value);
}
Store 實現類
MemoryStore - 內存存儲
基於 ConcurrentHashMap 的內存實現,適用於測試和開發環境。
關鍵代碼:
public class MemoryStore extends BaseStore {
/**
* Thread-safe storage for store items. Key format: "namespace1/namespace2/key"
*/
private final Map<String, StoreItem> storage = new ConcurrentHashMap<>();
/**
* Read-write lock for thread safety during search operations.
*/
private final ReadWriteLock lock = new ReentrantReadWriteLock();
@Override
public void putItem(StoreItem item) {
validatePutItem(item);
lock.writeLock().lock();
try {
String storeKey = createStoreKey(item.getNamespace(), item.getKey());
storage.put(storeKey, item);
}
finally {
lock.writeLock().unlock();
}
}
@Override
public Optional<StoreItem> getItem(List<String> namespace, String key) {
validateGetItem(namespace, key);
lock.readLock().lock();
try {
String storeKey = createStoreKey(namespace, key);
return Optional.ofNullable(storage.get(storeKey));
}
finally {
lock.readLock().unlock();
}
}
特點:
- 線程安全(使用 ConcurrentHashMap 和 ReadWriteLock)
- 高性能(內存訪問)
- 數據不持久化(應用重啓後丟失)
FileSystemStore - 文件系統存儲
基於文件系統的持久化存儲實現。
特點:
- 數據持久化到本地文件系統
- 支持目錄結構組織
- 適合單機部署
DatabaseStore - 數據庫存儲
基於關係型數據庫(MySQL/PostgreSQL)的存儲實現。
特點:
- 支持事務
- 支持複雜查詢
- 適合生產環境
RedisStore - Redis 存儲
基於 Redis 的存儲實現。
特點:
- 高性能
- 支持分佈式
- 支持過期策略
MongoStore - MongoDB 存儲
基於 MongoDB 的存儲實現。
特點:
- 支持文檔存儲
- 支持複雜查詢
- 適合非結構化數據
關鍵類關係圖
以下 PlantUML 類圖展示了 Store 系統的類關係:
這種設計避免了特殊字符衝突,確保鍵的唯一性。
3. 搜索和過濾
StoreSearchRequest 支持多種搜索方式:
- 命名空間過濾:在指定命名空間下搜索
- 文本查詢:在鍵和值中搜索文本
- 自定義過濾:基於值的鍵值對過濾
- 排序:支持多字段排序
- 分頁:支持 offset 和 limit
關鍵代碼:
protected boolean matchesSearchCriteria(StoreItem item, StoreSearchRequest searchRequest) {
// Namespace filter
List<String> namespaceFilter = searchRequest.getNamespace();
if (!namespaceFilter.isEmpty() && !startsWithPrefix(item.getNamespace(), namespaceFilter)) {
return false;
}
// Text query filter
String query = searchRequest.getQuery();
if (query != null && !query.trim().isEmpty()) {
String lowerQuery = query.toLowerCase();
// Search in key
if (!item.getKey().toLowerCase().contains(lowerQuery)) {
// Search in value
String valueStr = item.getValue().toString().toLowerCase();
if (!valueStr.contains(lowerQuery)) {
return false;
}
}
}
// Custom filters
Map<String, Object> filters = searchRequest.getFilter();
if (!filters.isEmpty()) {
Map<String, Object> itemValue = item.getValue();
for (Map.Entry<String, Object> filter : filters.entrySet()) {
Object itemVal = itemValue.get(filter.getKey());
if (!Objects.equals(itemVal, filter.getValue())) {
return false;
}
}
}
return true;
}
實現關鍵點説明
1. 模板方法模式
BaseStore 實現了模板方法模式:
- 定義通用的驗證和工具方法
- 子類實現具體的存儲邏輯
- 確保所有實現的一致性
2. 策略模式
不同的 Store 實現代表不同的存儲策略:
MemoryStore:內存策略(快速但不持久)FileSystemStore:文件系統策略(持久但單機)DatabaseStore:數據庫策略(事務支持)RedisStore:緩存策略(高性能)MongoStore:文檔數據庫策略(靈活)
3. 線程安全
MemoryStore使用ConcurrentHashMap和ReadWriteLock確保線程安全- 其他實現依賴底層存儲的線程安全機制
4. 鍵編碼設計
使用 Base64 編碼避免特殊字符問題:
- 支持任意命名空間和鍵名
- 確保鍵的唯一性
- 便於序列化和反序列化
5. 搜索優化
BaseStore 提供通用的搜索匹配邏輯:
- 命名空間前綴匹配
- 文本包含匹配
- 自定義過濾匹配
- 多字段排序支持
總結説明
核心設計理念
- 統一接口:
Store接口定義統一的存儲操作,屏蔽底層實現差異 - 層次化組織:通過命名空間支持數據的層次化組織
- 靈活查詢:支持多種搜索和過濾方式
- 可擴展性:通過接口和抽象類支持新的存儲實現
支持的存儲模式
- 內存模式(MemoryStore):適合測試和開發
- 文件系統模式(FileSystemStore):適合單機部署
- 數據庫模式(DatabaseStore):適合生產環境,支持事務
- 緩存模式(RedisStore):適合高性能場景
- 文檔數據庫模式(MongoStore):適合非結構化數據
關鍵優勢
- 一致性:所有實現遵循相同的接口規範
- 可替換性:不同實現可以無縫替換
- 可擴展性:易於添加新的存儲實現
- 類型安全:使用泛型和接口確保類型安全
使用場景
- 長期記憶:Agent 的長期記憶存儲
- 知識庫:存儲結構化知識
- 用户數據:存儲用户偏好和配置
- 會話數據:跨會話的數據持久化