C++ Redis 客户端深度解析(hiredis 与 redis-plus-plus)——工业数采实时缓存 C++ 侧实战

技术栈:C / C++17 · Redis 7.x · hiredis 1.1.0 · redis-plus-plus(sw/redis++)1.3.x

配套环境:Windows 11 / Linux(本文示例以 CMake + vcpkg/FetchContent 集成为主)


1. 背景

1.1 工业数采的双路径存储模型

工业数据采集(CNC / PLC / 传感器)的落地链路通常分为两条路径:

  • 历史流:设备点位数据以消息形式进入 Kafka,最终落入 TDengine 等时序数据库,用于离线分析、告警回溯、OEE 统计。特点是数据量大、只追加、不需要毫秒级响应。
  • 实时层 :设备最新值 、命令下发队列 、多网关互斥锁 、实时告警等场景需要毫秒级读写,这类数据用内存缓存(Redis)承载,再定期/按需下沉到历史存储。

在这条链路中,Go 侧通常使用 go-redis(已有专篇),而 C++ 采集网关(尤其是直接对接 FANUC FOCAS、三菱 MC 协议、西门子 snap7 的采集端)也需要一个可靠、高性能的 Redis 客户端来维护"点位最新值缓存"、"命令队列"、"网关分布式锁"。

1.2 为什么选择 hiredis 与 redis-plus-plus

C/C++ 生态的 Redis 客户端主要有三条路线:

库 语言 定位 特点
hiredis C 官方推荐的底层客户端 极简、零依赖、同步+异步双模型、RESP 解析器独立可用;是 redis-plus-plus、很多框架(如 Drogon 的 redis 插件)的底层
redis-plus-plus(sw/redis++) C++17 hiredis 之上的现代 C++ 封装 Redis/RedisCluster 类、命令族方法、Optional 返回值、Pipeline/Transaction、连接池、订阅回调、Lua 脚本
cpp-redis / acre C++ 第三方封装 生态小,维护不活跃

hiredis 适合对性能与控制力要求极高的场景(手写事件循环、嵌入式采集器);redis-plus-plus 适合工程化程度高的 C++ 项目(自动连接池、类型安全的命令 API、异常体系)。两者可以混用:redis-plus-plus 本身依赖 hiredis,你可以在一个工程里同时使用。

2. Redis 核心概念速览(客户端视角必须知道的部分)

2.1 RESP 协议(REdis Serialization Protocol)

hiredis 本质就是一个 RESP 协议解析器。客户端与 Redis 之间传输的每一条命令/回复都是 RESP 编码的文本帧:

类型 首字节 示例 说明
Simple String + +OK\r\n 状态回复
Error - -ERR unknown command\r\n 错误回复
Integer : :1000\r\n 整数回复(INCR 等)
Bulk String $ $5\r\nhello\r\n 二进制安全的字符串
Array * *2\r\n3\\r\\nfoo\\r\\n3\r\nbar\r\n 多条回复

客户端发送命令时把参数构造成 Array 帧:*3\r\n3\\r\\nSET\\r\\n3\r\nfoo\r\n$3\r\nbar\r\n。

2.2 五种基本数据结构与底层编码

结构 常用命令 底层编码(Redis 7)
String SET / GET / INCR / SETNX int、embstr(≤44 字节)、raw
Hash HSET / HGET / HGETALL / HINCRBY listpack(小)、hashtable
List LPUSH / RPOP / BRPOP / LLEN quicklist(listpack 节点链表)
Set SADD / SMEMBERS / SISMEMBER intset(整数小集合)、hashtable
ZSet ZADD / ZSCORE / ZREVRANGE / ZRANGEBYSCORE listpack(小)、skiplist + dict

此外还有 Bitmap、HyperLogLog、Geo、Stream 等扩展类型。

2.3 关键语义:TTL、过期与淘汰

  • SET key val EX 30 / SETEX / EXPIRE:给实时点位最新值设置 TTL,自动过期清理"失联设备"的残留数据。
  • Redis 的过期删除是惰性删除 + 定期删除,客户端不该依赖"过期后立刻不可见"(有窗口期)。
  • 内存淘汰策略(maxmemory-policy)决定内存满时的行为,生产建议 allkeys-lru 或 noeviction(宁可报错不丢数据)。

3. API 说明

3.1 hiredis 核心 API

3.1.1 连接管理
cpp 复制代码
#include <hiredis/hiredis.h>

redisContext *redisConnect(const char *ip, int port);              // 同步连接
redisContext *redisConnectWithTimeout(const char *ip, int port,
                                      const struct timeval tv);     // 带连接超时
void redisFree(redisContext *c);                                    // 释放连接
int redisSetTimeout(redisContext *c, const struct timeval tv);      // 设置读写超时

redisContext 关键字段:

cpp 复制代码
typedef struct redisContext {
    int err;            // 0=正常;REDIS_ERR_IO / REDIS_ERR_EOF / REDIS_ERR_PROTOCOL / REDIS_ERR_OOM / REDIS_ERR_OTHER
    char errstr[128];   // 错误描述
    int fd;             // 套接字,-1 表示未连接
    int flags;          // REDIS_BLOCK / REDIS_CONNECTED 等
} redisContext;
3.1.2 同步命令
cpp 复制代码
void *redisCommand(redisContext *c, const char *format, ...);        // printf 风格格式化
void *redisCommandArgv(redisContext *c, int argc, const char **argv,
                       const size_t *argvlen);                       // 数组形式,二进制安全

// 返回值是 redisReply*,必须用 freeReplyObject() 释放
typedef struct redisReply {
    int type;           // REDIS_REPLY_STRING / ERROR / INTEGER / NIL / STATUS / ARRAY / DOUBLE ...
    long long integer;  // type==INTEGER 时有效
    size_t len;         // str 长度(二进制安全)
    char *str;          // type==STRING/ERROR/STATUS 时有效
    size_t elements;    // type==ARRAY 时元素个数
    struct redisReply **element;  // 元素数组
} redisReply;

void freeReplyObject(void *reply);
3.1.3 管道(Pipelining)
cpp 复制代码
// 写入不等待回复
redisAppendCommand(c, "SET foo bar");
redisAppendCommand(c, "GET foo");
// 逐个读取(每个 Append 必须对应一次 GetReply)
redisReply *reply = NULL;
redisGetReply(c, (void **)&reply);   // SET 的结果
freeReplyObject(reply);
redisGetReply(c, (void **)&reply);   // GET 的结果
3.1.4 异步模型
cpp 复制代码
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
void redisAsyncFree(redisAsyncContext *ac);
int redisAsyncSetConnectCallback(redisAsyncContext *ac,
                                 redisConnectCallback fn);            // fn(ac, status)
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac,
                                    redisDisconnectCallback fn);       // fn(ac, status)
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn,
                      void *privdata, const char *format, ...);

// 事件循环驱动(核心!必须接入 epoll/select/libevent/uv 等)
void redisAsyncHandleRead(redisAsyncContext *ac);
void redisAsyncHandleWrite(redisAsyncContext *ac);

异步回调原型:void callback(redisAsyncContext *c, void *reply, void *privdata);(reply 用完调用释放函数;回调内不要阻塞)。

3.1.5 发布订阅(同步方式)
cpp 复制代码
redisReply *reply = redisCommand(c, "SUBSCRIBE chan:alarm");
freeReplyObject(reply);
while (redisGetReply(c, (void **)&reply) == REDIS_OK) {
    // reply->type == REDIS_REPLY_ARRAY
    // element[0]=subscribe/message, element[1]=channel, element[2]=payload
    freeReplyObject(reply);
}

3.2 redis-plus-plus 核心 API

3.2.1 连接与配置
3.2.2 String / Hash / List / Set / ZSet 命令族(节选)
cpp 复制代码
// String
auto val = redis.get("foo");          // OptionalString,std::optional<std::string>
redis.set("foo", "bar");
redis.set("foo", "bar", std::chrono::seconds(30));  // 带 TTL
auto n = redis.incr("counter");       // 返回 long long

// Hash
redis.hset("dev:1", "axis_x", "123.45");
auto x = redis.hget("dev:1", "axis_x");
std::unordered_map<std::string, std::string> kv;
redis.hgetall("dev:1", std::inserter(kv, kv.begin()));

// List
redis.lpush("cmd:queue", "MOVE_AXIS 10");
auto item = redis.rpop("cmd:queue");          // OptionalString
redis.brpop("cmd:queue", std::chrono::seconds(1));  // 阻塞弹出

// Set / ZSet
redis.sadd("online_gateways", "gw-1");
bool online = redis.sismember("online_gateways", "gw-1");
redis.zadd("alarm_rank", "high", 10);         // 注意:redis-plus-plus 的 zadd 参数顺序是 score, member
auto rank = redis.zrevrange("alarm_rank", 0, 9);

注意:redis-plus-plus 命令方法的参数顺序与原生 Redis 命令不一定一致(如 zadd(key, score, member)),以头文件签名 / IDE 提示为准,这是最常见的踩坑点之一。

3.2.3 Pipeline 与 Transaction
cpp 复制代码
// Pipeline:批量发送、一次 exec() 取结果
auto pipe = redis.pipeline();
pipe.set("a", "1");
pipe.set("b", "2");
auto results = pipe.exec();   // vector<OptionalString> 等

// Transaction:MULTI/EXEC,可配合 WATCH
auto tx = redis.multi();
tx.set("a", "1");
tx.incr("counter");
auto tx_results = tx.exec();
3.2.4 订阅与 Lua
cpp 复制代码
// 订阅(阻塞运行,回调在订阅线程执行,回调内勿阻塞)
redis.subscribe({"chan:alarm"}, [](const std::string &chan,
                                    const std::string &msg) {
    std::cout << chan << ": " << msg << std::endl;
});

// Lua 脚本(原子执行)
auto script = R"(
    local v = redis.call('GET', KEYS[1])
    if v == false then return 0 end
    redis.call('DEL', KEYS[1])
    return 1
)";
long long n = redis.eval(script, {"key"}, {}, 0);  // 注意签名:eval(script, keys, args, num_keys)
3.2.5 RedisCluster
cpp 复制代码
RedisCluster cluster("tcp://127.0.0.1:7000,tcp://127.0.0.1:7001,tcp://127.0.0.1:7002");
cluster.set("foo", "bar");   // 自动按 CRC16 路由到正确分片

4. 详细使用说明

4.0 环境搭建

复制代码
# 方式一:vcpkg
# vcpkg install hiredis redis-plus-plus
find_package(hiredis CONFIG REQUIRED)
find_package(redis++ CONFIG REQUIRED)
target_link_libraries(app PRIVATE hiredis::hiredis redis++::redis++)

# 方式二:FetchContent(redis-plus-plus 需要 hiredis 作为依赖)
FetchContent_Declare(hiredis URL https://github.com/redis/hiredis/archive/refs/tags/v1.1.0.tar.gz)
FetchContent_Declare(redis-plus-plus URL https://github.com/sewenew/redis-plus-plus/archive/refs/tags/1.3.11.tar.gz)

4.1 hiredis 最小同步示例(点位读写)

cpp 复制代码
#include <hiredis/hiredis.h>
#include <cstdio>
#include <cstdlib>

int main() {
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == nullptr || c->err) {
        std::fprintf(stderr, "connect failed: %s\n", c ? c->errstr : "no mem");
        if (c) redisFree(c);
        return 1;
    }

    // SET
    redisReply *r = (redisReply *)redisCommand(c, "SET dev:1:axis_x 123.45");
    if (r == nullptr) { std::fprintf(stderr, "cmd error: %s\n", c->errstr); redisFree(c); return 1; }
    freeReplyObject(r);   // 必须释放!

    // GET + 判断类型
    r = (redisReply *)redisCommand(c, "GET dev:1:axis_x");
    if (r->type == REDIS_REPLY_STRING)
        std::printf("axis_x = %.*s\n", (int)r->len, r->str);
    else
        std::printf("not found / nil\n");
    freeReplyObject(r);

    // INCR 返回 INTEGER
    r = (redisReply *)redisCommand(c, "INCR dev:1:cmd_seq");
    if (r->type == REDIS_REPLY_INTEGER)
        std::printf("seq = %lld\n", r->integer);
    freeReplyObject(r);

    redisFree(c);
    return 0;
}

4.2 hiredis 管道批量写入(采集网关周期性批量上报最新值)

cpp 复制代码
redisAppendCommand(c, "MSET dev:1:axis_x 100.1 dev:1:axis_y 200.2");
redisAppendCommand(c, "MSET dev:2:axis_x 300.3 dev:2:axis_y 400.4");
// 读取两个回复
redisReply *r = nullptr;
redisGetReply(c, (void **)&r); freeReplyObject(r);
redisGetReply(c, (void **)&r); freeReplyObject(r);

也可以直接用 redisCommandArgv 构造 MSET 参数数组(参数值无需转义、天然二进制安全)。

4.3 hiredis 异步 + 事件循环(对接 libuv 风格集成)

cpp 复制代码
void onReply(redisAsyncContext *ac, void *reply, void *privdata) {
    if (reply == nullptr) { /* 连接已断开 */ return; }
    redisReply *r = (redisReply *)reply;
    if (r->type == REDIS_REPLY_STRING) {
        std::printf("async got: %.*s\n", (int)r->len, r->str);
    }
    // hiredis 异步回调里 reply 无需手动释放(框架会处理),注意别 freeReplyObject!
}

int main() {
    redisAsyncContext *ac = redisAsyncConnect("127.0.0.1", 6379);
    if (ac->err) { std::fprintf(stderr, "async connect: %s\n", ac->errstr); return 1; }
    redisAsyncSetConnectCallback(ac, [](redisAsyncContext *c, int status) {
        if (status != REDIS_OK) { std::fprintf(stderr, "connect fail\n"); return; }
        redisAsyncCommand(c, onReply, nullptr, "GET dev:1:axis_x");
    });
    redisAsyncSetDisconnectCallback(ac, [](redisAsyncContext *c, int status) {
        std::fprintf(stderr, "disconnected: %d\n", status);
    });

    // 事件循环驱动(示意):libevent / libuv / 自行 select
    // 注册 ac->fd 的可读/可写事件后分别调用 redisAsyncHandleRead / redisAsyncHandleWrite
    // ...
    redisAsyncFree(ac);
    return 0;
}

4.4 redis-plus-plus 工程化示例:连接池 + 管道 + 分布式锁(工业数采)

cpp 复制代码
#include <sw/redis++/redis++.h>
#include <iostream>
using namespace sw::redis;

int main() {
    ConnectionOptions opts;
    opts.host = "127.0.0.1";
    opts.port = 6379;
    opts.socket_timeout = std::chrono::milliseconds(300);
    opts.connect_timeout = std::chrono::milliseconds(200);
    ConnectionPoolOptions pool_opts;
    pool_opts.pool_size = 16;
    pool_opts.wait_timeout = std::chrono::milliseconds(200);
    Redis redis(opts, pool_opts);

    // 1) 点位最新值 + TTL
    redis.set("dev:1:axis_x", "123.45", std::chrono::seconds(60));
    auto v = redis.get("dev:1:axis_x");
    if (v) std::cout << "axis_x=" << *v << std::endl;

    // 2) 管道批量写入
    auto pipe = redis.pipeline();
    pipe.set("dev:1:axis_y", "456.78");
    pipe.set("dev:1:axis_z", "789.01");
    pipe.expire("dev:1:axis_y", std::chrono::seconds(60));
    auto res = pipe.exec();

    // 3) 分布式锁(Lua 保证原子性:SETNX + EXPIRE)
    auto lock_script = R"(
        if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then
            redis.call('EXPIRE', KEYS[1], ARGV[2])
            return 1
        else
            return 0
        end
    )";
    long long locked = redis.eval(lock_script, {"gw:lock:axis"}, {"gw-1", "10"}, 1);
    if (locked == 1) std::cout << "lock acquired" << std::endl;
    // 释放:先校验 token 再 DEL(避免误删他人锁)
    auto unlock_script = R"(
        if redis.call('GET', KEYS[1]) == ARGV[1] then
            return redis.call('DEL', KEYS[1])
        else
            return 0
        end
    )";
    redis.eval(unlock_script, {"gw:lock:axis"}, {"gw-1"}, 1);

    // 4) 实时告警 PubSub
    redis.publish("chan:alarm", "axis_x overflow");
    return 0;
}

4.5 工业数采链路建议(C++ 采集网关视角)

复制代码
FANUC/MC/snap7 设备
      │  FOCAS / MC 协议 / S7 协议
      ▼
C++ 采集网关
      │  hiredis / redis-plus-plus
      ▼
Redis 实时层(点位最新值 TTL / 命令下发队列 List / 网关分布式锁 / 告警 PubSub)
      │  定时批量
      ▼
Kafka(历史流)→ TDengine(时序落库)→ 分析 / 告警平台
  • 点位最新值:String + TTL,键 dev:{id}:{point}。
  • 命令下发:List cmd:{dev},采集线程 BRPOP 阻塞取命令执行后回写状态。
  • 多网关互斥:Lua 分布式锁,避免多网关同时下发冲突命令。
  • 实时告警:PubSub chan:alarm,监控线程订阅并转 MQTT/日志。

5. 底层实现剖析

5.1 hiredis 结构:连接层与解析器分离

hiredis 的核心设计是同步/异步共用同一套 RESP 解析器:

  • net.c:非阻塞 socket 建立、超时设置(SO_RCVTIMEO / SO_SNDTIMEO 或 poll 轮询)。
  • reader(read.c):纯内存状态机,输入字节流,输出一棵 redisReply 树。不依赖 socket,所以异步模型复用同一解析器。
  • redisContext 的 reader 字段是解析器实例;obuf 是待发送缓冲。

5.2 RESP 解析状态机(reader 内部要点)

解析器按首字节分派:

  1. 读到 (Bulk String):先读 <len>\r\n,若 len == -1 表示 nil,否则读 len 字节 + \r\n,构造 REDIS_REPLY_STRING。
  2. 读到 *(Array):先读 *<count>\r\n,若 count == -1 表示 nil array,否则递归解析 count 个元素,构造 REDIS_REPLY_ARRAY。
  3. 读到 + / - / ::读一行,分别是 STATUS / ERROR / INTEGER。

每次 redisGetReply 会尝试从 reader 取一棵完整回复树;若字节流不完整(半包),返回 REDIS_ERR_IO(err=REDIS_ERR_EOF 语义),下次再调用继续解析剩余字节------这就是"粘包/半包"天然被 hiredis 处理掉的原因。

5.3 异步模型:事件循环驱动

  • redisAsyncContext 包装 redisContext,把命令封装成 redisCallback 链表挂在连接上。
  • redisAsyncCommand 只是把命令追加到输出缓冲;真正发送发生在事件循环调用 redisAsyncHandleWrite(fd 可写)时。
  • 回复到达后 redisAsyncHandleRead 解析,按命令顺序触发回调(hiredis 用 FIFO 保证命令-回调配对)。
  • 没有事件循环 = 异步永远不会推进,这是初学 hiredis 最大的困惑来源。

5.4 redis-plus-plus 的封装层次

  • Redis 持有 ConnectionPool(hiredis 连接数组 + 互斥借用归还),每个命令方法从池中借连接执行再归还。
  • RedisCluster 内部维护 slot → node 映射(16384 个 CRC16 slot),收到 MOVED/ASK 后自动重路由。
  • Pipeline/Transaction 封装成 PipelineImpl/TransactionImpl,延迟到 exec() 统一发送+收集。
  • 异常体系:Error、TimeoutError、OOMError、ClosedError、IoError 等,所有命令可能抛 Error,工程上必须捕获。

6. 常错点 / 坑(Top 20)

坑点 后果 正确姿势
1 不检查 redisContext 的 err / NULL 空指针解引用、静默失败 连接后立即检查 c && !c->err
2 redisCommand 返回 NULL 还继续访问 崩溃 / 未定义行为 判空后先看 c->errstr
3 redisReply 用完不 freeReplyObject 内存泄漏(长连接下尤其严重) 每条回复必释放
4 不判断 r->type 直接用 r->str 类型错读(INTEGER 无 str) 按 type 分派处理
5 管道 Append 次数与 GetReply 次数不匹配 回复错位、逻辑错误 每 Append 必须有对应 GetReply
6 异步回调里 freeReplyObject(reply) 二次释放崩溃 异步回调 reply 由框架管理
7 异步连接不接入任何事件循环 命令永远发不出去/收不到 注册 fd 可读可写事件并调用 HandleRead/Write
8 多线程共享同一个同步 context 数据竞争、协议错乱 每线程独立连接,或用异步+互斥
9 同步订阅后在同一连接上再发普通命令 阻塞、命令排队无人消费 订阅用专用连接/线程
10 未设超时(redisSetTimeout) 网络抖动时无限阻塞 显式设置读写超时
11 只查 connect 成功就以为一切 OK 密码错误/AUTH 失败在首次命令才暴露 连接后主动 PING
12 Cluster 直连单节点 MOVED/ASK 错误 用 Cluster 客户端或处理重定向
13 get 返回值不判空直接解引用 未命中时崩溃 用 if (val) / val.has_value()
14 zadd(key, member, score) 参数顺序记反 数据写错 顺序是 zadd(key, score, member)
15 命令抛异常不捕获 进程中断 捕获 sw::redis::Error 系列
16 池耗尽 + wait_timeout 设 0 快速失败或无限等待 评估峰值并发合理配置
17 brpop 入参顺序错误 弹出错误方向/顺序错乱 按签名传(先 key 后 timeout 语义看 IDE)
18 订阅回调里执行阻塞/耗时操作 卡住整个订阅线程、丢消息 回调只做投递,用队列交给工作线程
19 多个命令想原子执行却用多个调用 中间状态可见 用 Lua / Transaction
20 误把 exec() 返回值当单个结果 类型不匹配编译/运行错误 Pipeline 返回 vector,Transaction 同理

7. 性能优化清单

  1. 管道化批量:采集网关 100ms 周期把 N 个点位 MSET/Pipeline 一次发送,吞吐可提升 10~50 倍。
  2. 连接池:redis-plus-plus 默认池化;hiredis 自行维护连接复用,避免每次命令建连。
  3. 避免大 Key:单 Key 超过几十 KB 会拖慢整个实例;点位数据按点拆 Key。
  4. 合理 TTL:实时点位设 TTL,防止僵尸 Key 堆积。
  5. 序列化最小化:数值点位用字符串化数字即可,不要套 JSON。
  6. Lua 原子化:分布式锁/计数/校验用 Lua,省 RTT 且原子。
  7. 订阅线程独立:PubSub 高频消息不要与业务命令共用连接。
  8. 关闭 AOF 或调整策略:纯缓存场景可 appendonly no;需要持久化用 everysec。
  9. 客户端侧超时收敛:socket_timeout 统一几百 ms,避免雪崩式阻塞。
  10. 监控:INFO stats / SLOWLOG 治理慢命令;客户端侧统计命令耗时。

8. FAQ 速查表

Q1: hiredis 和 redis-plus-plus 该选哪个? A: 需要极致控制力/零依赖/嵌入式 → hiredis;工程化开发(连接池、类型安全、Cluster、订阅封装)→ redis-plus-plus。两者可同工程混用。

Q2: hiredis 同步 API 是线程安全的吗? A: 不是。一个 context 只能被一个线程使用;多线程请各自建连接或加锁。

Q3: 为什么异步连接明明 connect 成功却收不到回调? A: 因为没有事件循环驱动 redisAsyncHandleRead/Write。必须把 ac->fd 注册到 epoll/select/libevent/libuv。

Q4: redis-plus-plus 的 Redis 对象是线程安全的吗? A: 是的,Redis 内部用连接池 + 互斥保证多线程安全,可跨线程共享。

Q5: Pipeline 和 Transaction 的区别? A: Pipeline 只做批量发送减少 RTT,不保证原子;Transaction(MULTI/EXEC)保证原子性。

Q6: 为什么用 Lua 做分布式锁? A: SETNX + EXPIRE 两条命令非原子,进程崩溃会死锁;Lua 单次执行原子,配合 token 校验释放避免误删。

Q7: Redis 会丢数据吗? A: 默认内存为主。纯缓存可接受;要求可靠性用 AOF everysec 或直接走 Kafka 历史流。

Q8: 大流量采集网关怎么组织 Key? A: 每设备每点位独立 Key + TTL;批量上报用 Pipeline/MSET;命令下发用 List + BRPOP。


9. 总结

C++ 侧访问 Redis 的主流路径是 hiredis(官方 C 客户端,RESP 解析器 + 同步/异步双模型) 与其上层的 redis-plus-plus(C++17 现代封装)。对工业数采网关而言,Redis 实时层承担"点位最新值、命令下发、分布式锁、实时告警"四类职责,与 Kafka/TDengine 的历史流形成"实时 + 历史"双通道,与 Go 侧 go-redis 构成跨语言对照。掌握本文的 API 分层、RESP 解析原理、异步事件驱动模型与 20 条高频坑点,即可在生产采集链路上稳定落地。

相关推荐
mengge.cloud1 小时前
Redis
运维·服务器·数据库·redis·缓存·云计算
垆边人似月.1 小时前
华为机试题 :最长递增子序列题目
数据结构·c++·算法·华为
欣栀♡1 小时前
Redis 缓存穿透、击穿、雪崩,背出定义只是及格线
数据库·redis·缓存
开发者联盟league1 小时前
c++11 使用chrono库转换时间点和字符串,计算时间点间隔
java·c++·算法
codigger1 小时前
Redis 正式接入 AI:当"最懂速度的数据库"开始解决"记忆问题"
redis·分布式·后端·ai·向量检索
禾小西2 小时前
08丨Redis 哨兵集群:哨兵故障后还能完成切换吗?
数据库·redis·缓存
挖掘狂人2 小时前
Redis 正式接入 AI:当"最懂速度的数据库"开始解决"记忆问题"
人工智能·redis·后端
蜕变的土豆2 小时前
工业监控RTSP取流-实践指南
c++·视频
阿标在干嘛2 小时前
缓存架构的“场景适配“:我们为什么在部分服务中采用了本地缓存?
缓存·架构