一、安裝使用 --Ubuntu 下啟用
1. 前置依賴 - hiredis
- hiredis 是一個用 C 語言實現的 Redis 客戶端庫,
redis-plus-plus
庫基于hiredis
實現。 - 在開始之前,請確保已安裝
libhiredis-dev
,可以通過以下命令安裝:
sudo apt install libhiredis-dev
2. 安裝 redis-plus-plus
C++操作redis的庫有很多,咱們此處使用 redis-plus-plus。這個庫的功能強大,使用簡單。Github 地址:【https://github.com/sewenew/redis-plus-plus】
redis-plus-plus
是一個功能強大且易于使用的 C++ Redis 客戶端庫。它具有統一的接口風格,使得使用起來非常方便。- 以下是安裝步驟:克隆
redis-plus-plus
的 GitHub 倉庫到本地
git clone https://github.com/sewenew/redis-plus-plus.git
- 其實在 Ubuntu 下 由于網速限制,可能會出現超時 clone 失敗情況,當然大家可以去網上搜教程改變 ip 來提高下載速度。但是這里我的思路是 下載壓縮包到本地,然后把壓縮包拖入到 Ubuntu 中,然后解包即可
進入克隆下來的項目目錄,并創建構建目錄:
cd redis-plus-plus
mkdir build && cd build
- 創建一個 build 目錄是習慣做法,并非必要,目的是為了讓編譯生成得到臨時文件放到 build 下,避免污染源代碼目錄
由于 redis-plus-plus 是使用 CMake 作為構建工具的
- CMake:相當于是 Makefile 的升級版,因為 Makefile 本身功能比較簡陋,比較原始,寫起來也更加麻煩,所以實際開發中很少手寫 Makefile,所以需要通過程序來生成 Makefile,cmake(C 語言)就是一個生成 Makefile 的工具
使用 CMake 配置并編譯安裝:
sudo apt install cmakelighthouse@VM-8-10-ubuntu:build$ cmake .. # 生成 makefile, 此次 .. 指向的是 CMakeLists.txt 文件所在目錄lighthouse@VM-8-10-ubuntu:build$ ll
total 104
drwxrwxr-x 6 lighthouse lighthouse 4096 Jul 4 09:34 ./
drwxrwxr-x 7 lighthouse lighthouse 4096 Jul 4 09:29 ../
drwxrwxr-x 2 lighthouse lighthouse 4096 Jul 4 09:34 cmake/
-rw-rw-r-- 1 lighthouse lighthouse 19575 Jul 4 09:34 CMakeCache.txt
drwxrwxr-x 7 lighthouse lighthouse 4096 Jul 4 09:34 CMakeFiles/
-rw-rw-r-- 1 lighthouse lighthouse 8885 Jul 4 09:34 cmake_install.cmake
-rw-r--r-- 1 lighthouse lighthouse 3616 Jul 4 09:34 CPackConfig.cmake
-rw-r--r-- 1 lighthouse lighthouse 4100 Jul 4 09:34 CPackSourceConfig.cmake
-rw-rw-r-- 1 lighthouse lighthouse 33406 Jul 4 09:34 Makefile
drwxrwxr-x 3 lighthouse lighthouse 4096 Jul 4 09:34 src/
drwxrwxr-x 3 lighthouse lighthouse 4096 Jul 4 09:34 test/lighthouse@VM-8-10-ubuntu:build$ make # 編譯
lighthouse@VM-8-10-ubuntu:build$ sudo make install # 把編譯生成.a .so庫拷貝到系統目錄
說明:
redis-plus-plus
庫支持多種方式傳遞參數,如 初始化列表或迭代器對。- 當函數需要返回多個數據時,通常會使用插入迭代器將結果添加到容器中。
- 對于可能返回無效值的情況,
redis-plus-plus
通常會使用std::optional
來表示。 - 上面編譯安裝的庫默認在
/usr/local
路徑下的
3. 示例
Quick Start 創建一個簡單的 C++ 程序來連接 Redis 并發送一個 ping
命令,以檢查連接是否成功。
① 包含 redis-plus-plus 的頭文件路徑如下:
/usr/local/include/sw/redis++ # sw 目錄是作者名字縮寫lighthouse@VM-8-10-ubuntu:redis++$ ll
total 668
drwxr-xr-x 3 root root 4096 Jul 4 09:37 ./
drwxr-xr-x 3 root root 4096 Jul 4 09:37 ../
-rw-r--r-- 1 root root 29717 Jun 12 23:42 cmd_formatter.h
-rw-r--r-- 1 root root 5026 Jun 12 23:42 command_args.h
-rw-r--r-- 1 root root 78953 Jun 12 23:42 command.h
-rw-r--r-- 1 root root 4211 Jun 12 23:42 command_options.h
-rw-r--r-- 1 root root 5803 Jun 12 23:42 connection.h
-rw-r--r-- 1 root root 5195 Jun 12 23:42 connection_pool.h
-rw-r--r-- 1 root root 1361 Jun 12 23:42 cxx_utils.h
-rw-r--r-- 1 root root 4353 Jun 12 23:42 errors.h
-rw-r--r-- 1 root root 66 Jul 4 09:34 hiredis_features.h
drwxr-xr-x 2 root root 4096 Jul 4 09:37 patterns/
-rw-r--r-- 1 root root 1466 Jun 12 23:42 pipeline.h
-rw-r--r-- 1 root root 68779 Jun 12 23:42 queued_redis.h
-rw-r--r-- 1 root root 7118 Jun 12 23:42 queued_redis.hpp
-rw-r--r-- 1 root root 58456 Jun 12 23:42 redis_cluster.h
-rw-r--r-- 1 root root 56322 Jun 12 23:42 redis_cluster.hpp
-rw-r--r-- 1 root root 1019 Jun 12 23:42 redis++.h # 包含 redis-plus-plus 的頭文件
-rw-r--r-- 1 root root 175414 Jun 12 23:42 redis.h
-rw-r--r-- 1 root root 50024 Jun 12 23:42 redis.hpp
-rw-r--r-- 1 root root 2344 Jun 12 23:42 redis_uri.h
-rw-r--r-- 1 root root 21785 Jun 12 23:42 reply.h
-rw-r--r-- 1 root root 3883 Jun 12 23:42 sentinel.h
-rw-r--r-- 1 root root 3458 Jun 12 23:42 shards.h
-rw-r--r-- 1 root root 3664 Jun 12 23:42 shards_pool.h
-rw-r--r-- 1 root root 8242 Jun 12 23:42 subscriber.h
-rw-r--r-- 1 root root 1200 Jun 12 23:42 tls.h
-rw-r--r-- 1 root root 2101 Jun 12 23:42 transaction.h
-rw-r--r-- 1 root root 6295 Jun 12 23:42 utils.h
-rw-r--r-- 1 root root 992 Jun 12 23:42 version.h# 如果找不到的,也可以用 find 進行查找
lighthouse@VM-8-10-ubuntu:~$ find /usr/ -name "redis++*"/usr/local/lib/pkgconfig/redis++.pc
/usr/local/include/sw/redis++
/usr/local/include/sw/redis++/redis++.h
② 編寫 main.cc 代碼
#include <iostream>
#include <string>
#include <sw/redis++/redis++.h>using namespace std;int main(){// 構建Redis對象的時候,在構造函數中,指定Redis服務器的地址和端口sw::redis::Redis redis("tcp://127.0.0.1:6379");string ret = redis.ping();cout << ret << endl;return 0;
}
③ Makefile 文件編寫(需要引入 庫文件, redis++ 的靜態庫/ hiredis的靜態庫/ 線程庫)
main: main.ccg++ -o $@ $^ -std=c++17 /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -lpthread
.PHONY: clean
clean:rm main
④ 結果如下:
lighthouse@VM-8-10-ubuntu:redis-code$ ./main
PONG
注意:如果我們啟用了密碼驗證的話,就會輸出錯誤信息如下:
- Redis 默認是沒有設置密碼的。如果你之前設置了密碼(比如通過修改
redis.conf
文件中的requirepass
配置項),那么每次連接都需要使用.auth("your_password")
進行認證。
terminate called after throwing an instance of 'sw::redis::ReplyError'what(): NOAUTH Authentication required.
Aborted (core dumped)
此時就需要在創建 Redis 對象后,添加下面一段代碼:
// 如果 Redis 設置了密碼,請在這里添加認證
redis.auth("your_redis_password"); // 替換為你的 Redis 密碼
二、常用類型接口學習
-
接口的相似性:sw/redis++ 的接口設計與 Redis 原生命令非常相似,這使得熟悉 Redis 命令的開發者可以快速上手。
-
參數傳遞方式:
- 當需要傳入多個參數時,有兩種方法:
- 使用容器(如 std::vector 或 std::set)保存參數,并傳遞容器的迭代器。
- 直接使用初始化列表 {} 傳遞參數。
-
鍵值對傳遞:如果需要傳遞鍵值對,可以使用 std::pair 來表示,例如 std::pair<std::string, std::string>。
-
常用 C++ 類型:
- OptionalString:用于接收可能為 nil 的返回值,可以判斷是否為空。
- StringView:用于傳遞只讀字符串,但通常直接使用 std::string 即可。
- long long:用于接收整數類型的返回值。
- double:用于 ZSET 中的分數(score)類型。
-
輸出多個變量:如果函數需要輸出多個變量,應該 傳遞一個插入迭代器(如 std::inserter 或 std::back_inserter(返回值為
std::back_insert_iterator
)),這樣可以將結果直接插入到容器中。
前置準備
下面執行模板代碼如下:
#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <sw/redis++/redis++.h>
#include "../util.hpp"
using namespace std::chrono_literals; // 字面量
using sw::redis::Redis;int main(){sw::redis::Redis redis("tcp://127.0.0.1:6379");// 如果 Redis 設置了密碼,請在這里添加認證redis.auth("123456"); // 替換 Redis 密碼// 測試函數return 0;
}
Makefile 文件如下:
test:test.ccg++ -o $@ $^ -std=c++17 /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -lpthread
.PHONY: clean
clean:rm -f test test.o
Util.hpp 代碼如下:
#pragma#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <unordered_map>// 1. 通用的容器打印函數, 可以打印任何類型的容器
template<typename T>
inline void printContainer(const T& container) {for (const auto& elem : container) std::cout << elem << std::endl;
}
// 2. 專為存儲 std::pair 類型元素的容器設計
template<typename T>
inline void printContainerPair(const T& container) {for (auto& elem : container) {// 此處預期 elem 是一個 std::pairstd::cout << elem.first << ": " << elem.second << std::endl;}
}
// 3. 專為存儲 std::optional 類型元素的容器設計
template<typename T>
inline void printContainerOptional(const T& container) {for (const auto& elem : container) {// 此處預期 elem 是一個 optional 類型的元素, 打印之前, 先判定一下, 看是否有效if (elem) {std::cout << elem.value() << std::endl;} else {std::cout << "元素無效" << std::endl;}}
}
1. 通用命令
① get/set
這里的 set 函數如下:
bool set(const sw::redis::StringView &key, const sw::redis::String View &val, bool keepttl, sw::redis::UpdateType type = sw::redis::UpdateType::ALWAYS)
- 這里的參數和之前的 set 命令選項有很大關聯
- 這里用到了 StringView 的類型(在 sw::redis 命名空間 中,StringView 只讀不可修改)
編寫代碼如下:
void test1(sw::redis::Redis &redis){std::cout << "Get 和 Set 使用" << std::endl;redis.flushall(); // 使用前先清空數據庫, 避免之前殘留數據產生干擾redis.set("key1", "value1"); // 使用 set 命令設置鍵值對// 使用 get 命令獲取鍵的值auto value = redis.get("key1"); // 這里返回的OptionalString可以表示一個非法值if (value) { // optional 可以隱式轉換成 bool 類型std::cout << "key1: " << *value << std::endl; // 輸出: key1: value2} else {std::cout << "key1 not found" << std::endl;}auto value2 = redis.get("key2"); // key2 不存在std::cout << "key2: " << value2.value() << std::endl; // 輸出: key2: <nil>, 這里就不能用指針來輸出了, 最好用 value() 方法
}
輸出如下:
lighthouse@VM-8-10-ubuntu:common$ ./test
Get 和 Set 使用
key1: value1
terminate called after throwing an instance of 'std::bad_optional_access'what(): bad optional access
Aborted (core dumped)
- 這里 key2 不存在,因此 value2 就是 optional 的非法狀態,就會報異常
② exists/del
編寫代碼如下:
void test2(sw::redis::Redis &redis){std::cout << "Exists 和 Del 使用" << std::endl;redis.flushall();redis.set("key", "value1");auto ret = redis.exists("key");std::cout << "key exists: " << ret << std::endl;// 多級判斷 redis.exists({"key1", "key2", "key3"}) 返回一個 值 表示幾個 key 存在auto ret = redis.exists({"key", "key2", "key3"});std::cout << "key, key2, key3 exists: " << ret << std::endl; redis.del("key");ret = redis.exists("key");std::cout << "key exists after delete: " << ret << std::endl;
}
輸出如下:
lighthouse@VM-8-10-ubuntu:common$ ./test
Exists 和 Del 使用
key exists: 1
key exists after delete: 0
③ keys
編寫代碼如下:
void test3(sw::redis::Redis &redis){std::cout << "Keys 使用" << std::endl;redis.flushall();redis.set("key1", "value1");redis.set("key2", "value2");redis.set("key3", "value3");// 獲取所有的鍵 // keys 的第二個參數, 是一個插入迭代器, 需準備好一個保存結果的容器// 接下來再創建一個插入迭代器指向容器的位置, 把 keys 的結果插入到容器中std::vector<std::string> ret;auto it = std::back_insert_iterator(ret);redis.keys("*", it); // 匹配所有鍵for (const auto& key : ret) {std::cout << "Key: " << key << std::endl;}
}
輸出如下:
lighthouse@VM-8-10-ubuntu:common$ ./test
Keys 使用
Key: key1
Key: key3
Key: key2
④ expire/ttl
編寫代碼如下:
void test4(sw::redis::Redis &redis){using namespace std::chrono_literals; // 使用 chrono 的字面量std::cout << "Expire 和 TTL" << std::endl;redis.flushall();redis.set("key1", "value1");redis.expire("key1", std::chrono::seconds(5)); // 設置 key1 的過期時間為 5 秒std::this_thread::sleep_for(std::chrono::seconds(3s)); // 等待 3 秒long long time = redis.ttl("key1"); // 獲取 key1 的剩余過期時間 if (time) {std::cout << "TTL for key1: " << time << " seconds" << std::endl;} else {std::cout << "key1 does not have a TTL" << std::endl;}
}
輸出如下:
lighthouse@VM-8-10-ubuntu:common$ ./test
Expire 和 TTL
TTL for key1: 2 seconds
⑤ type
編寫代碼如下:
void test5(sw::redis::Redis &redis){std::cout << "Type" << std::endl;redis.flushall();redis.set("key1", "1111");std::string res = redis.type("key1"); std::cout << "Type of key1: " << res << std::endl; redis.hset("hash1", "field1", "value1");res = redis.type("hash1");std::cout << "Type of hash1: " << res << std::endl; // 輸出: Type of hash1: hash
}
輸出如下:
lighthouse@VM-8-10-ubuntu:common$ ./test
Type
Type of key1: string
Type of hash1: hash
2. String
① set 使用
void test1(sw::redis::Redis &redis){std::cout << "Set 帶有超時時間" << std::endl;redis.flushall();redis.set("key", "111", 10s);std::this_thread::sleep_for(3s);long long time = redis.ttl("key");std::cout << "time: " << time << std::endl;
}void test2(sw::redis::Redis &redis){std::cout << "set NX 和 XX" << std::endl;redis.flushall();redis.set("key", "111");// set 的重載版本中, 沒有單獨提供 NX 和 XX 的版本, 必須搭配過期時間的版本來使用. redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);auto value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;} else {std::cout << "key 不存在!" << std::endl;}
}// 輸出如下
Set 帶有超時時間
time: 7
set NX 和 XX
value: 222
② mget & mset
- 可以把多個鍵值對提前組織到容器 vector<pair<string, string>> 中. 以迭代器的形式告訴 mset
void test3(sw::redis::Redis &redis){std::cout << "mset & mget" << std::endl;redis.flushall();// // 寫法一: 初始化列表描述多個鍵值對// redis.mset({// std::make_pair("key1", "value1"),// std::make_pair("key2", "value2"),// std::make_pair("key3", "value3")// });// 寫法二: 使用 std::vector<std::pair<std::string, std::string>> 描述多個鍵值對, 然后以迭代器的形式告訴 msetstd::vector<std::pair<std::string, std::string>> keys = {{"key1", "111"},{"key2", "222"},{"key3", "333"}};redis.mset(keys.begin(), keys.end());std::vector<sw::redis::OptionalString> res;auto it = std::back_inserter(res);redis.mget({"key1", "key2", "key3"}, it);printContainerOptional(res);
}// 輸出
mset & mget
111
222
333
③ getrange & setrange
void test4(Redis& redis) {std::cout << "getrange 和 setrange" << std::endl;redis.flushall();redis.set("key", "abcdefghijk"); std::string result = redis.getrange("key", 2, 5);std::cout << "result: " << result << std::endl;redis.setrange("key", 2, "xyz");auto value = redis.get("key");std::cout << "value: " << value.value() << std::endl;
}// 輸出
getrange 和 setrange
result: cdef
value: abxyzfghijk
④ incr & decr
void test5(Redis& redis) {redis.flushall();redis.set("key", "100");//對比關注 如下兩種返回結果//返回結果1:long longlong long result = redis.incr("key");std::cout << "result: " << result << std::endl;//返回結果2:對象auto value = redis.get("key");std::cout << "value: " << value.value() << std::endl;result = redis.decr("key");std::cout << "result: " << result << std::endl;value = redis.get("key");std::cout << "value: " << value.value() << std::endl;
}// 輸出
result: 101
value: 101
result: 100
value: 100
3. List
① lpush & rpush & lrange
void test1(Redis& redis){redis.flushall(); redis.lpush("mylist", "111"); // 插入單個元素, 基于初始化列表// 插入一組元素, 基于迭代器std::vector<std::string> values = {"222", "333", "444"};redis.rpush("mylist", values.begin(), values.end());// 獲取列表元素std::vector<std::string> res;// auto it = std::back_inserter(res);redis.lrange("mylist", 0, -1, std::back_insert_iterator(res));// 也可以用下面的寫法// redis.lrange("mylist", 0, -1, std::back_inserter(res));printContainer(res);
}// 輸出
111
222
333
444
關于 std::back_inserter(res)
和 std::back_insert_iterator(res)
std::back_inserter(res)
是工廠函數,返回std::back_insert_iterator<std::vector<std::string>>
。std::back_insert_iterator(res)
是直接構造迭代器,模板實參由 CTAD(C++17 起)或你手動寫出。
編譯器展開后兩種寫法生成的臨時對象類型一模一樣,行為也一樣:
operator=
里調用res.push_back(value)
,因此最終res
的內容、性能、異常安全性均無任何差異。
結論:用 std::back_inserter(res)
更短、更慣用;用 std::back_insert_iterator(res)
只是顯式寫出類型,二者可互換。
② lpop & rpop
void test2(Redis& redis){redis.flushall();redis.rpush("key", {"1", "2", "3", "4"}); // 構造一個 listauto result = redis.lpop("key");if (result) std::cout << "lpop: " << result.value() << std::endl;result = redis.rpop("key");if (result) std::cout << "rpop: " << result.value() << std::endl;
}// 輸出
lpop: 1
rpop: 4
③ blpop
void test3(Redis& redis){redis.flushall(); auto res = redis.blpop({"key1", "key2"}, 5s);if (res) {std::cout << "blpop: " << res->first << ", " << res->second << std::endl;} else {std::cout << "blpop timeout" << std::endl;}
}// 輸出
blpop timeout
- TIPS:對于
std::optional
類型來說,可以直接使用->
訪問optional
內部包含的元素的成員
④ llen
void test4(Redis& redis){std::cout << "llen" << std::endl;redis.flushall(); redis.rpush("mylist", {"1", "2", "3", "4"});auto len = redis.llen("mylist");std::cout << "Length of mylist: " << len << std::endl;
}// 輸出
Length of mylist: 4
4. Hash
① Hset 和 Hget
向哈希中添加字段-值對,并從中獲取特定字段的值。
void test1(Redis& redis){// 清空數據庫redis.flushall();// 設置單個字段-值對redis.hset("key", "f1", "111");// 使用 std::pair 設置另一個字段-值對redis.hset("key", std::make_pair("f2", "222"));// 批量設置多個字段-值對redis.hset("key", {std::make_pair("f3", "333"), std::make_pair("f4", "444")});// 使用容器批量設置std::vector<std::pair<std::string, std::string>> fields = {std::make_pair("f5", "555"),std::make_pair("f6", "666")};redis.hset("key", fields.begin(), fields.end());// 獲取并打印字段 f3 的值auto result = redis.hget("key", "f3");if (result) {std::cout << "result: " << result.value() << std::endl;} else {std::cout << "result 無效!" << std::endl;}
}// 輸出
result: 333
② Hexists & Hdel & Hlen
- 檢查指定字段是否存在于給定的哈希中
- 刪除哈希中的一個或多個字段,并輸出受影響的字段數量
- 查看哈希長度
void test2(Redis& redis){// 清空數據庫redis.flushall();redis.hset("key", {std::make_pair("f1", "111"), std::make_pair("f2", "222"), std::make_pair("f3", "333")});std::cout << "key 的字段數量: " << redis.hlen("key") << std::endl;std::cout << "f1 是否存在: " << redis.hexists("key", "f1") << std::endl;redis.hdel("key", "f2");std::cout << "刪除f2后, f2 是否存在: " << redis.hexists("key", "f2") << std::endl;
}// 輸出
key 的字段數量: 3
f1 是否存在: 1
刪除f2后, f2 是否存在: 0
③ Hkeys 和 Hvals
獲取哈希中的所有 field 和對應的 value,并分別打印出來
void test3(Redis& redis){// 清空數據庫redis.flushall();redis.hset("key", {std::make_pair("f1", "111"), std::make_pair("f2", "222"), std::make_pair("f3", "333")});std::vector<std::string> fields;redis.hkeys("key", std::back_inserter(fields));printContainer(fields);// 獲取所有字段的值std::vector<std::string> values;redis.hvals("key", std::back_inserter(values));printContainer(values);
}// 輸出
f1 f2 f3
111 222 333
④ Hmget 和 Hmset
一次性設置多個字段-值對 ( vector<pair>
),并從哈希中獲取多個字段的值。
void test4(Redis& redis){// 清空數據庫redis.flushall();redis.hset("key", {std::make_pair("f1", "111"), std::make_pair("f2", "222"), std::make_pair("f3", "333")});// 再次批量設置更多字段-值對std::vector<std::pair<std::string, std::string>> pairs = {std::make_pair("f4", "444"),std::make_pair("f5", "555"),std::make_pair("f6", "666")};redis.hmset("key", pairs.begin(), pairs.end());// 獲取并打印指定字段的值std::vector<std::string> values;auto it = std::back_inserter(values);redis.hmget("key", {"f1", "f2", "f3"}, it);printContainer(values);
}// 輸出
111 222 333
5. Set
① Sadd & Smembers & Spop
此示例展示了如何向集合中添加元素以及如何獲取集合中的所有成員,并且如何隨機移除集合中的一個元素,并打印被移除的元素或相應的錯誤信息
void test1(Redis& redis){std::cout << "sadd & spop & smembers" << std::endl;redis.flushall();redis.sadd("myset", "111"); // 插入單個元素, 基于初始化列表// 插入一組元素, 基于迭代器std::vector<std::string> values = {"222", "333", "444"};redis.sadd("myset", values.begin(), values.end());auto num = redis.spop("myset"); // 隨機彈出1個元素std::cout << "spop: " << num.value_or("null") << std::endl;// 獲取集合元素std::set<std::string> res;redis.smembers("myset", std::back_insert_iterator(res));printContainer(res);
}// 輸出
spop: 222
111
333
444
- 注意,保存
smembers
結果時,使用std::set
可能更合適,因為集合中的元素是無序且唯一的。
② Sismember 和 Scard
此示例展示了如何向集合中添加元素以及如何獲取集合中的所有成員。
void test2(Redis& redis){redis.flushall();redis.sadd("key", {"111", "222", "333"});std::cout << "集合元素個數: " << redis.scard("key") << std::endl; std::cout << "是否包含元素 111: " << redis.sismember("key", "111") << std::endl;std::cout << "是否包含元素 444: " << redis.sismember("key", "444") << std::endl;
}// 輸出
集合元素個數: 3
是否包含元素 111: 1
是否包含元素 444: 0
③ Sinter 和 SinterStore
此示例說明了如何找到兩個集合的交集,并將結果存儲在一個新的集合中,同時打印新集合的大小和內容
void test3(Redis& redis){std::cout << "sinter & sinterstore" << std::endl;redis.flushall();redis.sadd("key1", {"111", "222", "333"});redis.sadd("key2", {"111", "222", "444"});// 計算兩個集合的交集std::set<std::string> result;auto it = std::inserter(result, result.end());redis.sinter({"key1", "key2"}, it);// 打印交集結果printContainer(result);long long len = redis.sinterstore("key3", {"key1", "key2"});std::cout << "len: " << len << std::endl;
}// 輸出
111 222
len: 2
④ srem 和 smove
void test4(Redis& redis){redis.flushall();redis.sadd("set1", {"a", "b", "c"});redis.sadd("set2", {"d", "e"});// 移除元素redis.srem("set1", "b");std::cout << "set1 after srem: ";std::vector<std::string> res;redis.smembers("set1", std::back_insert_iterator(res));printContainer(res);// 移動元素redis.smove("set1", "set2", "c");std::cout << "set1 after smove: ";std::vector<std::string> res1;redis.smembers("set1", std::back_insert_iterator(res1));printContainer(res1);std::cout << "set2 after smove: ";std::vector<std::string> res2;redis.smembers("set2", std::back_insert_iterator(res2));printContainer(res2);
}// 輸出
set1 after srem: c a
set1 after smove: a
set2 after smove: c e d
6. Zset
① Zadd 和 Zrange & Zscore
- 向有序集合中添加成員,并通過
zrange
命令以兩種不同的方式獲取成員:只查詢成員或同時查詢成員和分數。 - 獲取指定成員的分數,并輸出結果
void test1(Redis& redis){redis.flushall();redis.zadd("key", {std::make_pair("a", 98), std::make_pair("b", 97)});// 使用容器批量添加成員及其分數std::vector<std::pair<std::string, double>> members = {std::make_pair("c", 95),std::make_pair("d", 93)};redis.zadd("key", members.begin(), members.end());// 獲取所有成員std::vector<std::string> membersResults;redis.zrange("key", 0, -1, std::back_inserter(membersResults));printContainer(membersResults);// 獲取并打印成員 c 的分數auto score = redis.zscore("key", "c");if (score) {std::cout << "score: " << score.value() << std::endl;} else {std::cout << "score 無效" << std::endl;}// 獲取并打印所有成員及其分數std::vector<std::pair<std::string, double>> membersWithScore;redis.zrange("key", 0, -1, std::back_inserter(membersWithScore));printContainerPair(membersWithScore);
}// 輸出
d c b a
score: 95
d: 93
c: 95
b: 97
a: 98
zrange支持兩種主要的風格:
- 只查詢member, 不帶score
- 查詢member同時帶score
關鍵就是看插入迭代器指向的容器的類型:
- 指向的容器只是包含一個string, 就是只查詢member
- 指向的容器包含的是一個pair, 里面有string和 double, 就是查詢member同時帶有score
② Zcard & Zrem
- 計算有序集合中的成員數量,并輸出結果
- 從有序集合中刪除一個成員,并輸出刪除后的集合大小
void test2(Redis& redis){redis.flushall();redis.zadd("key", {std::make_pair("a", 98), std::make_pair("b", 97)});std::cout << "key 的成員數量: " << redis.zcard("key") << std::endl;std::cout << "a 是否存在: " << redis.zscore("key", "a").has_value() << std::endl;redis.zrem("key", "b");std::cout << "刪除 b 后, b 是否存在: " << redis.zscore("key", "b").has_value() << std::endl;
}// 輸出
key 的成員數量: 2
a 是否存在: 1
刪除 b 后, b 是否存在: 0