Skip to content

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。

系统学习 C++ 生态,深入底层架构