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.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;
}