C++中使用Redis指南:基于redis-plus-plus库

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的优秀选择。

相关推荐
星晨雪海2 小时前
Redis-逻辑查询详情讲解
java·开发语言
大鹏说大话2 小时前
Java线程池调优实战:从核心参数到避坑指南
java·开发语言
lolo大魔王3 小时前
Go语言的基础语法
开发语言·后端·golang
小陈工3 小时前
Python Web开发入门(八):用户认证系统实现,给你的应用加上安全锁
开发语言·前端·数据库·python·安全·django·sqlite
铅笔侠_小龙虾3 小时前
Miniconda + Poetry 实战
开发语言·python
深海空无一人3 小时前
python基础
开发语言·python
難釋懷3 小时前
Redis缓存预热
redis·spring·缓存
仟濹3 小时前
【算法打卡day35(2026-03-31 周二)】DFS专项训练2(今日算法:DFS & 记忆化搜索 & 回溯)
c++·算法·蓝桥杯·深度优先
大尚来也3 小时前
Java多线程实战:从基础创建到返回值获取的深度解析
开发语言