【Redis#11】Redis 在 C++ 客戶端下的安裝使用流程(一條龍服務)

一、安裝使用 --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 密碼

二、常用類型接口學習

  1. 接口的相似性:sw/redis++ 的接口設計與 Redis 原生命令非常相似,這使得熟悉 Redis 命令的開發者可以快速上手。

  2. 參數傳遞方式

    • 當需要傳入多個參數時,有兩種方法:
    • 使用容器(如 std::vector 或 std::set)保存參數,并傳遞容器的迭代器。
    • 直接使用初始化列表 {} 傳遞參數。
  3. 鍵值對傳遞:如果需要傳遞鍵值對,可以使用 std::pair 來表示,例如 std::pair<std::string, std::string>。

  4. 常用 C++ 類型

    • OptionalString:用于接收可能為 nil 的返回值,可以判斷是否為空。
    • StringView:用于傳遞只讀字符串,但通常直接使用 std::string 即可。
    • long long:用于接收整數類型的返回值。
    • double:用于 ZSET 中的分數(score)類型。
  5. 輸出多個變量:如果函數需要輸出多個變量,應該 傳遞一個插入迭代器(如 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支持兩種主要的風格:

  1. 只查詢member, 不帶score
  2. 查詢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

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/96994.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/96994.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/96994.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

kibana+elasticsearch console查詢示例

kibana console查詢入口如下 http://localhost:5601/app/dev_tools#/console/shell 1 整體查詢 獲取index為newbook的所有數據 GET newbook/_search 2 通用查詢 獲取index為newbook的數據中&#xff0c;bookname包含“西游”的所有數據。 GET newbook/_search { "query&q…

軟考系統架構設計師之軟件風格篇

一、軟件架構風格-數據流風格 數據-》第1步處理-》數據-》第2步處理-》數據-》第N步處理 【分步處理】 優點&#xff1a; 1、松耦合【高內聚-低耦合】 2、良好的重用性/可維護性; 3、可擴展性【標準接口適配】 4、良好的隱蔽性; 5、支持并行。 缺點 1、交互性較差; 2、復雜性較…

初始QML

由于項目原因&#xff0c;最近要進行qml相關開發&#xff0c;我之前也沒有搞過qml&#xff0c;因此開一個qml系列的專欄&#xff0c;記錄自己關于qml的相關學習新建第一個qml工程按如下圖所示方法新建一個最簡單的qml工程&#xff1a;編譯運行可以看到是一個標題為“hello word…

Coze源碼分析-資源庫-創建知識庫-基礎設施/存儲/安全

6. 基礎設施層 基礎設施層為知識庫創建功能提供底層技術支撐&#xff0c;包括數據存儲、緩存、消息隊列、文檔處理、向量化等核心服務。 6.1 數據存儲服務 6.1.1 MySQL數據庫 文件位置: backend/infra/rdb/mysql.go // MySQLConfig MySQL配置 type MySQLConfig struct {Host …

【iOS】設計模式復習

目錄 觀察者模式 通知機制 基本使用 注冊觀察者 創建一個通知 發送通知 通知與多線程 使用異步發送通知 NSNotificationQueue通知隊列 在子線程中運行觀察者函數 實現原理 named表 nameless表 wildcard表 添加觀察者 發送通知 移除通知? KVO機制 基本使用 …

RK3568 NPU :RKNN-ToolKit2環境搭建

1. 安裝Miniconda3 下載 Linux 64 位 Miniconda 最新版安裝腳本 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh安裝 Miniconda bash Miniconda3-latest-Linux-x86_64.sh -u安裝完成后會自動設置環境變量。打開新的終端&#xff0c;發現用戶名前…

Ubuntu 24.04 Server 版系統安裝及配置

Ubuntu 24.04 Server 版安裝及配置 文章目錄Ubuntu 24.04 Server 版安裝及配置一、獲取安裝文件二、虛擬機配置三、安裝界面四、配置網絡五、擴容根分區 一、獲取安裝文件二、虛擬機配置三、安裝界面 選擇English&#xff08;US&#xff09;問是否升級內核配置鍵盤手動配置ipv4…

Java 事務失效場景全解析

在 Java 開發中&#xff0c;事務管理是保證數據一致性的核心機制&#xff0c;尤其是在 Spring 框架中&#xff0c;Transactional注解的使用極大簡化了事務配置。然而&#xff0c;在實際開發中&#xff0c;事務常常會因為一些細節問題而失效&#xff0c;導致數據異常。本文將詳細…

【Coze搞錢實戰】14. 抖音直播間自動回復機器人實戰教程:三小時搭建智能客服,互動率提升150%(保姆級無代碼指南)

摘要:抖音直播間高頻問題重復回復、觀眾互動不及時是運營痛點。本文針對新手和進階用戶,提供無代碼的自動回復機器人搭建方案:新手1小時完成基礎配置(Coze+抖音對接),進階用戶通過促銷倒計時、粉絲分層、熱點借勢三大策略提升互動率150%。方案基于某女裝直播間實測數據(…

云計算核心知識梳理

云計算作為新一代信息技術的核心,其體系涵蓋特點、定義、技術演進及分類等多個維度,以下是對相關知識的系統整合與解讀。 一、云計算的核心特點 / 優勢 云計算的優勢圍繞資源利用效率、服務靈活性和管理便捷性展開,具體可拆解為五大核心特性: 按需自助服務:用戶無需人工干…

安卓13_ROM修改定制化-----安卓 13 系統 ROM 定制化與低版本系統的核心區別

安卓系統憑借其全球領先的市場占有率,開放特性為廠商和開發者提供了深度定制的空間,形成了豐富的ROM生態圈。從最初的安卓1.0到最新的安卓15,系統在功能、性能和安全方面不斷迭代升級,同時也為ROM定制帶來了新的機遇與挑戰。特別是從安卓11開始,谷歌對系統架構和安全機制進…

【Java后端】Spring Boot 2.7.x 和 Swagger 3.0.x (springfox 3.x) 的兼容性問題

springfox 在 Spring Boot 2.6 開始就有很多兼容性 bug&#xff08;主要是 Spring MVC PathPatternParser 的引入&#xff09;&#xff0c;導致在 Spring Boot 2.6/2.7 里經常出現 無法啟動 / 無法訪問 swagger-ui.html 的情況。&#x1f50e; 問題原因Spring Boot 2.6 開始默認…

Vue3+ts使用oidc-client-ts

配置 OIDC 客戶端 在項目中創建 authOptions 對象&#xff0c;定義 OIDC 認證所需的配置項&#xff1a; export const authOptions {authority: https://xxxxxxxxx/UserCenter, // 認證服務器 URLclient_id: xxxx, // 客戶端 IDredirect_uri: http://localhost:3000/callbac…

從 “數據中轉站“ 到 “邊緣智能中樞“:區域網關的技術突圍與開發范式重構

在物聯網架構中,區域網關長期被視為 "邊緣與云端的橋梁"—— 負責協議轉換、數據轉發、設備接入等基礎功能。但隨著邊緣計算興起與 AI 模型輕量化,區域網關正經歷從 "被動轉發" 到 "主動決策" 的范式躍遷。 本文將從開發視角拆解區域網關的三大…

Django全棧班v1.04 Python基礎語法 20250913 早上

print 函數基本用法 print函數會自加換行符&#xff0c;一個print&#xff0c;會打印一行輸出。 print("第一行") print("第二行") print("第三行")輸出結果&#xff1a;print 輸出多個值 一個print可以同時輸出多個值&#xff0c;這多個值會在一…

面試鴨Java八股之Kafka

Kafka是什么&#xff1f;它的主要應用場景有哪些? Kafka是一種分布式流事件處理平臺&#xff0c;最初由 LinkedIn 開發&#xff0c;現在是 Apache 基金會的一部分。它的核心功能主要包括消息隊列、流處理和數據集成。Kafka以高吞吐量、低延遲、可擴展和高容錯性著稱。 Kafka…

ARM32平臺Bus Error深度排查:從調用棧到硬件原理的完整拆解

ARM32平臺Bus Error深度排查&#xff1a;從調用棧到硬件原理的完整拆解 在嵌入式開發中&#xff0c;Bus Error&#xff08;信號7&#xff09;是個容易讓人頭疼的問題——它不像SIGSEGV&#xff08;段錯誤&#xff09;那樣直觀&#xff0c;常與硬件內存布局、指針破壞等底層問題…

適合工業用的筆記本電腦

在工業領域&#xff0c;生產環境往往復雜多變&#xff0c;從高溫、高濕的車間&#xff0c;到布滿粉塵的礦山&#xff0c;再到震動頻繁的施工現場&#xff0c;普通的筆記本電腦很難在這樣的環境中穩定運行&#xff0c;而工業用筆記本電腦的誕生&#xff0c;完美地解決了這一難題…

在LINUX中常見的文件系統類型

常見文件系統類型對比表文件系統類型作用和特點主要使用場景優缺點ext4Linux標準文件系統&#xff0c;日志式&#xff0c;支持大文件和分區Linux根文件系統、/home、/var等主要分區優點&#xff1a;穩定成熟&#xff0c;支持大文件(16TB)&#xff0c;日志功能保證數據安全&…

Unity核心概念⑥:Time

一、Time的主要用途主要用于游戲中參與位移、記時、時間暫停等。二、時間縮放比例1.時間停止&#xff1a;Time.timeScale 0;2.回復正常&#xff1a;Time.timeScale 1;3.二倍速&#xff1a;Time.timeScale 2;三、幀間隔時間幀間隔時間是指最近的一幀用了多少時間。1.用途主要…