Hiredis 构建 Redis 命令实战指南

发布于:2025-07-21 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、同步命令构造

1.1 redisCommand(fmt, …)

最常用的同步接口即 redisCommand,其原型如下:

void *redisCommand(redisContext *c, const char *format, ...);
  • 参数

    • c:已连接的 redisContext*
    • format:与 printf 类似的格式字符串
// 基本用法:发送 SET foo bar
redisReply *reply = redisCommand(c, "SET foo bar");

// 插值字符串:发送 SET key:1 Hello
char *myKeyNumber = "1", *myValue = "Hello";
redisReply *r2 = redisCommand(c, "SET key:%s %s", myKeyNumber, myValue);
支持二进制数据:%b

当需要存储或检索二进制数据(如向量嵌入)时,可使用 %b,并在后面传入指针与长度:

char *entryNumber = "1";
char *embedding = "<binary data>";
size_t embLen = 13;
char *url = "https://redis.io/";

redisReply *r3 = redisCommand(c,
    "HSET entry:%s embedding %b url %s",
    entryNumber,
    embedding, embLen,
    url
);

注意:除了 %% 外,不支持其它 printf 规格(例如 %d)。

1.2 redisCommandArgv(argv, argvlen)

当参数来自数组或包含多段二进制时,直接使用 redisCommandArgv 更安全:

void *redisCommandArgv(
    redisContext *c,
    int argc,
    const char **argv,
    const size_t *argvlen
);
  • 示例:发送 SET greeting hello
const char *argv[]     = {"SET", "greeting", "hello"};
const size_t argvlen[] = {3,       8,          5};
int argc = 3;

redisReply *reply = redisCommandArgv(c, argc, argv, argvlen);

若传入 argvlen=NULL,Hiredis 会调用 strlen(),但遇二进制数据仍应显式提供长度数组。

二、异步命令构造

对于高并发场景,非阻塞地发送命令并处理回调极为关键。Hiredis 提供与同步接口对应的异步版本。

2.1 基础接口

int redisAsyncCommand(
    redisAsyncContext *ac,
    redisCallbackFn *fn,
    void *privdata,
    const char *format,
    ...
);

int redisAsyncCommandArgv(
    redisAsyncContext *ac,
    redisCallbackFn *fn,
    void *privdata,
    int argc,
    const char **argv,
    const size_t *argvlen
);
  • 参数

    • ac:异步上下文 redisAsyncContext*
    • fn:可选的回调函数,命令执行完毕后触发
    • privdata:用户自定义数据,回调时原样传回
    • format / argv:与同步接口一致

2.2 回调函数签名

void getCallback(redisAsyncContext *c, void *reply, void *privdata) {
    redisReply *r = reply;
    char *key = privdata;

    if (!r) {
        if (c->errstr) printf("errstr: %s\n", c->errstr);
        return;
    }
    printf("Key: %s, value: %s\n", key, r->str);

    // 使用完毕后优雅断开
    redisAsyncDisconnect(c);
}

2.3 示例:混合使用

// 1. 异步 SET,不关心结果
char *key = "testkey", *value = "testvalue";
redisAsyncCommand(ac, NULL, NULL, "SET %s %s", key, value);

// 2. 异步 GET,需回调处理
redisAsyncCommand(ac, getCallback, key, "GET %s", key);

异步上下文在事件循环中调度,执行完所有回调后,可在回调内调用 redisAsyncDisconnect(ac) 进行优雅断开;若需立即释放,使用 redisAsyncFree(ac)

三、处理命令回复

所有命令回复均通过 redisReply 结构体返回,其 type 字段指示具体格式(字符串、整数、数组等),详见 Hiredis 文档的“Handle replies”章节。务必检查 reply->typereply->str / reply->integer 等字段,以确保正确解析。

四、最佳实践与性能优化

  1. 二进制安全:凡涉及原始字节,统一使用带长度参数的 %bargvlen,避免 strlen() 截断。
  2. 复用 Context:长连接或连接池可显著减少频繁连接带来的开销。
  3. 批量命令:结合 MULTI/EXEC 或 Lua 脚本,减少网络往返次数。
  4. 异步模式:高并发场景优先考虑异步接口,并合理使用回调释放资源。
  5. 错误处理:检查 c->errac->errstrreply->type == REDIS_REPLY_ERROR,以便日志告警和重试。

五、总结

Hiredis 虽然接口简洁,但通过 redisCommand/redisCommandArgvredisAsyncCommand/redisAsyncCommandArgv,可完全满足同步与异步场景下对 Redis 的灵活访问需求。结合合适的错误检查、连接复用及批量操作设计,您的 C/C++ 应用即可实现高性能、低延迟的 Redis 通信能力。希望本文能帮助您快速上手 Hiredis,并在生产环境中游刃有余地构造各类 Redis 命令。