Redis:(6) 三级缓存+连接池与高性能 Redis 客户端封装

1. Redis 客户端架构

1.1 结构

cpp 复制代码
+---------------------+
|     应用业务逻辑     | ← 调用 get/set 等方法
+---------------------+
|RedisClient (统一接口)|
+---------------------+
|  ┌──────────────┐   |
|  │ 布隆过滤器    │   | ← L0: 快速判断"一定不存在"
|  └──────────────┘   |
|  ┌──────────────┐   |
|  │ 本地LRU缓存   │   | ← L1: 进程内高速缓存
|  └──────────────┘   |
|  ┌──────────────┐   |
|  │  Redis缓存    │   | ← L2: 分布式共享缓存
|  └──────────────┘   |
+---------------------+
|      数据库/源系统   |
+---------------------+

1.2 布隆过滤器

  • 作用 :作为第一道防线,快速、高效地判断一个 key 是否"绝对不存在"
  • 原理:基于位数组和多个哈希函数的概率型数据结构。
  • 特性
    • 无漏判:如果说"不存在",那它就一定不存在。
    • 有误判:如果说"可能存在",则有一定概率是误判
  • 代码实现
    • set()hSet() 等写操作时,会调用 addToBloomFilter(key) 将 key 加入过滤器。
    • get(), hGet() 等读操作时,首先检查布隆过滤器。如果返回 false,直接返回 filtered(),避免后续所有开销。

1.3 本地 LRU 缓存

  • 读穿透 (Read-Through) :当 Redis 命中时,会自动回填 (updateLocalCache) 到本地缓存。
  • 写穿透 (Write-Through)set() 操作会同时更新 Redis 和本地缓存。
  • 失效策略 :写操作(如 incr, del)会主动 invalidateLocalCache,保证一致性。
  • 空值处理 :支持 setNull(),防止对数据库中确实不存在的数据反复穿透。

1.4 Redis 缓存

  • 通过 RedisConnectionPool 管理连接,保证资源复用和性能。
  • 封装了 RedisCache 类,提供对 String, Hash, List, Set, ZSet 等数据结构的操作。
  • 所有操作最终都委托给这个模块。

1.5 连接池

1.5.1 为什么需要连接池?

  • Redis 连接是昂贵的 :每次 connect() 涉及 TCP 握手、认证、选库等开销;
  • 频繁创建/销毁连接 会导致:
    • 性能下降(延迟高);
    • Redis 服务器压力大(连接数突增);
    • 端口耗尽(TIME_WAIT 积累)。

1.5.2 连接生命周期管理

cpp 复制代码
std::queue<RedisConnection::Ptr> idleConnections_; // 空闲连接队列
std::atomic<int> currentSize_{0};                 // 当前总连接数
  • 空闲队列:存放可复用的连接;
  • 计数器:原子变量,线程安全地跟踪连接总数;
  • 智能指针shared_ptr 自动管理内存,避免泄漏。

1.5.3 线程安全模型

cpp 复制代码
std::mutex poolMutex_;           // 保护队列和状态
std::condition_variable poolCond_; // 实现等待/唤醒
  • 互斥锁:确保对 idleConnections_ 的操作是原子的;
  • 条件变量:实现"无连接时等待,有连接时唤醒"的高效协作。

1.5.4 获取连接的优先级策略

在 getConnection() 中,按以下顺序尝试:

  1. 直接取空闲连接(最快,零开销);
  2. 创建新连接(若未达上限);
  3. 等待超时(若有其他线程归还连接)。

1.5.5 连接有效性检查

cpp 复制代码
if (conn->isConnected()) { ... } else { discard; }
  • 防止返回已断开的连接(如网络闪断后);
  • 归还时也检查:无效连接直接销毁,不污染池。

2. 源码

2.1 redis_client.h

cpp 复制代码
/**
 * @file redis_client.h
 * @brief Redis客户端统一接口 - 三级缓存架构
 * @author DFS Team
 * @date 2025-02-22
 * 
 * @version 1.0.0
 * - 整合布隆过滤器、LRU本地缓存、Redis缓存为统一客户端
 * - 实现三级缓存架构,提供高效的数据访问能力
 * 
 * 架构说明:
 * ┌─────────────────────────────────────────────────────────────────┐
 * │                     三级缓存查询流程                              │
 * │                                                                 │
 * │   ┌──────────────┐    ┌──────────────┐    ┌──────────────┐     │
 * │   │ 布隆过滤器    │───►│  LRU本地缓存  │───►│  Redis缓存   │     │
 * │   │ (L0-快速判断) │    │  (L1-进程内)  │    │  (L2-分布式)  │     │
 * │   └──────────────┘    └──────────────┘    └──────────────┘     │
 * │          │                   │                   │              │
 * │          ▼                   ▼                   ▼              │
 * │   可能存在→继续          命中→返回           命中→返回+回填       │
 * │   一定不存在→拦截        未命中→继续         未命中→返回空        │
 * └─────────────────────────────────────────────────────────────────┘
 * 
 * 缓存更新策略:
 * 1. 读穿透(Read-Through):查询时自动回填上层缓存
 * 2. 写穿透(Write-Through):写入时同步更新Redis,失效本地缓存
 * 3. 缓存预热:启动时加载热点数据到布隆过滤器和本地缓存
 * 
 * 一致性保证:
 * 1. 本地缓存:短TTL(秒级),适合读多写少场景
 * 2. Redis缓存:长TTL(分钟级),作为分布式共享缓存
 * 3. 写操作:先更新Redis,再删除本地缓存(延迟双删可选)
 * 
 * 线程安全:
 * - 所有公共方法线程安全,可多线程并发调用
 * - 内部使用mutex保护共享状态
 */

#ifndef DFS_REDIS_REDIS_CLIENT_H
#define DFS_REDIS_REDIS_CLIENT_H

#include "bloom_filter.h"
#include "local_cache.h"
#include "redis_cache.h"
#include "redis_connection_pool.h"
#include <optional>
#include <vector>
#include <unordered_map>
#include <string>
#include <memory>
#include <mutex>
#include <atomic>

namespace dfs {
namespace redis {

/**
 * @struct RedisClientConfig
 * @brief Redis客户端配置结构体
 * 
 * 包含三级缓存架构所需的所有配置参数
 */
struct RedisClientConfig {
    RedisConfig redisConfig;              // Redis连接配置
    BloomFilterConfig bloomConfig;        // 布隆过滤器配置
    size_t localCacheMaxSize = 10000;     // 本地缓存最大条目数
    int localCacheDefaultTtl = 300;       // 本地缓存默认TTL(秒)
    int redisDefaultTtl = 3600;           // Redis缓存默认TTL(秒)
    int maxPoolSize = 10;                 // 连接池最大连接数
    int minPoolSize = 2;                  // 连接池最小连接数
    bool enableBloomFilter = true;        // 是否启用布隆过滤器
    bool enableLocalCache = true;         // 是否启用本地缓存
};

/**
 * @enum CacheLevel
 * @brief 缓存层级枚举,标识数据来源
 */
enum class CacheLevel {
    NONE = 0,         // 未命中任何缓存
    BLOOM_FILTER = 1, // 布隆过滤器拦截(不存在)
    LOCAL_CACHE = 2,  // 本地缓存命中
    REDIS_CACHE = 3   // Redis缓存命中
};

/**
 * @struct CacheResult
 * @brief 缓存查询结果结构体
 * 
 * 包含查询结果值及其来源信息
 */
template<typename T>
struct CacheResult {
    std::optional<T> value;    // 缓存值(nullopt表示未命中或不存在)
    CacheLevel level;          // 数据来源层级
    bool certain;              // 是否确定不存在(布隆过滤器拦截时为true)
    
    CacheResult() : level(CacheLevel::NONE), certain(false) {}
    explicit CacheResult(const T& v, CacheLevel l) 
        : value(v), level(l), certain(true) {}
    
    bool hit() const { return value.has_value(); }
    bool missed() const { return !value.has_value() && !certain; }
    bool filtered() const { return !value.has_value() && certain; }
};

/**
 * @class RedisClient
 * @brief Redis客户端统一接口类
 * 
 * 整合布隆过滤器、LRU本地缓存、Redis缓存,提供统一的数据访问接口。
 * 实现三级缓存架构,优化查询性能,防止缓存穿透。
 * 
 * 【使用示例】
 * @code
 * // 初始化客户端
 * RedisClientConfig config;
 * config.redisConfig.host = "127.0.0.1";
 * config.redisConfig.port = 6379;
 * config.localCacheMaxSize = 5000;
 * 
 * auto client = std::make_shared<RedisClient>(config);
 * if (!client->init()) {
 *     // 初始化失败处理
 * }
 * 
 * // 查询数据
 * auto result = client->get("user:123");
 * if (result.hit()) {
 *     std::cout << "Got: " << result.value.value() << " from level: " << (int)result.level;
 * } else if (result.filtered()) {
 *     std::cout << "Key definitely not exists (bloom filter)";
 * } else {
 *     // 未命中,查询数据库并回填缓存
 *     std::string dbValue = queryFromDatabase("user:123");
 *     if (!dbValue.empty()) {
 *         client->set("user:123", dbValue);
 *     } else {
 *         client->setNull("user:123"); // 防止缓存穿透
 *     }
 * }
 * @endcode
 */
class RedisClient {
public:
    using Ptr = std::shared_ptr<RedisClient>;

    explicit RedisClient(const RedisClientConfig& config);
    ~RedisClient() = default;
    RedisClient(const RedisClient&) = delete;
    RedisClient& operator=(const RedisClient&) = delete;
    
    // =========================================================================
    // 初始化与状态管理
    // =========================================================================
    bool init(); // 初始化客户端
    void close(); // 关闭客户端,释放资源
    bool isInitialized() const; // 检查客户端是否已初始化
    
    // =========================================================================
    // 核心数据操作 - String类型
    // =========================================================================
    
    /**
     * @brief 三级缓存查询
     * @param key 键名
     * @return CacheResult<std::string> 查询结果
     * 
     * 查询流程:
     * 1. 布隆过滤器判断 -> 可能存在则继续,一定不存在则返回filtered
     * 2. 本地缓存查询 -> 命中则返回
     * 3. Redis缓存查询 -> 命中则回填本地缓存并返回
     * 
     * @note 线程安全
     */
    CacheResult<std::string> get(const std::string& key);
    
    /**
     * @brief 设置键值对(写穿透)
     * @param key 键名
     * @param value 值
     * @param ttlSeconds 过期时间(秒),-1使用默认值
     * @return true 设置成功
     * 
     * 写入流程:
     * 1. 写入Redis缓存
     * 2. 添加到布隆过滤器
     * 3. 更新本地缓存
     */
    bool set(const std::string& key, const std::string& value, int ttlSeconds = -1);
    
    /**
     * @brief 删除键(缓存失效)
     * @param key 键名
     * @return true 删除成功
     * 
     * 删除流程:
     * 1. 删除Redis缓存
     * 2. 删除本地缓存
     * 3. (可选)从布隆过滤器移除(布隆过滤器不支持删除)
     */
    bool del(const std::string& key);
    
    /**
     * @brief 检查键是否存在
     * @param key 键名
     * @return true 存在
     * @note 先查本地缓存,再查Redis
     */
    bool exists(const std::string& key);
    
    /**
     * @brief 设置空值占位(防止缓存穿透)
     * @param key 键名
     * @param ttlSeconds 空值缓存时间(秒)
     * 
     * 当数据库查询结果为空时,缓存空值防止频繁穿透
     */
    void setNull(const std::string& key, int ttlSeconds = 60);
    
    /**
     * @brief 检查是否为空值占位
     * @param key 键名
     * @return true 是空值占位
     */
    bool isNull(const std::string& key);
    
    /**
     * @brief 自增操作
     * @param key 键名
     * @return int64_t 自增后的值
     * @note 直接操作Redis,本地缓存会被失效
     */
    int64_t incr(const std::string& key);
    
    /**
     * @brief 自增指定值
     * @param key 键名
     * @param increment 增量
     * @return int64_t 自增后的值
     */
    int64_t incrBy(const std::string& key, int64_t increment);
    
    /**
     * @brief 自减操作
     * @param key 键名
     * @return int64_t 自减后的值
     */
    int64_t decr(const std::string& key);
    
    /**
     * @brief 自减指定值
     * @param key 键名
     * @param decrement 减量
     * @return int64_t 自减后的值
     */
    int64_t decrBy(const std::string& key, int64_t decrement);
    
    // =========================================================================
    // Hash类型操作
    // =========================================================================
    
    /**
     * @brief 设置Hash字段
     * @param key 键名
     * @param field 字段名
     * @param value 字段值
     * @return true 成功
     */
    bool hSet(const std::string& key, const std::string& field, const std::string& value);
    
    /**
     * @brief 获取Hash字段
     * @param key 键名
     * @param field 字段名
     * @return CacheResult<std::string> 查询结果
     */
    CacheResult<std::string> hGet(const std::string& key, const std::string& field);
    
    /**
     * @brief 删除Hash字段
     * @param key 键名
     * @param field 字段名
     * @return true 删除成功
     */
    bool hDel(const std::string& key, const std::string& field);
    
    /**
     * @brief 获取Hash所有字段
     * @param key 键名
     * @return std::unordered_map<std::string, std::string> 字段-值映射
     * @note 直接查询Redis,不经过本地缓存
     */
    std::unordered_map<std::string, std::string> hGetAll(const std::string& key);
    
    /**
     * @brief Hash字段自增
     * @param key 键名
     * @param field 字段名
     * @param increment 增量
     * @return int64_t 自增后的值
     */
    int64_t hIncrBy(const std::string& key, const std::string& field, int64_t increment);
    
    // =========================================================================
    // List类型操作
    // =========================================================================
    
    /**
     * @brief 左侧推入列表
     * @param key 键名
     * @param value 值
     * @return int64_t 列表长度
     */
    int64_t lPush(const std::string& key, const std::string& value);
    
    /**
     * @brief 右侧推入列表
     * @param key 键名
     * @param value 值
     * @return int64_t 列表长度
     */
    int64_t rPush(const std::string& key, const std::string& value);
    
    /**
     * @brief 左侧弹出元素
     * @param key 键名
     * @return std::optional<std::string> 元素值
     */
    std::optional<std::string> lPop(const std::string& key);
    
    /**
     * @brief 右侧弹出元素
     * @param key 键名
     * @return std::optional<std::string> 元素值
     */
    std::optional<std::string> rPop(const std::string& key);
    
    /**
     * @brief 获取列表范围
     * @param key 键名
     * @param start 起始索引
     * @param stop 结束索引
     * @return std::vector<std::string> 元素列表
     */
    std::vector<std::string> lRange(const std::string& key, int64_t start, int64_t stop);
    
    /**
     * @brief 获取列表长度
     * @param key 键名
     * @return int64_t 长度
     */
    int64_t lLen(const std::string& key);
    
    // =========================================================================
    // Set类型操作
    // =========================================================================
    
    /**
     * @brief 添加集合成员
     * @param key 键名
     * @param member 成员
     * @return int64_t 新添加的成员数量
     */
    int64_t sAdd(const std::string& key, const std::string& member);
    
    /**
     * @brief 移除集合成员
     * @param key 键名
     * @param member 成员
     * @return int64_t 被移除的成员数量
     */
    int64_t sRem(const std::string& key, const std::string& member);
    
    /**
     * @brief 判断成员是否在集合中
     * @param key 键名
     * @param member 成员
     * @return true 在集合中
     */
    bool sIsMember(const std::string& key, const std::string& member);
    
    /**
     * @brief 获取集合所有成员
     * @param key 键名
     * @return std::vector<std::string> 成员列表
     */
    std::vector<std::string> sMembers(const std::string& key);
    
    // =========================================================================
    // ZSet类型操作
    // =========================================================================
    
    /**
     * @brief 添加有序集合成员
     * @param key 键名
     * @param member 成员
     * @param score 分数
     * @return int64_t 新添加的成员数量
     */
    int64_t zAdd(const std::string& key, const std::string& member, double score);
    
    /**
     * @brief 移除有序集合成员
     * @param key 键名
     * @param member 成员
     * @return int64_t 被移除的成员数量
     */
    int64_t zRem(const std::string& key, const std::string& member);
    
    /**
     * @brief 获取成员分数
     * @param key 键名
     * @param member 成员
     * @return std::optional<double> 分数
     */
    std::optional<double> zScore(const std::string& key, const std::string& member);
    
    /**
     * @brief 获取成员排名
     * @param key 键名
     * @param member 成员
     * @return int64_t 排名(0-based)
     */
    int64_t zRank(const std::string& key, const std::string& member);
    
    /**
     * @brief 获取指定排名范围的成员
     * @param key 键名
     * @param start 起始排名
     * @param stop 结束排名
     * @return std::vector<std::string> 成员列表
     */
    std::vector<std::string> zRange(const std::string& key, int64_t start, int64_t stop);
    
private:
    std::string buildLocalCacheKey(const std::string& key, const std::string& field = "");
    void updateLocalCache(const std::string& key, const std::string& value, int ttlSeconds);
    void invalidateLocalCache(const std::string& key);
    void addToBloomFilter(const std::string& key);

    RedisClientConfig config_;
    std::shared_ptr<RedisConnectionPool> connectionPool_;
    RedisCache::Ptr redisCache_;
    std::unique_ptr<BloomFilter> bloomFilter_;
    std::unique_ptr<LocalCache> localCache_;
    std::atomic<bool> initialized_{false};
};
} // namespace redis
} // namespace dfs
#endif // DFS_REDIS_REDIS_CLIENT_H

2.2 redis_client.cpp

cpp 复制代码
/**
 * @file redis_client.cpp
 * @brief Redis客户端统一接口实现 - 三级缓存架构
 * @author DFS Team
 * @date 2025-02-22
 * 
 * 实现细节:
 * 1. 三级缓存查询流程:布隆过滤器 -> 本地缓存 -> Redis缓存
 * 2. 写穿透策略:先写Redis,再更新本地缓存
 * 3. 缓存一致性:写操作时失效本地缓存
 * 4. 防缓存穿透:空值缓存 + 布隆过滤器
 */

#include "redis_client.h"
#include "../common/logger.h"

namespace dfs {
namespace redis {

// =============================================================================
// 构造函数与初始化
// =============================================================================

RedisClient::RedisClient(const RedisClientConfig& config) : config_(config) {}

bool RedisClient::init() {
    if (initialized_.load()) {
        LOG_WARN("RedisClient already initialized");
        return true;
    }

    // 1. 初始化连接池
    connectionPool_ = std::make_shared<RedisConnectionPool>(
        config_.redisConfig, 
        config_.maxPoolSize, 
        config_.minPoolSize
    );
    
    if (!connectionPool_->init()) {
        LOG_ERROR("Failed to initialize Redis connection pool");
        return false;
    }

    // 2. 初始化RedisCache
    redisCache_ = std::make_shared<RedisCache>(connectionPool_);

    // 3. 初始化布隆过滤器
    if (config_.enableBloomFilter) {
        bloomFilter_ = std::make_unique<BloomFilter>(config_.bloomConfig);
        LOG_INFO("Bloom filter initialized, bitSize=%zu, hashCount=%zu",
                 bloomFilter_->getBitSize(), bloomFilter_->getHashCount());
    }

    // 4. 初始化本地缓存
    if (config_.enableLocalCache) {
        localCache_ = std::make_unique<LocalCache>(
            config_.localCacheMaxSize, 
            config_.localCacheDefaultTtl
        );
        LOG_INFO("Local cache initialized, maxSize=%zu, defaultTtl=%d",
                 config_.localCacheMaxSize, config_.localCacheDefaultTtl);
    }

    initialized_.store(true);
    LOG_INFO("RedisClient initialized successfully");
    return true;
}

void RedisClient::close() {
    if (!initialized_.load()) {
        return;
    }

    initialized_.store(false);

    // 按依赖关系逆序释放
    localCache_.reset();
    bloomFilter_.reset();
    redisCache_.reset();
    connectionPool_->close();

    LOG_INFO("RedisClient closed");
}

bool RedisClient::isInitialized() const {
    return initialized_.load();
}


// =============================================================================
// 核心数据操作 - String类型
// =============================================================================

CacheResult<std::string> RedisClient::get(const std::string& key) {
    CacheResult<std::string> result;

    // 【第1级】布隆过滤器检查
    if (config_.enableBloomFilter && bloomFilter_) {
        if (!bloomFilter_->mightContain(key)) {
            // 一定不存在,直接返回
            result.level = CacheLevel::BLOOM_FILTER;
            result.certain = true;
            LOG_DEBUG("Bloom filter miss for key: %s", key.c_str());
            return result;
        }
    }

    // 【第2级】本地缓存查询
    if (config_.enableLocalCache && localCache_) {
        // 先检查是否为空值占位
        if (localCache_->isNull(key)) {
            result.level = CacheLevel::LOCAL_CACHE;
            result.certain = true;
            return result;
        }

        auto localValue = localCache_->get(key);
        if (localValue.has_value()) {
            result.value = localValue;
            result.level = CacheLevel::LOCAL_CACHE;
            result.certain = true;
            LOG_DEBUG("Local cache hit for key: %s", key.c_str());
            return result;
        }
    }

    // 【第3级】Redis缓存查询
    auto redisValue = redisCache_->get(key);
    if (redisValue.has_value()) {
        result.value = redisValue;
        result.level = CacheLevel::REDIS_CACHE;
        result.certain = true;

        // 回填本地缓存
        if (config_.enableLocalCache && localCache_) {
            localCache_->set(key, redisValue.value(), config_.localCacheDefaultTtl);
        }

        LOG_DEBUG("Redis cache hit for key: %s", key.c_str());
        return result;
    }

    // 未命中任何缓存
    result.level = CacheLevel::NONE;
    result.certain = false;

    LOG_DEBUG("Cache miss for key: %s", key.c_str());
    return result;
}

bool RedisClient::set(const std::string& key, const std::string& value, int ttlSeconds) {
    // 1. 写入Redis
    int actualTtl = ttlSeconds > 0 ? ttlSeconds : config_.redisDefaultTtl;
    if (!redisCache_->set(key, value, actualTtl)) {
        LOG_ERROR("Failed to set key in Redis: %s", key.c_str());
        return false;
    }

    // 2. 添加到布隆过滤器
    addToBloomFilter(key);

    // 3. 更新本地缓存
    int localTtl = ttlSeconds > 0 ? ttlSeconds : config_.localCacheDefaultTtl;
    updateLocalCache(key, value, localTtl);

    LOG_DEBUG("Set key: %s, ttl=%d", key.c_str(), actualTtl);
    return true;
}

bool RedisClient::del(const std::string& key) {
    // 1. 删除Redis缓存
    bool redisDeleted = redisCache_->del(key);

    // 2. 删除本地缓存
    if (config_.enableLocalCache && localCache_) {
        localCache_->del(key);
    }

    // 注意:布隆过滤器不支持删除,这是其局限性
    // 如需支持删除,可考虑使用布隆过滤器的变体(如Counting Bloom Filter)

    LOG_DEBUG("Deleted key: %s, redisResult=%d", key.c_str(), redisDeleted);
    return redisDeleted;
}

bool RedisClient::exists(const std::string& key) {
    // 先查本地缓存
    if (config_.enableLocalCache && localCache_) {
        if (localCache_->exists(key)) {
            return true;
        }
    }

    // 再查Redis
    return redisCache_->exists(key);
}

void RedisClient::setNull(const std::string& key, int ttlSeconds) {
    // 在本地缓存中设置空值标记
    if (config_.enableLocalCache && localCache_) {
        localCache_->setNull(key, ttlSeconds);
    }

    // 可选:在Redis中也设置空值标记
    // redisCache_->set(key, "NULL", ttlSeconds);

    LOG_DEBUG("Set null placeholder for key: %s, ttl=%d", key.c_str(), ttlSeconds);
}

bool RedisClient::isNull(const std::string& key) {
    if (config_.enableLocalCache && localCache_) {
        return localCache_->isNull(key);
    }
    return false;
}

int64_t RedisClient::incr(const std::string& key) {
    // 自增操作直接走Redis,并失效本地缓存
    invalidateLocalCache(key);
    return redisCache_->incr(key);
}

int64_t RedisClient::incrBy(const std::string& key, int64_t increment) {
    invalidateLocalCache(key);
    return redisCache_->incrBy(key, increment);
}

int64_t RedisClient::decr(const std::string& key) {
    invalidateLocalCache(key);
    return redisCache_->decr(key);
}

int64_t RedisClient::decrBy(const std::string& key, int64_t decrement) {
    invalidateLocalCache(key);
    return redisCache_->decrBy(key, decrement);
}

// =============================================================================
// Hash类型操作
// =============================================================================

bool RedisClient::hSet(const std::string& key, const std::string& field, const std::string& value) {
    // 写入Redis
    bool success = redisCache_->hSet(key, field, value);
    if (success) {
        // 添加到布隆过滤器
        addToBloomFilter(key);
        // 失效本地缓存(Hash操作较复杂,简化处理)
        invalidateLocalCache(buildLocalCacheKey(key, field));
    }
    return success;
}

CacheResult<std::string> RedisClient::hGet(const std::string& key, const std::string& field) {
    CacheResult<std::string> result;
    std::string localKey = buildLocalCacheKey(key, field);

    // 布隆过滤器检查
    if (config_.enableBloomFilter && bloomFilter_) {
        if (!bloomFilter_->mightContain(key)) {
            result.level = CacheLevel::BLOOM_FILTER;
            result.certain = true;
            return result;
        }
    }

    // 本地缓存查询
    if (config_.enableLocalCache && localCache_) {
        auto localValue = localCache_->get(localKey);
        if (localValue.has_value()) {
            result.value = localValue;
            result.level = CacheLevel::LOCAL_CACHE;
            result.certain = true;
            return result;
        }
    }

    // Redis查询
    auto redisValue = redisCache_->hGet(key, field);
    if (redisValue.has_value()) {
        result.value = redisValue;
        result.level = CacheLevel::REDIS_CACHE;
        result.certain = true;

        // 回填本地缓存
        if (config_.enableLocalCache && localCache_) {
            localCache_->set(localKey, redisValue.value(), config_.localCacheDefaultTtl);
        }
        return result;
    }

    result.level = CacheLevel::NONE;
    result.certain = false;
    return result;
}

bool RedisClient::hDel(const std::string& key, const std::string& field) {
    bool success = redisCache_->hDel(key, field);
    if (success) {
        invalidateLocalCache(buildLocalCacheKey(key, field));
    }
    return success;
}

std::unordered_map<std::string, std::string> RedisClient::hGetAll(const std::string& key) {
    // HGETALL直接查询Redis,不经过本地缓存(数据量可能较大)
    return redisCache_->hGetAll(key);
}

int64_t RedisClient::hIncrBy(const std::string& key, const std::string& field, int64_t increment) {
    invalidateLocalCache(buildLocalCacheKey(key, field));
    return redisCache_->hIncrBy(key, field, increment);
}

// =============================================================================
// List类型操作
// =============================================================================

int64_t RedisClient::lPush(const std::string& key, const std::string& value) {
    // List操作直接走Redis
    invalidateLocalCache(key);
    return redisCache_->lPush(key, value);
}

int64_t RedisClient::rPush(const std::string& key, const std::string& value) {
    invalidateLocalCache(key);
    return redisCache_->rPush(key, value);
}

std::optional<std::string> RedisClient::lPop(const std::string& key) {
    invalidateLocalCache(key);
    return redisCache_->lPop(key);
}

std::optional<std::string> RedisClient::rPop(const std::string& key) {
    invalidateLocalCache(key);
    return redisCache_->rPop(key);
}

std::vector<std::string> RedisClient::lRange(const std::string& key, int64_t start, int64_t stop) {
    // LRANGE直接查询Redis
    return redisCache_->lRange(key, start, stop);
}

int64_t RedisClient::lLen(const std::string& key) {
    return redisCache_->lLen(key);
}

// =============================================================================
// Set类型操作
// =============================================================================

int64_t RedisClient::sAdd(const std::string& key, const std::string& member) {
    invalidateLocalCache(key);
    int64_t result = redisCache_->sAdd(key, member);
    if (result > 0) {
        addToBloomFilter(key);
    }
    return result;
}

int64_t RedisClient::sRem(const std::string& key, const std::string& member) {
    invalidateLocalCache(key);
    return redisCache_->sRem(key, member);
}

bool RedisClient::sIsMember(const std::string& key, const std::string& member) {
    // 布隆过滤器检查
    if (config_.enableBloomFilter && bloomFilter_) {
        if (!bloomFilter_->mightContain(key)) {
            return false;
        }
    }
    return redisCache_->sIsMember(key, member);
}

std::vector<std::string> RedisClient::sMembers(const std::string& key) {
    return redisCache_->sMembers(key);
}

// =============================================================================
// ZSet类型操作
// =============================================================================

int64_t RedisClient::zAdd(const std::string& key, const std::string& member, double score) {
    invalidateLocalCache(key);
    int64_t result = redisCache_->zAdd(key, member, score);
    if (result > 0) {
        addToBloomFilter(key);
    }
    return result;
}

int64_t RedisClient::zRem(const std::string& key, const std::string& member) {
    invalidateLocalCache(key);
    return redisCache_->zRem(key, member);
}

std::optional<double> RedisClient::zScore(const std::string& key, const std::string& member) {
    // 布隆过滤器检查
    if (config_.enableBloomFilter && bloomFilter_) {
        if (!bloomFilter_->mightContain(key)) {
            return std::nullopt;
        }
    }
    return redisCache_->zScore(key, member);
}

int64_t RedisClient::zRank(const std::string& key, const std::string& member) {
    return redisCache_->zRank(key, member);
}

std::vector<std::string> RedisClient::zRange(const std::string& key, int64_t start, int64_t stop) {
    return redisCache_->zRange(key, start, stop);
}


// =============================================================================
// 私有辅助方法
// =============================================================================

std::string RedisClient::buildLocalCacheKey(const std::string& key, const std::string& field) {
    if (field.empty()) {
        return key;
    }
    return key + ":" + field;
}

void RedisClient::updateLocalCache(const std::string& key, const std::string& value, int ttlSeconds) {
    if (config_.enableLocalCache && localCache_) {
        localCache_->set(key, value, ttlSeconds);
    }
}

void RedisClient::invalidateLocalCache(const std::string& key) {
    if (config_.enableLocalCache && localCache_) {
        localCache_->del(key);
    }
}

void RedisClient::addToBloomFilter(const std::string& key) {
    if (config_.enableBloomFilter && bloomFilter_) {
        bloomFilter_->add(key);
    }
}

} // namespace redis
} // namespace dfs

3. Redis 客户端使用

cpp 复制代码
/**
 * @file redis_client_example.cpp
 * @brief RedisClient三级缓存使用示例
 * @author DFS Team
 * @date 2025-02-22
 */

#include <iostream>
#include <memory>
#include <string>
#include <chrono>

#include "../redis_client.h"

using namespace dfs::redis;

std::string queryFromDatabase(const std::string& key) {
    std::cout << "[DB] 查询数据库: " << key << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    
    if (key.find("user:") == 0) {
        std::string id = key.substr(5);
        return "{\"id\":" + id + ",\"name\":\"User" + id + "\"}";
    }
    return "";
}

void example_basicStringOps(RedisClient::Ptr& client) {
    std::cout << "\n========== 示例1: String基础操作 ==========\n" << std::endl;
    
    client->set("app:name", "DFS-System", 300);
    std::cout << "[SET] app:name = DFS-System, TTL=300s" << std::endl;
    
    CacheResult<std::string> result = client->get("app:name");
    if (result.hit()) {
        std::cout << "[GET] 命中, 层级=" << static_cast<int>(result.level) 
                  << ", 值=" << result.value.value() << std::endl;
    }
    
    int64_t counter = client->incr("app:counter");
    std::cout << "[INCR] app:counter = " << counter << std::endl;
    
    counter = client->incrBy("app:counter", 10);
    std::cout << "[INCRBY] app:counter += 10 -> " << counter << std::endl;
    
    counter = client->decr("app:counter");
    std::cout << "[DECR] app:counter = " << counter << std::endl;
    
    bool exists = client->exists("app:name");
    std::cout << "[EXISTS] app:name = " << (exists ? "存在" : "不存在") << std::endl;
    
    client->del("app:name");
    std::cout << "[DEL] app:name 已删除" << std::endl;
    
    exists = client->exists("app:name");
    std::cout << "[EXISTS] app:name = " << (exists ? "存在" : "不存在") << std::endl;
}

void example_threeLevelCache(RedisClient::Ptr& client) {
    std::cout << "\n========== 示例2: 三级缓存查询流程 ==========\n" << std::endl;
    
    std::string key = "user:1001";
    
    std::cout << "--- 第一次查询(缓存未命中)---" << std::endl;
    CacheResult<std::string> result1 = client->get(key);
    
    if (result1.hit()) {
        std::cout << "[命中] 值=" << result1.value.value() << std::endl;
    } else if (result1.filtered()) {
        std::cout << "[布隆过滤] 键确定不存在" << std::endl;
    } else {
        std::cout << "[未命中] 层级=" << static_cast<int>(result1.level) << std::endl;
        
        std::string dbValue = queryFromDatabase(key);
        if (!dbValue.empty()) {
            client->set(key, dbValue, 300);
            std::cout << "[回填] 已写入缓存" << std::endl;
        }
    }
    
    std::cout << "\n--- 第二次查询(本地缓存命中)---" << std::endl;
    CacheResult<std::string> result2 = client->get(key);
    
    if (result2.hit()) {
        std::string levelName;
        switch (result2.level) {
            case CacheLevel::LOCAL_CACHE: levelName = "L1-本地缓存"; break;
            case CacheLevel::REDIS_CACHE: levelName = "L2-Redis缓存"; break;
            default: levelName = "未知"; break;
        }
        std::cout << "[命中] 层级=" << levelName << ", 值=" << result2.value.value() << std::endl;
    }
}

void example_cachePenetration(RedisClient::Ptr& client) {
    std::cout << "\n========== 示例3: 防止缓存穿透 ==========\n" << std::endl;
    
    std::string key = "user:99999";
    
    std::cout << "--- 查询不存在的键 ---" << std::endl;
    CacheResult<std::string> result = client->get(key);
    
    if (result.hit()) {
        std::cout << "[命中] 值=" << result.value.value() << std::endl;
    } else if (result.filtered()) {
        std::cout << "[布隆过滤] 键确定不存在,已拦截穿透" << std::endl;
    } else {
        std::string dbValue = queryFromDatabase(key);
        
        if (dbValue.empty()) {
            client->setNull(key, 60);
            std::cout << "[空值占位] 设置NULL标记,TTL=60s,防止穿透" << std::endl;
        }
    }
    
    std::cout << "\n--- 再次查询(命中空值占位)---" << std::endl;
    if (client->isNull(key)) {
        std::cout << "[空值检测] 该键为空值占位,数据库无此记录" << std::endl;
    }
}

void example_hashOps(RedisClient::Ptr& client) {
    std::cout << "\n========== 示例4: Hash操作 ==========\n" << std::endl;
    
    std::string key = "user:profile:2001";
    
    client->hSet(key, "name", "张三");
    client->hSet(key, "age", "25");
    client->hSet(key, "city", "北京");
    std::cout << "[HSET] 写入用户资料字段" << std::endl;
    
    CacheResult<std::string> nameResult = client->hGet(key, "name");
    if (nameResult.hit()) {
        std::cout << "[HGET] name=" << nameResult.value.value() << std::endl;
    }
    
    int64_t age = client->hIncrBy(key, "age", 1);
    std::cout << "[HINCRBY] age += 1 -> " << age << std::endl;
    
    auto allFields = client->hGetAll(key);
    std::cout << "[HGETALL] 所有字段:" << std::endl;
    for (const auto& [field, value] : allFields) {
        std::cout << "  " << field << " = " << value << std::endl;
    }
    
    client->hDel(key, "city");
    std::cout << "[HDEL] 已删除 city 字段" << std::endl;
}

void example_listOps(RedisClient::Ptr& client) {
    std::cout << "\n========== 示例5: List操作 ==========\n" << std::endl;
    
    std::string key = "queue:tasks";
    
    client->rPush(key, "task1");
    client->rPush(key, "task2");
    client->rPush(key, "task3");
    std::cout << "[RPUSH] 写入3个任务" << std::endl;
    
    int64_t len = client->lLen(key);
    std::cout << "[LLEN] 队列长度=" << len << std::endl;
    
    auto items = client->lRange(key, 0, -1);
    std::cout << "[LRANGE] 所有元素:" << std::endl;
    for (const auto& item : items) {
        std::cout << "  " << item << std::endl;
    }
    
    auto popped = client->lPop(key);
    if (popped.has_value()) {
        std::cout << "[LPOP] 弹出=" << popped.value() << std::endl;
    }
    
    client->del(key);
}

void example_setOps(RedisClient::Ptr& client) {
    std::cout << "\n========== 示例6: Set操作 ==========\n" << std::endl;
    
    std::string key = "tags:article:1";
    
    client->sAdd(key, "redis");
    client->sAdd(key, "cache");
    client->sAdd(key, "distributed");
    std::cout << "[SADD] 添加3个标签" << std::endl;
    
    bool isMember = client->sIsMember(key, "redis");
    std::cout << "[SISMEMBER] redis=" << (isMember ? "存在" : "不存在") << std::endl;
    
    auto members = client->sMembers(key);
    std::cout << "[SMEMBERS] 所有标签:" << std::endl;
    for (const auto& m : members) {
        std::cout << "  " << m << std::endl;
    }
    
    client->sRem(key, "cache");
    std::cout << "[SREM] 已移除 cache" << std::endl;
    
    client->del(key);
}

void example_zsetOps(RedisClient::Ptr& client) {
    std::cout << "\n========== 示例7: ZSet操作 ==========\n" << std::endl;
    
    std::string key = "leaderboard:daily";
    
    client->zAdd(key, "player1", 100.0);
    client->zAdd(key, "player2", 200.0);
    client->zAdd(key, "player3", 150.0);
    std::cout << "[ZADD] 添加3名玩家分数" << std::endl;
    
    auto score = client->zScore(key, "player2");
    if (score.has_value()) {
        std::cout << "[ZSCORE] player2 分数=" << score.value() << std::endl;
    }
    
    int64_t rank = client->zRank(key, "player3");
    std::cout << "[ZRANK] player3 排名=" << rank << std::endl;
    
    auto topPlayers = client->zRange(key, 0, 2);
    std::cout << "[ZRANGE] 前三名:" << std::endl;
    for (size_t i = 0; i < topPlayers.size(); ++i) {
        std::cout << "  " << (i + 1) << ". " << topPlayers[i] << std::endl;
    }
    
    client->zRem(key, "player1");
    std::cout << "[ZREM] 已移除 player1" << std::endl;
    
    client->del(key);
}

int main() {
    std::cout << "========================================" << std::endl;
    std::cout << "       RedisClient 三级缓存示例" << std::endl;
    std::cout << "========================================" << std::endl;
    
    RedisClientConfig config;
    config.redisConfig.host = "127.0.0.1";
    config.redisConfig.port = 6379;
    config.redisConfig.password = "";
    config.redisConfig.timeout = 3000;
    config.redisConfig.database = 0;
    
    config.bloomConfig.expectedInsertions = 100000;
    config.bloomConfig.falsePositiveProbability = 0.01;
    
    config.localCacheMaxSize = 5000;
    config.localCacheDefaultTtl = 300;
    config.redisDefaultTtl = 3600;
    config.maxPoolSize = 10;
    config.minPoolSize = 2;
    config.enableBloomFilter = true;
    config.enableLocalCache = true;
    
    auto client = std::make_shared<RedisClient>(config);
    
    std::cout << "\n[初始化] 连接Redis..." << std::endl;
    
    if (!client->init()) {
        std::cerr << "[错误] 初始化失败,请检查Redis服务" << std::endl;
        return 1;
    }
    
    std::cout << "[成功] 已连接" << std::endl;
    std::cout << "  布隆过滤器: " << (config.enableBloomFilter ? "启用" : "禁用") << std::endl;
    std::cout << "  本地缓存: " << (config.enableLocalCache ? "启用" : "禁用") << std::endl;
    
    try {
        example_basicStringOps(client);
        example_threeLevelCache(client);
        example_cachePenetration(client);
        example_hashOps(client);
        example_listOps(client);
        example_setOps(client);
        example_zsetOps(client);
    } catch (const std::exception& e) {
        std::cerr << "[异常] " << e.what() << std::endl;
        client->close();
        return 1;
    }
    
    std::cout << "\n========================================" << std::endl;
    std::cout << "       示例执行完成" << std::endl;
    std::cout << "========================================" << std::endl;
    
    client->close();
    return 0;
}
相关推荐
m0_738120721 小时前
渗透测试——Momentum靶机渗透提取详细教程(XSS漏洞解密Cookie,SS获取信息,Redis服务利用)
前端·redis·安全·web安全·ssh·php·xss
渣瓦攻城狮2 小时前
浜掕仈缃戝ぇ鍘侸ava闈㈣瘯锛氫弗鑲冮潰璇曞畼涓庢悶绗戠▼搴忓憳璋㈤鏈虹殑瀵硅瘽
jvm·redis·docker·springboot·java闈㈣瘯·澶氱嚎绋�·璁捐妯″紡
山岚的运维笔记2 小时前
SQL Server笔记 -- 第78章:MS SQL Server 基本 DDL 操作
数据库·笔记·sql·microsoft·oracle·sqlserver
Albert Edison8 小时前
【Python】学生管理系统
开发语言·数据库·python
heimeiyingwang11 小时前
企业供应链 AI 优化:需求预测与智能调度
大数据·数据库·人工智能·机器学习
山岚的运维笔记12 小时前
SQL Server笔记 -- 第73章:排序/对行进行排序
数据库·笔记·后端·sql·microsoft·sqlserver
XLYcmy12 小时前
智能体大赛 目录
数据库·ai·llm·prompt·agent·检索·万方
盟接之桥12 小时前
盟接之桥EDI软件:API数据采集模块深度解析,打造企业数据协同新引擎
java·运维·服务器·网络·数据库·人工智能·制造
闲人编程14 小时前
内存数据库性能调优
数据库·redis·字符串·高并发·哈希·内存碎片