← 返回主页

hiredis 使用指南

hiredis 是一个轻量级的 C 语言 Redis 客户端库,提供了简单、高效的接口。它是 Redis 官方推荐的 C 客户端之一,专注于高性能和简洁的 API 设计。

📦 安装

Ubuntu/Debian

sudo apt-get install libhiredis-dev

macOS

brew install hiredis

从源码编译

git clone https://github.com/redis/hiredis.git
cd hiredis
make
sudo make install

头文件

#include <hiredis/hiredis.h>

🚀 基础使用

连接 Redis

#include <hiredis/hiredis.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 连接到 Redis(默认地址 127.0.0.1:6379)
    redisContext *c = redisConnect("127.0.0.1", 6379);
    
    if (c == NULL || c->err) {
        if (c) {
            printf("Connection error: %s\n", c->errstr);
            redisFree(c);
        } else {
            printf("Connection error: can't allocate redis context\n");
        }
        return 1;
    }

    printf("Connected to Redis\n");
    
    // 断开连接
    redisFree(c);
    return 0;
}

简单的 SET 和 GET

#include <hiredis/hiredis.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == NULL || c->err) {
        printf("Connection error: %s\n", c->errstr);
        return 1;
    }

    // SET key value
    redisReply *reply = (redisReply *)redisCommand(c, "SET %s %s", "name", "hiredis");
    printf("SET: %s\n", reply->str);
    freeReplyObject(reply);

    // GET key
    reply = (redisReply *)redisCommand(c, "GET %s", "name");
    printf("GET name: %s\n", reply->str);
    freeReplyObject(reply);

    redisFree(c);
    return 0;
}

处理不同类型的数据

#include <hiredis/hiredis.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == NULL || c->err) return 1;

    // 字符串操作
    redisReply *reply = (redisReply *)redisCommand(c, "SET %s %s", "counter", "100");
    freeReplyObject(reply);

    reply = (redisReply *)redisCommand(c, "INCR %s", "counter");
    printf("INCR counter: %lld\n", reply->integer);
    freeReplyObject(reply);

    // 列表操作 LPUSH
    reply = (redisReply *)redisCommand(c, "LPUSH mylist item1 item2 item3");
    printf("LPUSH: %lld\n", reply->integer);
    freeReplyObject(reply);

    // 列表操作 LRANGE
    reply = (redisReply *)redisCommand(c, "LRANGE mylist 0 -1");
    printf("LRANGE mylist:\n");
    for (size_t i = 0; i < reply->elements; i++) {
        printf("  [%zu]: %s\n", i, reply->element[i]->str);
    }
    freeReplyObject(reply);

    redisFree(c);
    return 0;
}

🔐 带密码连接

#include <hiredis/hiredis.h>
#include <stdio.h>

int main() {
    // 带密码连接
    redisContext *c = redisConnectWithTimeout(
        "127.0.0.1", 
        6379, 
        (struct timeval){.tv_sec = 2, .tv_usec = 0}
    );

    if (c == NULL || c->err) {
        printf("Connection error: %s\n", c->errstr);
        return 1;
    }

    // 认证
    redisReply *reply = (redisReply *)redisCommand(c, "AUTH %s", "your_password");
    if (reply->type == REDIS_REPLY_ERROR) {
        printf("AUTH failed: %s\n", reply->str);
    } else {
        printf("AUTH success\n");
    }
    freeReplyObject(reply);

    redisFree(c);
    return 0;
}

🔄 管道操作

#include <hiredis/hiredis.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == NULL || c->err) return 1;

    // 发送多个命令但不读取回复
    redisAppendCommand(c, "SET key1 value1");
    redisAppendCommand(c, "SET key2 value2");
    redisAppendCommand(c, "SET key3 value3");
    redisAppendCommand(c, "GET key1");
    redisAppendCommand(c, "GET key2");

    // 读取所有回复
    redisReply *reply;
    for (int i = 0; i < 5; i++) {
        redisGetReply(c, (void **)&reply);
        printf("Reply %d: ", i + 1);
        if (reply->type == REDIS_REPLY_STRING) {
            printf("%s\n", reply->str);
        } else if (reply->type == REDIS_REPLY_STATUS) {
            printf("%s\n", reply->str);
        } else {
            printf("(type: %d)\n", reply->type);
        }
        freeReplyObject(reply);
    }

    redisFree(c);
    return 0;
}

⚡ 异步连接

#include <hiredis/async.h>
#include <hiredis/adapters/libevent.h>
#include <event2/event.h>
#include <stdio.h>
#include <stdlib.h>

void connectCallback(const redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        printf("Connect error: %s\n", c->errstr);
        return;
    }
    printf("Connected to Redis (async)\n");
}

void disconnectCallback(const redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        printf("Disconnect error: %s\n", c->errstr);
        return;
    }
    printf("Disconnected\n");
}

void getCallback(redisAsyncContext *c, void *r, void *privdata) {
    redisReply *reply = (redisReply *)r;
    if (reply == NULL) return;
    
    printf("GET result: %s\n", reply->str);
}

int main() {
    struct event_base *base = event_base_new();

    redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
    if (c->err) {
        printf("Connection error: %s\n", c->errstr);
        return 1;
    }

    redisLibeventAttach(c, base);
    redisAsyncSetConnectCallback(c, connectCallback);
    redisAsyncSetDisconnectCallback(c, disconnectCallback);

    // 异步命令
    redisAsyncCommand(c, NULL, NULL, "SET %s %s", "async_key", "async_value");
    redisAsyncCommand(c, getCallback, NULL, "GET %s", "async_key");

    event_base_dispatch(base);

    event_base_free(base);
    return 0;
}

📊 处理不同回复类型

#include <hiredis/hiredis.h>
#include <stdio.h>
#include <stdlib.h>

void printReply(redisReply *reply) {
    switch (reply->type) {
        case REDIS_REPLY_STRING:
            printf("STRING: %s\n", reply->str);
            break;
        case REDIS_REPLY_ARRAY:
            printf("ARRAY (%zu elements):\n", reply->elements);
            for (size_t i = 0; i < reply->elements; i++) {
                printf("  [%zu] ", i);
                printReply(reply->element[i]);
            }
            break;
        case REDIS_REPLY_INTEGER:
            printf("INTEGER: %lld\n", reply->integer);
            break;
        case REDIS_REPLY_NIL:
            printf("NIL\n");
            break;
        case REDIS_REPLY_STATUS:
            printf("STATUS: %s\n", reply->str);
            break;
        case REDIS_REPLY_ERROR:
            printf("ERROR: %s\n", reply->str);
            break;
        default:
            printf("UNKNOWN type: %d\n", reply->type);
    }
}

int main() {
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == NULL || c->err) return 1;

    // 设置一些数据
    redisCommand(c, "SET key1 value1");
    redisCommand(c, "LPUSH list1 a b c d e");

    // 测试不同类型的回复
    redisReply *reply;

    // STRING
    reply = (redisReply *)redisCommand(c, "GET key1");
    printf("GET key1: ");
    printReply(reply);
    freeReplyObject(reply);

    // INTEGER
    reply = (redisReply *)redisCommand(c, "LLEN list1");
    printf("LLEN list1: ");
    printReply(reply);
    freeReplyObject(reply);

    // ARRAY
    reply = (redisReply *)redisCommand(c, "LRANGE list1 0 -1");
    printf("LRANGE list1: ");
    printReply(reply);
    freeReplyObject(reply);

    // NIL
    reply = (redisReply *)redisCommand(c, "GET nonexistent");
    printf("GET nonexistent: ");
    printReply(reply);
    freeReplyObject(reply);

    redisFree(c);
    return 0;
}

💼 实际应用示例

简单的缓存封装

#include <hiredis/hiredis.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class RedisCache {
private:
    redisContext *context_;

public:
    RedisCache(const char *host = "127.0.0.1", int port = 6379) {
        context_ = redisConnect(host, port);
        if (context_ == NULL || context_->err) {
            printf("Redis connection failed\n");
            context_ = NULL;
        }
    }

    ~RedisCache() {
        if (context_) {
            redisFree(context_);
        }
    }

    bool isConnected() const {
        return context_ != NULL;
    }

    bool set(const char *key, const char *value, int seconds = 0) {
        redisReply *reply;
        if (seconds > 0) {
            reply = (redisReply *)redisCommand(context_, "SETEX %s %d %s", key, seconds, value);
        } else {
            reply = (redisReply *)redisCommand(context_, "SET %s %s", key, value);
        }

        bool success = (reply->type == REDIS_REPLY_STATUS);
        freeReplyObject(reply);
        return success;
    }

    bool get(const char *key, char *buffer, size_t buffer_size) {
        redisReply *reply = (redisReply *)redisCommand(context_, "GET %s", key);
        
        bool success = false;
        if (reply->type == REDIS_REPLY_STRING && reply->len < buffer_size) {
            strncpy(buffer, reply->str, buffer_size);
            success = true;
        }
        
        freeReplyObject(reply);
        return success;
    }

    bool del(const char *key) {
        redisReply *reply = (redisReply *)redisCommand(context_, "DEL %s", key);
        bool success = (reply->integer > 0);
        freeReplyObject(reply);
        return success;
    }
};

int main() {
    RedisCache cache;

    if (!cache.isConnected()) {
        printf("Failed to connect to Redis\n");
        return 1;
    }

    // 设置缓存(10秒过期)
    cache.set("user:1001", "{\"name\":\"Alice\",\"age\":25}", 10);

    // 获取缓存
    char value[256];
    if (cache.get("user:1001", value, sizeof(value))) {
        printf("Cached data: %s\n", value);
    }

    return 0;
}

📋 Redis 回复类型速查

类型常量 数值 说明
REDIS_REPLY_STRING 1 字符串
REDIS_REPLY_ARRAY 2 数组
REDIS_REPLY_INTEGER 3 整数
REDIS_REPLY_NIL 4 空值
REDIS_REPLY_STATUS 5 状态
REDIS_REPLY_ERROR 6 错误
💡 提示: hiredis 是线程不安全的。如果在多线程环境中使用,每个线程应该有自己的 redisContext,或者使用连接池。
⚠️ 注意:

🔗 参考资料