hiredis — Redis 客户端
hiredis 是 Redis 官方 C 客户端,轻量高效。配合 hiredis-vip 或 redis-plus-plus 可获得更好的 C++ 体验。
安装
bash
# Ubuntu
sudo apt install libhiredis-dev
# vcpkg
vcpkg install hiredis redis-plus-plus
# CMake(redis-plus-plus)
find_package(redis++ REQUIRED)
target_link_libraries(myapp PRIVATE redis++::redis++ hiredis::hiredis)redis-plus-plus(推荐)
cpp
#include <sw/redis++/redis++.h>
using namespace sw::redis;
// 连接
Redis redis("tcp://127.0.0.1:6379");
Redis redis_auth("tcp://:password@127.0.0.1:6379");
// 连接池
ConnectionOptions opts;
opts.host = "127.0.0.1";
opts.port = 6379;
opts.password = "password";
opts.db = 0;
ConnectionPoolOptions pool_opts;
pool_opts.size = 10;
pool_opts.wait_timeout = std::chrono::milliseconds(100);
Redis redis_pool(opts, pool_opts);基本数据类型
cpp
// String
redis.set("key", "value");
redis.set("key", "value", std::chrono::seconds(60)); // 带过期时间
auto val = redis.get("key"); // Optional<std::string>
if (val) std::cout << *val << "\n";
redis.incr("counter");
redis.incrby("counter", 10);
redis.decr("counter");
// 批量操作
redis.mset({{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}});
auto vals = redis.mget({"k1", "k2", "k3"});
for (auto& v : vals) {
if (v) std::cout << *v << "\n";
}
// Hash
redis.hset("user:1", "name", "Alice");
redis.hset("user:1", "age", "30");
redis.hmset("user:1", {{"name", "Alice"}, {"age", "30"}, {"email", "alice@example.com"}});
auto name = redis.hget("user:1", "name");
auto all = redis.hgetall("user:1"); // unordered_map<string, string>
redis.hincrby("user:1", "age", 1);
redis.hdel("user:1", "email");
// List
redis.lpush("queue", "task1");
redis.rpush("queue", "task2", "task3");
auto item = redis.lpop("queue"); // Optional<string>
auto bitem = redis.blpop({"queue"}, 5); // 阻塞等待 5 秒
redis.lrange("queue", 0, -1); // 获取所有元素
redis.llen("queue");
// Set
redis.sadd("tags", "cpp", "programming", "systems");
bool is_member = redis.sismember("tags", "cpp");
auto members = redis.smembers("tags"); // unordered_set<string>
redis.srem("tags", "cpp");
// Sorted Set
redis.zadd("leaderboard", {{"Alice", 100.0}, {"Bob", 85.0}, {"Charlie", 92.0}});
auto rank = redis.zrank("leaderboard", "Alice"); // 排名(0-indexed)
auto score = redis.zscore("leaderboard", "Alice");
auto top3 = redis.zrevrange("leaderboard", 0, 2); // 前3名过期与键操作
cpp
// 设置过期时间
redis.expire("key", std::chrono::seconds(60));
redis.expireat("key", std::chrono::system_clock::now() + std::chrono::hours(1));
redis.persist("key"); // 移除过期时间
// 检查
redis.exists("key");
redis.ttl("key"); // 剩余秒数,-1 永不过期,-2 不存在
redis.type("key"); // "string", "hash", "list", "set", "zset"
// 删除
redis.del("key");
redis.del({"k1", "k2", "k3"});
// 模式匹配(生产环境慎用 keys,用 scan 代替)
auto keys = redis.keys("user:*"); // 阻塞!
// scan:非阻塞迭代
std::vector<std::string> all_keys;
auto cursor = 0LL;
while (true) {
cursor = redis.scan(cursor, "user:*", 100, std::back_inserter(all_keys));
if (cursor == 0) break;
}Pipeline(批量命令)
cpp
// Pipeline:减少网络往返,大幅提升吞吐量
auto pipe = redis.pipeline();
pipe.set("k1", "v1")
.set("k2", "v2")
.get("k1")
.get("k2")
.incr("counter");
auto replies = pipe.exec();
// replies[2] 是 get("k1") 的结果
auto v1 = replies.get<OptionalString>(2);事务(MULTI/EXEC)
cpp
// 事务:原子执行一组命令
auto tx = redis.transaction();
tx.multi();
tx.set("balance", "100");
tx.decrby("balance", 50);
tx.get("balance");
auto replies = tx.exec();
// Watch + 乐观锁
redis.watch("balance");
auto balance = redis.get("balance");
// 检查余额...
auto tx2 = redis.transaction(true); // true = watch 模式
try {
tx2.multi();
tx2.decrby("balance", 50);
auto r = tx2.exec(); // 如果 balance 被其他客户端修改,exec 返回空
} catch (const WatchError& e) {
// 重试
}Pub/Sub
cpp
// 发布者
redis.publish("channel", "hello");
// 订阅者(阻塞)
auto sub = redis.subscriber();
sub.on_message([](std::string channel, std::string msg) {
std::cout << channel << ": " << msg << "\n";
});
sub.subscribe("channel");
sub.psubscribe("user:*"); // 模式订阅
// 在独立线程中消费
std::thread([&sub] {
while (true) {
try {
sub.consume();
} catch (const Error& e) {
std::cerr << e.what() << "\n";
}
}
}).detach();缓存模式
cpp
// Cache-Aside 模式
template<typename T>
std::optional<T> get_with_cache(Redis& redis, const std::string& key,
std::function<T()> loader,
std::chrono::seconds ttl = std::chrono::seconds(300)) {
// 先查缓存
auto cached = redis.get(key);
if (cached) {
return deserialize<T>(*cached);
}
// 缓存未命中,查数据库
T value = loader();
redis.set(key, serialize(value), ttl);
return value;
}
// 使用
auto user = get_with_cache<User>(redis, "user:1",
[&] { return db.find_user(1); },
std::chrono::minutes(10));关键认知
redis-plus-plus 是 hiredis 的现代 C++ 封装,API 简洁且线程安全。Pipeline 是提升吞吐量的关键,批量操作时必用。生产环境用连接池,避免频繁创建连接。scan 代替 keys,避免阻塞 Redis。