跳到主要内容Redis String 类型深度解析与 C++ 实战 | 极客日志C++算法
Redis String 类型深度解析与 C++ 实战
Redis String 类型是内存数据库的基础,配合 C++ redis-plus-plus 库可实现高效开发。本文涵盖 SET/GET 基础读写、过期策略设置、条件更新(NX/XX)、批量操作(MSET/MGET)、子串处理及原子计数器 INCR/DECR 等核心用法。通过现代 C++ 特性如 std::optional 和 std::chrono 增强安全性与可读性,并提供分布式锁实现示例。适合需要构建高性能缓存或分布式系统的 C++ 开发者参考。
佛系玩家1 浏览 Redis String 类型深度解析与 C++ 实战
基础读写:SET 与 GET
SET 和 GET 是 Redis 交互的起点。前者将字符串值绑定到键,后者根据键取值。若键已存在,SET 会直接覆盖旧值。

基本读写操作
#include <iostream>
#include <sw/redis++/redis.h>
using namespace std::chrono_literals;
void test1_basic_set_get(sw::redis::Redis& redis) {
std::cout << ">> 1. 演示 SET 和 GET 基础操作" << std::endl;
redis.flushall();
std::cout << "设置 'key' 的值为 '111'" << std::endl;
redis.set("key", "111");
auto value = redis.get("key");
if (value) {
std::cout << "获取到 'key' 的值:" << value.value() << std::endl;
} else {
std::cout << "'key' 不存在" << std::endl;
}
std::cout << << std::endl;
std::cout << << std::endl;
redis.(, );
value = redis.();
(value) {
std::cout << << value.() << std::endl;
}
}
"\n--------------------------\n"
"更新 'key' 的值为 '222'"
set
"key"
"222"
get
"key"
if
"更新后获取到 'key' 的值:"
value
sw::redis::Redis& redis: 函数通过引用接收连接对象,避免拷贝开销,确保所有操作在同一 TCP 连接上高效执行。
redis.flushall(): 对应 Redis 的 FLUSHALL,清空实例中所有数据。测试时能确保环境干净,但生产环境严禁使用。
redis.set("key", "111"): 原子操作,设置过程中不会被其他命令打断。
auto value = redis.get("key"): redis-plus-plus 返回 std::optional<std::string>。这是现代 C++ 的安全设计。
std::optional (C++17) 表示值可能存在也可能不存在,完美映射 GET 行为:
- 键存在:内部包含
std::string。
- 键不存在:处于'空'状态。
这种设计强制你在编译期处理'值可能不存在'的情况,避免了传统空指针解引用风险。
- 检查:
if (value) 或 if (value.has_value())。
- 获取:
value.value()。注意:空对象调用 .value() 会抛出异常,务必先检查。
- 默认值:
value.value_or("default_string") 更便捷。
错误处理最佳实践
网络中断或服务器宕机可能导致异常。健壮的代码应包裹在 try-catch 块中。
void robust_redis_operations(sw::redis::Redis& redis) {
try {
redis.set("key", "some_value");
auto val = redis.get("key");
if (val) {
std::cout << "Success: " << val.value() << std::endl;
}
} catch (const sw::redis::Error &e) {
std::cerr << "Redis command failed: " << e.what() << std::endl;
}
}
掌控时间:过期策略
缓存、会话、验证码等临时数据需要自动过期(TTL)。redis-plus-plus 利用 <chrono> 库提供类型安全的过期设置。
SET 的过期时间选项
void test2_set_with_expiration(sw::redis::Redis& redis) {
std::cout << "\n>> 2. 演示 SET 带有过期时间的操作" << std::endl;
redis.flushall();
std::cout << "设置 'key',10 秒后过期" << std::endl;
redis.set("key", "111", std::chrono::seconds(10));
long long ttl = redis.ttl("key");
std::cout << "设置后,'key' 的剩余过期时间 (TTL): " << ttl << " 秒" << std::endl;
std::cout << "程序休眠 5 秒..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
ttl = redis.ttl("key");
std::cout << "5 秒后,'key' 的剩余过期时间 (TTL): " << ttl << " 秒" << std::endl;
if (redis.exists("key")) {
std::cout << "此时 'key' 仍然存在" << std::endl;
}
std::cout << "程序再休眠 6 秒..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(6));
if (!redis.exists("key")) {
std::cout << "总共 11 秒后,'key' 已按预期自动删除" << std::endl;
}
}
redis.set(..., std::chrono::seconds(10)): 重载版本接受 std::chrono::duration。相比传入整数,明确了单位,提高了可读性和健壮性。
long long time = redis.ttl("key"): 对应 TTL 命令。
-
0: 键存在,剩余秒数。
- -1: 键存在,无过期时间。
- -2: 键不存在。
- 缓存: 页面片段、查询结果缓存几分钟,到期强制刷新。
- 会话管理: 登录状态存 Redis,超时自动失效。
- 分布式锁: 防止客户端崩溃导致死锁的关键机制。
精细控制:条件化 SET
分布式场景下,有时不希望无条件覆盖。NX (Not Exists) 和 XX (Exists) 选项提供了条件控制。
NX: 仅当键不存在时设置。
XX: 仅当键存在时更新。
redis-plus-plus 通过 sw::redis::UpdateType 枚举支持这些选项。

void test3_conditional_set(sw::redis::Redis& redis) {
std::cout << "\n>> 3. 演示 SET 的 NX 和 XX 选项" << std::endl;
redis.flushall();
std::cout << "尝试使用 NX (NOT_EXIST) 设置 'key'" << std::endl;
bool success = redis.set("key", "val_nx", 0s, sw::redis::UpdateType::NOT_EXIST);
if (success) {
std::cout << "成功:'key' 原本不存在,已设置为 'val_nx'" << std::endl;
} else {
std::cout << "失败:'key' 已存在,设置操作被忽略" << std::endl;
}
std::cout << "当前 'key' 的值:" << redis.get("key").value_or("N/A") << std::endl;
std::cout << "\n再次尝试使用 NX 设置 'key'" << std::endl;
success = redis.set("key", "another_val", 0s, sw::redis::UpdateType::NOT_EXIST);
if (success) {
std::cout << "成功:'key' 原本不存在,已设置为 'another_val'" << std::endl;
} else {
std::cout << "失败:'key' 已存在,设置操作被忽略" << std::endl;
}
std::cout << "当前 'key' 的值:" << redis.get("key").value_or("N/A") << std::endl;
std::cout << "\n尝试使用 XX (EXIST) 更新 'key'" << std::endl;
success = redis.set("key", "val_xx", 0s, sw::redis::UpdateType::EXIST);
if (success) {
std::cout << "成功:'key' 存在,已更新为 'val_xx'" << std::endl;
} else {
std::cout << "失败:'key' 不存在,更新操作被忽略" << std::endl;
}
std::cout << "当前 'key' 的值:" << redis.get("key").value_or("N/A") << std::endl;
redis.del("key");
std::cout << "\n删除 'key' 后,尝试使用 XX 更新" << std::endl;
success = redis.set("key", "another_val_xx", 0s, sw::redis::UpdateType::EXIST);
if (success) {
std::cout << "成功:'key' 存在,已更新为 'another_val_xx'" << std::endl;
} else {
std::cout << "失败:'key' 不存在,更新操作被忽略" << std::endl;
}
std::cout << "当前 'key' 的值:" << redis.get("key").value_or("N/A") << std::endl;
}
SET key value NX EX seconds 是经典模式。
- 获取锁: 尝试
redis.set("my_lock", "unique_id", 30s, sw::redis::UpdateType::NOT_EXIST)。
- 返回
true: 获取成功,unique_id 标识持有者,30s 防死锁。
- 返回
false: 锁已被占用。
- 释放锁: 需通过脚本判断
unique_id 是否一致,再执行 DEL,防止误删他人锁。
效率为王:批量操作
一次性处理 100 个键值对,循环调用会产生 100 次网络往返(RTT)。MSET 和 MGET 将开销降为 O(1)。
MSET:一次设置多个键值对
方法一:初始化列表
void test4_mset_initializer_list(sw::redis::Redis& redis) {
std::cout << "\n>> 4.1 演示 MSET (使用初始化列表)" << std::endl;
redis.flushall();
std::cout << "一次性设置 key1, key2, key3" << std::endl;
redis.mset({
std::make_pair("key1", "111"),
std::make_pair("key2", "222"),
{"key3", "333"}
});
auto value1 = redis.get("key1");
auto value2 = redis.get("key2");
auto value3 = redis.get("key3");
std::cout << "获取 'key1': " << value1.value_or("N/A") << std::endl;
std::cout << "获取 'key2': " << value2.value_or("N/A") << std::endl;
std::cout << "获取 'key3': " << value3.value_or("N/A") << std::endl;
}
方法二:迭代器
#include <vector>
#include <string>
void test5_mset_mget_iterators(sw::redis::Redis& redis) {
std::cout << "\n>> 4.2 演示 MSET 和 MGET (使用迭代器)" << std::endl;
redis.flushall();
std::vector<std::pair<std::string, std::string>> kvs = {
{"key1", "111"},
{"key2", "222"},
{"key3", "333"}
};
std::cout << "使用 vector 和迭代器进行 MSET" << std::endl;
redis.mset(kvs.begin(), kvs.end());
std::vector<std::string> keys_to_get = {"key1", "key_nonexistent", "key3"};
std::vector<sw::redis::OptionalString> result_values;
auto inserter = std::back_inserter(result_values);
std::cout << "\n使用 MGET 和输出迭代器获取 'key1', 'key_nonexistent', 'key3'" << std::endl;
redis.mget(keys_to_get.begin(), keys_to_get.end(), inserter);
std::cout << "MGET 返回结果:" << std::endl;
for (size_t i = 0; i < keys_to_get.size(); ++i) {
const auto& key = keys_to_get[i];
const auto& val = result_values[i];
if (val) {
std::cout << " - " << key << ": " << val.value() << std::endl;
} else {
std::cout << " - " << key << ": (不存在)" << std::endl;
}
}
}
redis.mset(kvs.begin(), kvs.end()): 接受迭代器区间,体现与 STL 的无缝集成。
MGET 与输出迭代器: MGET 返回列表,顺序与请求键对应。不存在的键返回 nil。
std::vector<sw::redis::OptionalString>: 接收结果的容器,必须用 OptionalString 处理空值。
std::back_inserter: 特殊迭代器适配器,赋值即 push_back。
redis.mget(..., inserter): 直接将结果填充到调用者容器,避免临时对象分配,高效灵活。
精雕细琢:子串操作
不需要修改整个字符串时,GETRANGE 和 SETRANGE 提供了切片能力。
void test6_range_operations(sw::redis::Redis& redis) {
std::cout << "\n>> 5. 演示 GETRANGE 和 SETRANGE" << std::endl;
redis.flushall();
redis.set("key", "Hello, Redis World!");
std::cout << "原始字符串:'Hello, Redis World!'" << std::endl;
std::string substring = redis.getrange("key", 7, 11);
std::cout << "getrange('key', 7, 11) -> '" << substring << "'" << std::endl;
substring = redis.getrange("key", -6, -1);
std::cout << "getrange('key', -6, -1) -> '" << substring << "'" << std::endl;
std::cout << "\n执行 setrange('key', 7, 'C++')" << std::endl;
long long new_len = redis.setrange("key", 7, "C++");
auto final_value = redis.get("key");
std::cout << "修改后字符串:'" << final_value.value_or("N/A") << "'" << std::endl;
std::cout << "SETRANGE 返回的新长度:" << new_len << std::endl;
redis.setrange("key", 25, "End");
std::cout << "\n在远超末尾的位置执行 setrange('key', 25, 'End')" << std::endl;
std::cout << "最终字符串:'" << redis.get("key").value_or("N/A") << "'" << std::endl;
}
redis.getrange("key", 2, 5): 提取索引 2 到 5 的子串。Redis 字符串二进制安全,可包含 �。
redis.setrange("key", 2, "abc"): 从索引 2 开始覆盖。若长度不足,自动扩展并用空字节填充。
- 位图(Bitmap): 结合
GETBIT/SETBIT,用于签到、在线统计。
- 二进制数据块: 序列化结构体后,精确修改部分字段而不读取全文。
原子之力:计数器
高并发下,'读 - 改 - 写'会导致竞争条件。INCR 和 DECR 提供原子解决方案。Redis 单线程执行保证了操作的绝对准确。

void test7_atomic_counters(sw::redis::Redis& redis) {
std::cout << "\n>> 6. 演示 INCR 和 DECR 原子计数器" << std::endl;
redis.flushall();
redis.set("counter", "10");
std::cout << "初始值 counter: " << redis.get("counter").value_or("N/A") << std::endl;
long long result = redis.incr("counter");
std::cout << "\n执行 incr('counter') 后..." << std::endl;
std::cout << " - INCR 命令返回值:" << result << std::endl;
std::cout << " - Redis 中存储的值:" << redis.get("counter").value_or("N/A") << std::endl;
result = redis.decr("counter");
std::cout << "\n执行 decr('counter') 后..." << std::endl;
std::cout << " - DECR 命令返回值:" << result << std::endl;
std::cout << " - Redis 中存储的值:" << redis.get("counter").value_or("N/A") << std::endl;
result = redis.incrby("counter", 5);
std::cout << "\n执行 incrby('counter', 5) 后..." << std::endl;
std::cout << " - INCRBY 命令返回值:" << result << std::endl;
result = redis.incr("new_counter");
std::cout << "\n对不存在的 'new_counter' 执行 incr 后..." << std::endl;
std::cout << " - INCR 命令返回值:" << result << std::endl;
std::cout << " - Redis 中存储的值:" << redis.get("new_counter").value_or("N/A") << std::endl;
redis.set("float_counter", "10.5");
double float_result = redis.incrbyfloat("float_counter", 2.1);
std::cout << "\n执行 incrbyfloat('float_counter', 2.1) 后..." << std::endl;
std::cout << " - INCRBYFLOAT 命令返回值:" << float_result << std::endl;
}
long long result = redis.incr("key"): 解析字符串为 64 位整数,加 1 后存回。关键点:返回的是新值。
- 原子性保证: 简化了并发编程,无需复杂加锁。
- 扩展命令:
INCRBY/DECRBY: 指定整数步长。
INCRBYFLOAT: 指定浮点数步长。
- 网站计数器: PV、UV 统计。
- 限流器(Rate Limiting): 为每个用户/IP 创建计数器,设置过期时间。请求时
INCR,超过阈值则拒绝。
总结
本文深入探讨了 Redis String 类型的核心命令及 redis-plus-plus 的实战用法。从基础的 SET/GET 到 std::optional 带来的类型安全,再到 std::chrono 赋予的生命周期管理;从 NX/XX 实现的分布式锁,到 MSET/MGET 结合迭代器的性能优化,以及 GETRANGE/SETRANGE 的局部操作和 INCR/DECR 的原子计数。Redis String 不仅是简单的键值存储,更是构建高性能、高并发系统的重要基石。熟练掌握这些特性,能让你的 C++ 应用在数据处理上更加游刃有余。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online