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 系統的類關係:

Spring Cloud Alibaba-全面詳解(學習總結---從入門到深化)_命名空間

這種設計避免了特殊字符衝突,確保鍵的唯一性。

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 使用 ConcurrentHashMapReadWriteLock 確保線程安全
  • 其他實現依賴底層存儲的線程安全機制

4. 鍵編碼設計

使用 Base64 編碼避免特殊字符問題:

  • 支持任意命名空間和鍵名
  • 確保鍵的唯一性
  • 便於序列化和反序列化

5. 搜索優化

BaseStore 提供通用的搜索匹配邏輯:

  • 命名空間前綴匹配
  • 文本包含匹配
  • 自定義過濾匹配
  • 多字段排序支持

總結説明

核心設計理念

  1. 統一接口Store 接口定義統一的存儲操作,屏蔽底層實現差異
  2. 層次化組織:通過命名空間支持數據的層次化組織
  3. 靈活查詢:支持多種搜索和過濾方式
  4. 可擴展性:通過接口和抽象類支持新的存儲實現

支持的存儲模式

  1. 內存模式(MemoryStore):適合測試和開發
  2. 文件系統模式(FileSystemStore):適合單機部署
  3. 數據庫模式(DatabaseStore):適合生產環境,支持事務
  4. 緩存模式(RedisStore):適合高性能場景
  5. 文檔數據庫模式(MongoStore):適合非結構化數據

關鍵優勢

  • 一致性:所有實現遵循相同的接口規範
  • 可替換性:不同實現可以無縫替換
  • 可擴展性:易於添加新的存儲實現
  • 類型安全:使用泛型和接口確保類型安全

使用場景

  • 長期記憶:Agent 的長期記憶存儲
  • 知識庫:存儲結構化知識
  • 用户數據:存儲用户偏好和配置
  • 會話數據:跨會話的數據持久化