C++中使用Redis指南:基于redis-plus-plus库
简介
Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,广泛用作数据库、缓存和消息队列。redis-plus-plus是一个现代化的C++ Redis客户端库,提供了直观易用的API、完整的Redis命令支持以及出色的性能表现。
核心特性:
- 完整的Redis命令支持
- 连接池管理
- 线程安全
- 异步操作支持
- 管道(Pipelining)和事务支持
- 集群和哨兵模式支持
- 异常安全
一、环境准备
1.1 安装Redis服务器
在开始之前,请确保已安装Redis服务器:
bash
# Ubuntu/Debian
sudo apt update
sudo apt install redis-server
# macOS (使用Homebrew)
brew install redis
# 启动Redis服务
redis-server

1.2 安装redis-plus-plus库
redis-plus-plus依赖hiredis库,需要先安装:
bash
# 安装依赖
# Ubuntu/Debian
sudo apt install libhiredis-dev
# macOS
brew install hiredis
# 安装Boost库(用于异步和SSL支持)
sudo apt install libboost-all-dev # Ubuntu/Debian
brew install boost # macOS
使用CMake构建并安装redis-plus-plus
bash
git clone https://github.com/sewenew/redis-plus-plus.git
cd redis-plus-plus
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DREDIS_PLUS_PLUS_BUILD_TEST=OFF
make -j$(nproc)
sudo make install
sudo ldconfig # 更新动态链接库缓存

二、基础连接与配置
2.1 建立连接
redis-plus-plus提供了Connection类用于管理连接:
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
int main() {
try {
// 基本连接
sw::redis::Redis redis("tcp://127.0.0.1:6379?password=888888");
// 测试连接
std::string pong = redis.ping();
std::cout << "Redis响应: " << pong << std::endl;
} catch (const sw::redis::Error &e) {
std::cerr << "连接错误: " << e.what() << std::endl;
return 1;
}
return 0;
}

2.2 连接池配置
生产环境中推荐使用连接池以提高性能:
cpp
#include <sw/redis++/redis++.h>
int main() {
sw::redis::ConnectionPoolOptions pool_opts;
pool_opts.host = "tcp://127.0.0.1:6379";
pool_opts.port = 6379;
pool_opts.pool_size = 10; // 连接池大小
pool_opts.wait_timeout = std::chrono::milliseconds(100);
pool_opts.connection_timeout = std::chrono::milliseconds(100);
sw::redis::ConnectionOptions conn_opts;
conn_opts.host = "127.0.0.1";
conn_opts.port = 6379;
conn_opts.password = "your_password"; // 如需认证
conn_opts.db = 0; // 选择数据库
sw::redis::Redis redis(conn_opts, pool_opts);
// 使用redis对象
redis.set("key", "value");
}

2.3 连接选项详解
| 选项 | 类型 | 说明 |
|---|---|---|
host |
string | Redis服务器地址 |
port |
int | 端口号,默认6379 |
password |
string | 认证密码 |
db |
int | 数据库编号 |
socket_timeout |
duration | socket超时时间 |
connection_timeout |
duration | 连接超时时间 |
keep_alive |
bool | TCP保活机制 |
三、字符串(String)操作
字符串是Redis最基础的数据类型,用于存储键值对。
3.1 基本SET/GET操作
String类型常用操作速查表:
| 操作 | C++ API | 说明 | 示例 |
|---|---|---|---|
| 设置键值 | set(key, value) |
设置单个键值对 | redis.set("name", "Alice") |
| 设置带过期时间 | set(key, value, expiry) |
设置值并指定过期时长 | redis.set("token", "abc", std::chrono::seconds(3600)) |
| 获取值 | get(key) |
返回OptionalString,需检查是否有值 | auto name = redis.get("name") |
| 批量设置 | mset(kvs...) |
一次设置多个键值对 | redis.mset("k1", "v1", "k2", "v2") |
| 批量获取 | mget(keys...) |
一次获取多个键的值 | auto values = redis.mget("k1", "k2") |
| 键是否存在 | exists(key) |
返回bool值 | redis.exists("name") |
| 删除键 | del(key) |
删除指定的键 | redis.del("name") |
| 设置过期时间 | expire(key, seconds) |
为键设置TTL | redis.expire("token", 7200) |
| 获取过期时间 | ttl(key) |
返回剩余秒数,-1表示永久 | redis.ttl("token") |
| 原子递增 | incr(key) |
将值+1 | redis.incr("counter") |
| 原子增加N | incrby(key, n) |
将值+N | redis.incrby("counter", 5) |
| 原子递减 | decr(key) |
将值-1 | redis.decr("counter") |
| 原子减少N | decrby(key, n) |
将值-N | redis.decrby("counter", 3) |
| 浮点递增 | incrbyfloat(key, f) |
浮点值增加 | redis.incrbyfloat("price", 0.01) |
| 获取旧值并设置 | getset(key, new_value) |
原子操作,返回旧值 | redis.getset("age", "30") |
| 追加内容 | append(key, value) |
在字符串末尾追加 | redis.append("greeting", " World") |
| 获取子串 | getrange(key, start, end) |
截取子串,end=-1表示到末尾 | redis.getrange("msg", 0, 4) |
| 获取长度 | strlen(key) |
返回字符串长度 | redis.strlen("message") |
| 替换子串 | setrange(key, offset, value) |
从offset开始替换 | redis.setrange("data", 6, "Redis") |
| 键不存在时设置 | setnx(key, value) |
仅当键不存在时设置,返回bool | redis.setnx("lock", "1") |
| 批量设置(原子) | msetnx(kvs...) |
全部键都不存在时才设置 | redis.msetnx("k1", "v1", "k2", "v2") |
代码示例:
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
#include <string>
#include <vector>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379?password=888888");
std::cout << "=== 基础 SET/GET 操作 ===" << std::endl;
redis.set("name", "Alice");
redis.set("age", "25");
auto name = redis.get("name");
if (name) {
std::cout << "GET name: " << *name << std::endl;
}
auto age = redis.get("age");
if (age) {
std::cout << "GET age: " << *age << std::endl;
}
std::cout << "\n=== 带过期时间的 SET ===" << std::endl;
redis.set("token", "abc123", std::chrono::seconds(3600));
redis.set("session", "xyz789", std::chrono::milliseconds(1800000));
auto token = redis.get("token");
if (token) {
std::cout << "Token (expires in 3600s): " << *token << std::endl;
}
std::cout << "\n=== MSET/MGET 批量操作 ===" << std::endl;
std::vector<std::pair<std::string, std::string>> kvs = {
{"key1", "value1"},
{"key2", "value2"},
{"key3", "value3"}
};
redis.mset(kvs.begin(), kvs.end());
std::cout << "批量设置了 3 个键值对" << std::endl;
std::vector<std::string> keys = {"key1", "key2", "key3"};
std::vector<sw::redis::OptionalString> values;
redis.mget(keys.begin(), keys.end(), std::back_inserter(values));
std::cout << "MGET key1, key2, key3:" << std::endl;
for (size_t i = 0; i < values.size(); ++i) {
if (values[i]) {
std::cout << " " << keys[i] << " = " << *values[i] << std::endl;
}
}
std::cout << "\n=== INCR/DECR 计数操作 ===" << std::endl;
redis.set("counter", "10");
std::cout << "初始值: " << *redis.get("counter") << std::endl;
redis.incr("counter");
std::cout << "INCR counter: " << *redis.get("counter") << std::endl;
redis.incrby("counter", 5);
std::cout << "INCRBY counter 5: " << *redis.get("counter") << std::endl;
redis.decr("counter");
std::cout << "DECR counter: " << *redis.get("counter") << std::endl;
redis.decrby("counter", 3);
std::cout << "DECRBY counter 3: " << *redis.get("counter") << std::endl;
std::cout << "\n=== INCRBYFLOAT 浮点操作 ===" << std::endl;
redis.set("price", "99.99");
std::cout << "初始价格: " << *redis.get("price") << std::endl;
redis.incrbyfloat("price", 0.01);
std::cout << "价格上涨 0.01: " << *redis.get("price") << std::endl;
redis.incrbyfloat("price", -0.50);
std::cout << "价格下降 0.50: " << *redis.get("price") << std::endl;
std::cout << "\n=== EXISTS/DELETE/EXPIRE/TTL ===" << std::endl;
bool exists_before = redis.exists("name");
std::cout << "name 存在吗? " << (exists_before ? "是" : "否") << std::endl;
redis.del("name");
bool exists_after = redis.exists("name");
std::cout << "删除后 name 存在吗? " << (exists_after ? "是" : "否") << std::endl;
redis.set("token", "abc123", std::chrono::seconds(3600));
redis.expire("token", 7200);
long long ttl = redis.ttl("token");
std::cout << "token 的 TTL: " << ttl << " 秒" << std::endl;
std::cout << "\n=== GETSET 原子替换 ===" << std::endl;
redis.set("age", "25");
std::cout << "修改前 age: " << *redis.get("age") << std::endl;
auto old_value = redis.getset("age", "30");
std::cout << "GETSET 返回旧值: " << (old_value ? *old_value : "null") << std::endl;
std::cout << "修改后 age: " << *redis.get("age") << std::endl;
std::cout << "\n=== APPEND 追加内容 ===" << std::endl;
redis.set("greeting", "Hello");
std::cout << "原始 greeting: " << *redis.get("greeting") << std::endl;
redis.append("greeting", " World");
std::cout << "APPEND ' World' 后: " << *redis.get("greeting") << std::endl;
std::cout << "\n=== GETRANGE 获取子串 ===" << std::endl;
redis.set("message", "Hello World");
std::cout << "完整字符串: " << *redis.get("message") << std::endl;
auto sub = redis.getrange("message", 0, 4);
std::cout << "GETRANGE message 0-4: " << sub << std::endl;
auto sub2 = redis.getrange("message", 6, -1);
std::cout << "GETRANGE message 6 到末尾: " << sub2 << std::endl;
std::cout << "\n=== STRLEN 获取长度 ===" << std::endl;
long long len = redis.strlen("message");
std::cout << "message 的长度: " << len << std::endl;
std::cout << "\n=== SETRANGE 替换子串 ===" << std::endl;
redis.set("data", "Hello World");
std::cout << "原始数据: " << *redis.get("data") << std::endl;
redis.setrange("data", 6, "Redis");
std::cout << "SETRANGE data 6 'Redis': " << *redis.get("data") << std::endl;
std::cout << "\n=== SETNX 键不存在时设置 ===" << std::endl;
redis.set("unique_key", "value1");
redis.setnx("unique_key", "value2");
std::cout << "尝试 SETNX 已存在的键: " << *redis.get("unique_key") << std::endl;
redis.setnx("new_unique_key", "brand_new");
std::cout << "SETNX 新键: " << *redis.get("new_unique_key") << std::endl;
std::cout << "\n=== MSETNX 批量设置(原子) ===" << std::endl;
std::vector<std::pair<std::string, std::string>> new_kvs = {
{"mkey1", "mval1"},
{"mkey2", "mval2"}
};
bool msetnx_result = redis.msetnx(new_kvs.begin(), new_kvs.end());
std::cout << "MSETNX 结果: " << (msetnx_result ? "成功" : "失败(部分键已存在)") << std::endl;
std::cout << "\n所有演示完成!" << std::endl;
return 0;
}

3.2 高级字符串操作
cpp
auto old_value = redis.getset("name", "Bob");
redis.set("greeting", "Hello");
redis.append("greeting", " World");
redis.set("message", "Hello World");
auto sub = redis.substr("message", 0, 4);
redis.setbit("bits", 5, 1);
auto bit = redis.getbit("bits", 5);
long long len = redis.strlen("message");
四、哈希(Hash)操作
哈希类型适合存储对象结构。
4.1 基本哈希操作
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
#include <unordered_map>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379");
redis.hset("user:1001", "name", "Alice");
redis.hset("user:1001", "email", "alice@example.com");
redis.hset("user:1001", "age", "25");
std::unordered_map<std::string, std::string> user_data = {
{"name", "Bob"},
{"email", "bob@example.com"},
{"age", "30"},
{"city", "Beijing"}
};
redis.hset("user:1002", user_data.begin(), user_data.end());
auto name = redis.hget("user:1001", "name");
auto all_fields = redis.hgetall("user:1001");
for (const auto& [field, value] : all_fields) {
std::cout << field << ": " << value << std::endl;
}
auto values = redis.hmget("user:1001", "name", "email");
bool has_email = redis.hexists("user:1001", "email");
auto fields = redis.hkeys("user:1001");
long long field_count = redis.hlen("user:1001");
redis.hdel("user:1001", "age");
redis.hset("stats", "visits", "100");
redis.hincrby("stats", "visits", 1);
redis.hincrbyfloat("stats", "score", 0.5);
return 0;
}

五、列表(List)操作
列表是简单的字符串列表,按插入顺序排序。
5.1 基本列表操作
cpp
#include <sw/redis++/redis++.h>
#include <vector>
#include <deque>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379?password=888888");
redis.lpush("tasks", "task1");
redis.lpush("tasks", "task2", "task3");
redis.rpush("queue", "item1");
redis.rpush("queue", "item2", "item3");
long long len = redis.llen("tasks");
auto items = redis.lrange("tasks", 0, -1);
auto first = redis.lindex("tasks", 0);
auto last = redis.lindex("tasks", -1);
redis.lset("tasks", 0, "new_task1");
auto task = redis.lpop("tasks");
auto item = redis.rpop("queue");
auto result = redis.blpop("tasks", std::chrono::seconds(5));
if (result) {
std::cout << "弹出的值: " << *result << std::endl;
}
redis.ltrim("tasks", 0, 9);
redis.rpoplpush("source", "destination");
redis.brpoplpush("source", "dest", std::chrono::seconds(1));
return 0;
}

六、集合(Set)操作
集合是无序且不重复的字符串集合。
6.1 基本集合操作
Set集合常用操作速查表:
| 操作 | C++ API | 说明 | 示例 |
|---|---|---|---|
| 添加成员 | sadd(key, members...) |
添加一个或多个成员,返回添加数量 | redis.sadd("tags", {"cpp", "redis"}) |
| 获取所有成员 | smembers(key) |
返回集合所有成员 | redis.smembers("tags", back_inserter(v)) |
| 检查成员 | sismember(key, member) |
检查成员是否存在,返回bool | redis.sismember("tags", "cpp") |
| 获取集合大小 | scard(key) |
返回成员数量 | redis.scard("tags") |
| 删除成员 | srem(key, members...) |
删除一个或多个成员 | redis.srem("tags", {"backend"}) |
| 随机获取 | srandmember(key, count?) |
获取随机成员,不删除 | redis.srandmember("tags") |
| 随机弹出 | spop(key, count?) |
随机弹出成员,会删除 | redis.spop("tags") |
| 交集 | sinter(keys...) |
多集合交集 | redis.sinter("set1", "set2", back_inserter(r)) |
| 并集 | sunion(keys...) |
多集合并集 | redis.sunion("set1", "set2", back_inserter(r)) |
| 差集 | sdiff(keys...) |
第一个集合与其他的差集 | redis.sdiff("set1", "set2", back_inserter(r)) |
| 交集存储 | sinterstore(dest, keys...) |
计算交集并存储到目标键 | redis.sinterstore("result", {"s1", "s2"}) |
| 并集存储 | sunionstore(dest, keys...) |
计算并集并存储到目标键 | redis.sunionstore("result", {"s1", "s2"}) |
| 差集存储 | sdiffstore(dest, keys...) |
计算差集并存储到目标键 | redis.sdiffstore("result", {"s1", "s2"}) |
| 移动成员 | smove(src, dest, member) |
原子移动,返回bool | redis.smove("set1", "set2", "a") |
代码示例:
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
#include <vector>
#include <iterator>
void print_set(const std::string& name, sw::redis::Redis& redis) {
std::vector<std::string> members;
redis.smembers(name, std::back_inserter(members));
std::cout << name << ": {";
for (size_t i = 0; i < members.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << members[i];
}
std::cout << "}" << std::endl;
}
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379?password=888888");
std::cout << "=== SADD/SREM 添加删除 ===" << std::endl;
std::vector<std::string> tags = {"cpp", "redis", "database"};
std::cout << "SADD tags [cpp, redis, database], count: " << redis.sadd("tags", tags.begin(), tags.end()) << std::endl;
print_set("tags", redis);
std::cout << "\n=== SISMEMBER/SCARD 查询 ===" << std::endl;
std::cout << "SISMEMBER tags 'cpp': " << redis.sismember("tags", "cpp") << std::endl;
std::cout << "SCARD tags: " << redis.scard("tags") << std::endl;
std::cout << "\n=== SRANDMEMBER/SPOP 随机操作 ===" << std::endl;
auto rand1 = redis.srandmember("tags");
std::cout << "SRANDMEMBER: " << (rand1 ? *rand1 : "nil") << std::endl;
auto popped = redis.spop("tags");
std::cout << "SPOP: " << (popped ? *popped : "nil") << std::endl;
print_set("tags", redis);
std::cout << "\n=== 集合运算 ===" << std::endl;
redis.sadd("set1", {"a", "b", "c", "d"});
redis.sadd("set2", {"c", "d", "e", "f"});
print_set("set1", redis);
print_set("set2", redis);
std::vector<std::string> keys = {"set1", "set2"};
std::vector<std::string> result;
redis.sinter(keys.begin(), keys.end(), std::back_inserter(result));
std::cout << "SINTER: {";
for (size_t i = 0; i < result.size(); ++i) { if (i > 0) std::cout << ", "; std::cout << result[i]; }
std::cout << "}" << std::endl;
result.clear();
redis.sunion(keys.begin(), keys.end(), std::back_inserter(result));
std::cout << "SUNION: {";
for (size_t i = 0; i < result.size(); ++i) { if (i > 0) std::cout << ", "; std::cout << result[i]; }
std::cout << "}" << std::endl;
result.clear();
redis.sdiff(keys.begin(), keys.end(), std::back_inserter(result));
std::cout << "SDIFF: {";
for (size_t i = 0; i < result.size(); ++i) { if (i > 0) std::cout << ", "; std::cout << result[i]; }
std::cout << "}" << std::endl;
std::cout << "\n=== SMOVE ===" << std::endl;
redis.smove("set1", "set2", "c");
std::cout << "SMOVE c from set1 to set2" << std::endl;
print_set("set1", redis);
print_set("set2", redis);
return 0;
}

七、有序集合(Sorted Set)操作
有序集合每个成员关联一个分数,按分数排序。
7.1 基本有序集合操作
Sorted Set常用操作速查表:
| 操作 | C++ API | 说明 | 示例 |
|---|---|---|---|
| 添加/更新成员 | zadd(key, member, score, ...) |
添加或更新分数 | redis.zadd("rank", "Alice", 100) |
| 获取分数 | zscore(key, member) |
返回成员的分数 | redis.zscore("rank", "Alice") |
| 获取集合大小 | zcard(key) |
返回成员数量 | redis.zcard("rank") |
| 增减分数 | zincrby(key, increment, member) |
增加/减少分数 | redis.zincrby("rank", 10, "Alice") |
| 删除成员 | zrem(key, members...) |
删除一个或多个成员 | redis.zrem("rank", {"Bob"}) |
| 按索引正序 | zrange(key, start, stop) |
按分数升序返回 | redis.zrange("rank", 0, 9, back_inserter(v)) |
| 按索引倒序 | zrevrange(key, start, stop) |
按分数降序返回 | redis.zrevrange("rank", 0, 9, back_inserter(v)) |
| 获取排名(升序) | zrank(key, member) |
返回升序排名(0开始) | redis.zrank("rank", "Alice") |
| 获取排名(降序) | zrevrank(key, member) |
返回降序排名(0开始) | redis.zrevrank("rank", "Alice") |
| 分数范围计数 | zcount(key, min, max) |
统计分数范围内的成员 | redis.zcount("rank", 90, 100) |
| 按分数正序 | zrangebyscore(key, min, max) |
按分数范围升序返回 | redis.zrangebyscore("rank", 90, 100, back_inserter(v)) |
| 按分数倒序 | zrevrangebyscore(key, max, min) |
按分数范围降序返回 | redis.zrevrangebyscore("rank", 100, 90, back_inserter(v)) |
| 删除排名范围 | zremrangebyrank(key, start, stop) |
删除指定排名范围内的成员 | redis.zremrangebyrank("rank", 0, 1) |
| 删除分数范围 | zremrangebyscore(key, min, max) |
删除指定分数范围内的成员 | redis.zremrangebyscore("rank", 0, 50) |
代码示例:
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
#include <vector>
#include <tuple>
#include <limits>
using namespace sw::redis;
void print_zset(const std::string& name, Redis& redis) {
std::vector<std::pair<std::string, double>> members;
redis.zrange(name, 0, -1, std::back_inserter(members));
std::cout << name << ": ";
for (const auto& [member, score] : members) {
std::cout << member << "(" << score << ") ";
}
std::cout << std::endl;
}
int main() {
Redis redis("tcp://127.0.0.1:6379?password=888888");
std::cout << "=== ZADD/ZREM 添加删除 ===" << std::endl;
redis.zadd("leaderboard", "Alice", 1000);
std::vector<std::pair<std::string, double>> members = {{"Bob", 950.0}, {"Charlie", 1100.0}, {"David", 1050.0}};
redis.zadd("leaderboard", members.begin(), members.end());
std::cout << "ZADD [Alice:1000, Bob:950, Charlie:1100, David:1050]" << std::endl;
print_zset("leaderboard", redis);
std::cout << "\n=== ZSCORE/ZCARD 查询 ===" << std::endl;
std::cout << "ZSCORE Alice: " << *redis.zscore("leaderboard", "Alice") << std::endl;
std::cout << "ZCARD: " << redis.zcard("leaderboard") << std::endl;
std::cout << "\n=== ZINCRBY 增减分数 ===" << std::endl;
redis.zincrby("leaderboard", 50, "Alice");
std::cout << "ZINCRBY Alice +50 -> " << *redis.zscore("leaderboard", "Alice") << std::endl;
std::cout << "\n=== ZRANGE/ZREVRANGE 排名查询 ===" << std::endl;
std::vector<std::pair<std::string, double>> range_result;
redis.zrange("leaderboard", 0, 2, std::back_inserter(range_result));
std::cout << "ZRANGE 0-2 (asc): ";
for (const auto& [m, s] : range_result) { std::cout << m << "(" << s << ") "; }
std::cout << std::endl;
range_result.clear();
redis.zrevrange("leaderboard", 0, 2, std::back_inserter(range_result));
std::cout << "ZREVRANGE 0-2 (desc): ";
for (const auto& [m, s] : range_result) { std::cout << m << "(" << s << ") "; }
std::cout << std::endl;
std::cout << "\n=== ZRANK/ZREVRANK 名次查询 ===" << std::endl;
std::cout << "ZRANK Alice (asc): " << *redis.zrank("leaderboard", "Alice") << std::endl;
std::cout << "ZREVRANK Alice (desc): " << *redis.zrevrank("leaderboard", "Alice") << std::endl;
std::cout << "\n=== ZCOUNT 范围计数 ===" << std::endl;
std::cout << "ZCOUNT [1000, 1100]: " << redis.zcount("leaderboard", BoundedInterval<double>(1000, 1100, BoundType::CLOSED)) << std::endl;
std::cout << "\n=== ZREM 移除成员 ===" << std::endl;
redis.zrem("leaderboard", "Bob");
std::cout << "ZREM Bob" << std::endl;
print_zset("leaderboard", redis);
return 0;
}

7.2 有序集合应用场景
cpp
// 实时排行榜实现
class Leaderboard {
private:
sw::redis::Redis& redis_;
std::string key_;
public:
Leaderboard(sw::redis::Redis& redis, const std::string& key)
: redis_(redis), key_(key) {}
void addPlayer(const std::string& player, double score) {
redis_.zadd(key_, player, score);
}
void updateScore(const std::string& player, double delta) {
redis_.zincrby(key_, delta, player);
}
std::optional<long long> getPlayerRank(const std::string& player) {
auto rank = redis_.zrevrank(key_, player);
if (rank) {
return *rank + 1; // 转换为1-based排名
}
return std::nullopt;
}
std::optional<double> getPlayerScore(const std::string& player) {
return redis_.zscore(key_, player);
}
std::vector<std::string> getTopPlayers(int n) {
return redis_.zrevrange(key_, 0, n - 1);
}
std::vector<std::pair<std::string, double>> getRangeWithScores(
double min_score, double max_score) {
std::vector<std::pair<std::string, double>> result;
redis_.zrangebyscore(key_,
std::make_pair(min_score, max_score),
std::back_inserter(result));
return result;
}
};
八、事务与管道
8.1 事务(Transaction)
事务和管道常用操作速查表:
| 操作 | C++ API | 说明 | 示例 |
|---|---|---|---|
| 开始事务 | transaction() |
创建事务对象 | auto txn = redis.transaction() |
| WATCH监控 | watch(keys...) |
监控键的变化 | redis.watch("key1", "key2") |
| 取消监控 | unwatch() |
取消所有监控 | redis.unwatch() |
| 执行事务 | exec() |
提交事务,返回结果 | auto results = txn.exec() |
| 放弃事务 | discard() |
放弃当前事务 | txn.discard() |
| 开始管道 | pipeline() |
创建管道对象 | auto pipe = redis.pipeline() |
| 批量执行 | exec() |
执行管道中的所有命令 | auto results = pipe.exec() |
| WATCH + MULTI | transaction(key, std::memory_order) |
乐观锁事务 | redis.transaction("key", std::memory_order_acquire) |
事务代码示例:
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379");
auto txn = redis.transaction();
txn.set("key1", "value1");
txn.incr("counter");
txn.get("key1");
auto results = txn.exec();
std::cout << "事务执行完成,结果数量: " << results.size() << std::endl;
auto pipe = redis.pipeline();
pipe.set("pipe_key", "pipe_value");
pipe.incr("pipe_counter");
pipe.get("pipe_key");
auto pipe_results = pipe.exec();
std::cout << "管道执行完成,结果数量: " << pipe_results.size() << std::endl;
return 0;
}

九、发布/订阅模式
Redis的发布/订阅功能用于消息广播。
9.1 发布者
发布/订阅常用操作速查表:
| 操作 | C++ API | 说明 | 示例 |
|---|---|---|---|
| 创建订阅者 | subscriber() |
创建订阅对象 | auto sub = redis.subscriber() |
| 订阅频道 | subscribe(channels...) |
订阅一个或多个频道 | sub.subscribe("news", "sports") |
| 取消订阅 | unsubscribe(channels...) |
取消订阅频道 | sub.unsubscribe("news") |
| 模式订阅 | psubscribe(patterns...) |
按模式订阅 | sub.psubscribe("news:*") |
| 取消模式订阅 | punsubscribe(patterns...) |
取消模式订阅 | sub.punsubscribe("news:*") |
| 消息回调 | on_message(callback) |
设置消息处理函数 | sub.on_message([](ch, msg){...}) |
| 模式回调 | on_pattern(callback) |
设置模式匹配处理 | sub.on_pattern([](pt, ch, msg){...}) |
| 消费消息 | consume() |
阻塞等待并处理消息 | sub.consume() |
| 发布消息 | publish(channel, msg) |
向频道发布消息 | redis.publish("news", "msg") |
发布者代码示例:
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
#include <thread>
#include <chrono>
int main() {
sw::redis::ConnectionOptions conn_opts;
conn_opts.host = "127.0.0.1";
conn_opts.port = 6379;
conn_opts.password = "888888";
sw::redis::Redis redis(conn_opts);
std::cout << "发布者启动..." << std::endl;
redis.publish("news", "Breaking news: Redis is awesome!");
std::cout << "已发布消息到news频道" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
redis.publish("sports", "Match started!");
std::cout << "已发布消息到sports频道" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
redis.publish("weather", "Sunny today");
std::cout << "已发布消息到weather频道" << std::endl;
std::cout << "发布者完成" << std::endl;
return 0;
}

9.2 订阅者
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379?password=888888");
auto sub = redis.subscriber();
sub.subscribe("news", "sports");
sub.on_message([](const std::string& channel, const std::string& msg) {
std::cout << "[" << channel << "] " << msg << std::endl;
});
sub.on_pattern([](const std::string& pattern,
const std::string& channel,
const std::string& msg) {
std::cout << "Pattern " << pattern << " matched "
<< channel << ": " << msg << std::endl;
});
sub.psubscribe("news:*");
while (true) {
try {
sub.consume();
} catch (const sw::redis::Error& e) {
std::cerr << "Error: " << e.what() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
return 0;
}
十、地理位置操作
Redis支持存储地理位置信息和距离计算。
10.1 基本地理位置操作
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379?password=888888");
redis.geoadd("cities", "Beijing", 116.407396, 39.904201);
redis.geoadd("cities", "Shanghai", 121.473701, 31.230416);
auto pos = redis.geopos("cities", "Beijing");
if (pos) {
std::cout << "Beijing位置已添加" << std::endl;
}
auto dist = redis.geodist("cities", "Beijing", "Shanghai");
if (dist) {
std::cout << "Beijing to Shanghai: " << *dist << " km" << std::endl;
}
return 0;
}

十一、流(Stream)操作
Streams是Redis 5.0引入的日志数据结构,适合构建消息队列。
11.1 基本流操作
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379?password=888888");
// 添加流条目
auto id1 = redis.xadd("mystream", "*",
"field1", "value1", "field2", "value2");
auto id2 = redis.xadd("mystream", "*",
"event", "login", "user_id", "12345");
// 指定ID添加
auto id3 = redis.xadd("mystream", "1700000000000-0",
"event", "logout", "user_id", "12345");
// 获取流长度
long long len = redis.xlen("mystream");
// 读取流数据
auto messages = redis.xrange("mystream", "-", "+");
for (const auto& [id, fields] : messages) {
std::cout << "ID: " << id << std::endl;
for (const auto& [k, v] : fields) {
std::cout << " " << k << ": " << v << std::endl;
}
}
// 反向读取
auto reversed = redis.xrevrange("mystream", "+", "-", 10);
// 删除流条目
redis.xdel("mystream", id1, id2);
// 修剪流
redis.xtrim("mystream", 1000, true); // 保留最新1000条
return 0;
}

11.2 消费者组
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379?password=888888");
std::string stream_key = "orders";
std::string group_name = "order_processors";
std::string consumer_name = "consumer-1";
// 创建消费者组
try {
redis.xgroup_create(stream_key, group_name, "0"); // 从头开始
// 或者
redis.xgroup_create(stream_key, group_name, "$"); // 只读新消息
} catch (const sw::redis::Error& e) {
// 组可能已存在
}
// 添加一些消息
redis.xadd(stream_key, "*", "order_id", "1001", "amount", "99.99");
redis.xadd(stream_key, "*", "order_id", "1002", "amount", "149.99");
// 读取新消息(阻塞)
auto messages = redis.xreadgroup(group_name, consumer_name,
stream_key, ">");
// 处理消息
for (const auto& [id, fields] : messages) {
std::cout << "处理订单: " << id << std::endl;
// 业务逻辑...
// 确认消息
redis.xack(stream_key, group_name, id);
}
// 读取待处理消息(之前获取但未确认的)
auto pending = redis.xpending(stream_key, group_name);
auto pending_msgs = redis.xclaim(stream_key, group_name, consumer_name,
std::chrono::minutes(1), stream_key, "+", "-", 10);
return 0;
}
十二、错误处理与最佳实践
12.1 异常类型
cpp
#include <sw/redis++/redis++.h>
#include <iostream>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379?password=888888");
try {
// Redis操作
redis.set("key", "value");
} catch (const sw::redis::TimeoutError& e) {
// 操作超时
std::cerr << "超时: " << e.what() << std::endl;
} catch (const sw::redis::Error& e) {
// 其他Redis错误
std::cerr << "Redis错误: " << e.what() << std::endl;
}
return 0;
}
12.2 连接重试机制
cpp
#include <sw/redis++/redis++.h>
#include <thread>
#include <chrono>
sw::redis::Redis create_reliable_connection() {
const std::string host = "127.0.0.1";
const int port = 6379;
const int max_retries = 5;
const int retry_delay_ms = 1000;
sw::redis::ConnectionOptions conn_opts;
conn_opts.host = host;
conn_opts.port = port;
conn_opts.retry_interval = std::chrono::milliseconds(100);
conn_opts.max_retry = 3;
return sw::redis::Redis(conn_opts);
}
void run_with_retry(sw::redis::Redis& redis, int max_attempts = 3) {
for (int attempt = 0; attempt < max_attempts; ++attempt) {
try {
redis.set("key", "value");
return; // 成功则返回
} catch (const sw::redis::Error& e) {
if (attempt < max_attempts - 1) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
throw; // 最后一次尝试失败后抛出异常
}
}
}

12.3 最佳实践建议
| 方面 | 建议 |
|---|---|
| 连接管理 | 使用连接池,配置合适的池大小 |
| 超时设置 | 合理设置连接超时和命令超时 |
| 错误处理 | 捕获异常并实现重试机制 |
| 内存管理 | 注意键的过期时间,避免内存泄漏 |
| 批处理 | 大量操作使用管道减少网络往返 |
| 安全 | 生产环境启用密码认证 |
| 监控 | 使用Redis内置命令监控性能 |
| 序列化 | 复杂对象建议使用JSON或Protobuf序列化 |
十三、高级特性
13.1 集群支持
cpp
#include <sw/redis++/redis++.h>
int main() {
// 集群连接
sw::redis::ConnectionOptions conn_opts;
conn_opts.password = "your_password";
conn_opts.socket_timeout = std::chrono::milliseconds(200);
sw::redis::ConnectionPoolOptions pool_opts;
pool_opts.pool_size = 16;
pool_opts.min_idle_size = 8;
sw::redis::RedisCluster cluster(
"127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003,"
"127.0.0.1:7004,127.0.0.1:7005",
conn_opts,
pool_opts
);
// 集群操作
cluster.set("key", "value");
auto val = cluster.get("key");
// 集群自动处理槽位映射
cluster.set("another_key", "another_value");
return 0;
}
13.2 哨兵模式
cpp
#include <sw/redis++/redis++.h>
int main() {
sw::redis::Sentinel sentinel;
sentinel.add("127.0.0.1", 26379); // 添加哨兵节点
sentinel.add("127.0.0.1", 26380);
sw::redis::ConnectionOptions conn_opts;
conn_opts.password = "your_password";
// 获取主节点连接
auto master = sentinel.master("mymaster", conn_opts);
master->set("key", "value");
// 获取从节点连接
auto slaves = sentinel.slaves("mymaster", conn_opts);
if (!slaves.empty()) {
auto slave = slaves[0];
auto val = slave->get("key");
}
return 0;
}
13.3 脚本执行
cpp
#include <sw/redis++/redis++.h>
int main() {
sw::redis::Redis redis("tcp://127.0.0.1:6379");
// Lua脚本
std::string lua_script = R"(
local key = KEYS[1]
local inc = tonumber(ARGV[1])
local current = redis.call('GET', key) or '0'
local new_val = tonumber(current) + inc
redis.call('SET', key, new_val)
return new_val
)";
// 执行脚本
auto result = redis.eval<int>(lua_script, 1, "counter", "10");
// 执行已加载的脚本
std::string sha = redis.script_load(lua_script);
auto cached_result = redis.evalsha<int>(sha, 1, "counter", "5");
// 检查脚本是否存在
bool exists = redis.script_exists(sha);
// 清空所有脚本缓存
redis.script_flush();
return 0;
}
十四、综合示例
14.1 缓存层实现
cpp
#include <sw/redis++/redis++.h>
#include <mutex>
#include <memory>
template<typename T>
class RedisCache {
private:
std::unique_ptr<sw::redis::Redis> redis_;
std::chrono::seconds default_ttl_;
public:
RedisCache(const std::string& url,
std::chrono::seconds default_ttl = std::chrono::hours(1))
: redis_(std::make_unique<sw::redis::Redis>(url)),
default_ttl_(default_ttl) {}
void set(const std::string& key, const T& value) {
set(key, value, default_ttl_);
}
void set(const std::string& key, const T& value,
std::chrono::seconds ttl) {
(*redis_) << key << value; // 依赖operator<<重载
redis_->expire(key, ttl);
}
std::optional<T> get(const std::string& key) {
T value;
if ((*redis_) >> key >> value) {
return value;
}
return std::nullopt;
}
bool exists(const std::string& key) {
return redis_->exists(key) > 0;
}
void remove(const std::string& key) {
redis_->del(key);
}
void clear() {
redis_->flushdb();
}
};
14.2 会话管理
cpp
#include <sw/redis++/redis++.h>
#include <unordered_map>
#include <random>
#include <iomanip>
class SessionManager {
private:
sw::redis::Redis& redis_;
std::chrono::seconds session_ttl_;
public:
struct Session {
std::string user_id;
std::string username;
std::string ip_address;
std::chrono::system_clock::time_point created_at;
std::unordered_map<std::string, std::string> data;
};
SessionManager(sw::redis::Redis& redis,
std::chrono::seconds ttl = std::chrono::hours(24))
: redis_(redis), session_ttl_(ttl) {}
std::string create_session(const std::string& user_id,
const std::string& username,
const std::string& ip) {
std::string session_id = generate_session_id();
std::string key = "session:" + session_id;
redis_.hset(key, "user_id", user_id);
redis_.hset(key, "username", username);
redis_.hset(key, "ip_address", ip);
redis_.hset(key, "created_at",
std::to_string(std::chrono::system_clock::now().time_since_epoch().count()));
redis_.expire(key, session_ttl_);
return session_id;
}
bool validate_session(const std::string& session_id) {
std::string key = "session:" + session_id;
return redis_.exists(key) > 0;
}
void extend_session(const std::string& session_id) {
std::string key = "session:" + session_id;
redis_.expire(key, session_ttl_);
}
void destroy_session(const std::string& session_id) {
std::string key = "session:" + session_id;
redis_.del(key);
}
void set_session_data(const std::string& session_id,
const std::string& field,
const std::string& value) {
std::string key = "session:" + session_id;
redis_.hset(key, field, value);
}
std::optional<std::string> get_session_data(
const std::string& session_id,
const std::string& field) {
std::string key = "session:" + session_id;
return redis_.hget(key, field);
}
private:
std::string generate_session_id() {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 15);
std::ostringstream oss;
for (int i = 0; i < 32; ++i) {
oss << std::hex << std::setw(1) << std::setfill('0') << dis(gen);
}
return oss.str();
}
};
十五、性能优化
15.1 连接优化
cpp
// 优化的连接池配置
sw::redis::ConnectionPoolOptions create_optimal_pool() {
sw::redis::ConnectionPoolOptions pool;
// 根据CPU核心数和负载调整
pool.pool_size = std::thread::hardware_concurrency() * 2;
pool.min_idle_size = std::thread::hardware_concurrency();
// 超时配置
pool.wait_timeout = std::chrono::milliseconds(100);
pool.connection_timeout = std::chrono::milliseconds(200);
// 连接生命周期
pool.idle_timeout = std::chrono::minutes(5);
pool.retry_interval = std::chrono::milliseconds(100);
return pool;
}
15.2 批量操作优化
cpp
// 大批量数据导入
void bulk_import(sw::redis::Redis& redis,
const std::vector<std::pair<std::string, std::string>>& data,
int batch_size = 1000) {
auto pipe = redis.pipeline(10000); // 大缓冲区
for (size_t i = 0; i < data.size(); ++i) {
pipe.set(data[i].first, data[i].second);
// 每批执行一次
if ((i + 1) % batch_size == 0) {
pipe.execute();
pipe = redis.pipeline(10000); // 重置管道
}
}
// 处理剩余数据
if (!data.empty() && data.size() % batch_size != 0) {
pipe.execute();
}
}
总结
本文详细介绍了在C++中使用redis-plus-plus库操作Redis的各种方法,涵盖了:
- 基础操作:字符串、哈希、列表、集合、有序集合
- 高级特性:事务、管道、发布订阅
- 特殊功能:地理位置、流、集群
- 最佳实践:错误处理、性能优化、安全建议
redis-plus-plus库提供了现代化、类型安全且易于使用的C++ API,是C++项目中集成Redis的优秀选择。