[Redis][C++客户端]详细讲解
[Redis][C++客户端]详细讲解
·
目录
0.RESP
- RESP:Redis自定义的应用层协议
- 传输层基于TCP,但是和TCP又没有强耦合
- 优点:
- 简单好实现
- 快速进行解析
- 肉眼可读
- 工作流程:请求和响应之间的通信模型是一问一答的形式
- 客户端给服务器发一个请求(Redis 命令),服务器返回一个响应
- 客户端和服务器只需要按照格式,构造出字符串,往
socket
中写入或者从socket
中读取字符串,按照上述格式解析
1.前置依赖 – hiredis
redis-plus-plus
是基于hiredis
实现的,hiredis
是⼀个C语⾔实现的Redis客⼾端- 安装:
apt install libhiredis-dev
2.安装 redis-plus-plus
- C++操作Redis的库有很多,此处使用
redis-plus-plus
,该库功能强大,使用简单, - 安装:
git clone https://github.com/sewenew/redis-plus-plus.git cd redis-plus-plus mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr make make install
- 说明:
redis-plus-plus
该库接口风格非常统一,用起来非常舒服- 当一个函数,参数需要传递多个值的时候,往往都是支持初始化列表或者是一对迭代器的方式,来进行实现
- 当一个函数,返回值需要表示多个数据的时候,也往往会接入插入迭代器,来实现往一个容器中添加元素的效果
- 当某些场景涉及到无效值的时候,往往会搭配
std::optional
来进行使用
3.示例:通用命令
1.Quick Start
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
:main:main.cc g++ -std=c++17 -o $@ $^ -l redis++ -l hiredis -l pthread .PHONY:clean clean: rm main
2.Get && Set
- 此处的
redis.get()
返回的是一个optional
类型optional
类型在C++14中,正式纳入标准库- 因为
redis.get()
会返回nil
无效值,用std::string
不方便表现nil
,而std::string*
虽然可以用nullptr
表示无效,但是返回指针又设计内存管理问题 - 所以综上,此处使用
optional
表示"非法值"/“无效值”
void TestGetAndSet(sw::redis::Redis& redis) { // 清空数据库,避免之前残留的数据干扰 redis.flushall(); // 使用set设置key redis.set("key1", "233"); redis.set("key2", "666"); redis.set("key3", "888"); // 使用get获取到key对应的value auto value1 = redis.get("key1"); // optional 可以隐式转成 bool 类型, 可以直接在 if 中判定. 如果是无效元素, 就是返回 false if(value1) { cout << "value1=" << value1.value() << endl; // sw::redis::OptionalString没有重载<< } auto value2 = redis.get("key2"); if(value2) { cout << "value2=" << value2.value() << endl; } auto value3 = redis.get("key3"); if (value2) { cout << "value3=" << value3.value() << endl; } auto value4 = redis.get("key4"); if (value4) { // 如果不加判断,则会在此处抛出异常 std::bad_optional_acess cout << "value4=" << value4.value() << endl; } }
3.Exists
void TestExists(sw::redis::Redis &redis)
{
redis.flushall();
redis.set("key1", "111");
redis.set("key3", "111");
auto ret = redis.exists("key1");
cout << ret << endl;
ret = redis.exists("key2");
cout << ret << endl;
// exists()的重载形式,可以用一个初始化列表传参
ret = redis.exists({"key1", "key2", "key3"});
cout << ret << endl;
}
4.Keys
void TestKeys(sw::redis::Redis &redis)
{
redis.flushall();
redis.set("key1", "111");
redis.set("key2", "222");
redis.set("key3", "333");
redis.set("key4", "444");
redis.set("key5", "555");
redis.set("key6", "666");
// keys 的第二个参数, 是一个 "插入迭代器". 需要先准备好一个保存结果的容器.
// 接下来再创建一个插入迭代器指向容器的位置. 就可以把 keys 获取到的结果依次通过刚才的插入迭代器插入到容器的指定位置中了.
// 插入迭代器 -> 位置 + 动作
vector<string> ret;
auto it = std::back_inserter(ret);
redis.keys("*", it);
PrintContainer(ret);
}
5.Expire && TTL
void TestExpireAndTTL(sw::redis::Redis &redis)
{
redis.flushall();
redis.set("key", "111");
// 10s -> std::chrono::seconds(10);
redis.expire("key", 10s); // 此处10s为字面值,类似于2.33f
std::this_thread::sleep_for(3s);
auto time = redis.ttl("key");
cout << time << endl;
}
6.Type
void TestType(sw::redis::Redis& redis)
{
redis.flushall();
redis.set("key1", "111");
string result = redis.type("key");
cout << "key1: " << result << endl;
redis.lpush("key2", "111");
result = redis.type("key2");
cout << "key2: " << result << endl;
redis.hset("key3", "aaa", "111");
result = redis.type("key3");
cout << "key3: " << result << endl;
redis.sadd("key4", "aaa");
result = redis.type("key4");
cout << "key4: " << result << endl;
redis.zadd("key5", "吕布", 99);
result = redis.type("key5");
cout << "key5: " << result << endl;
}
4.示例:String
1.Set with expire
void TestSetWithExpire(Redis& redis)
{
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;
}
2.Set NX / XX
void TestSetNXAndXX(Redis &redis)
{
redis.flushall();
redis.set("key", "111");
// set 的重载版本中, 没有单独提供 NX 和 XX 的版本, 必须搭配过期时间的版本来使用
// 过期时间填0s则为永不过期
redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);
auto value = redis.get("key");
if (value)
{
std::cout << "value: " << value.value() << std::endl;
}
}
3.Mset
void TestMset(Redis &redis)
{
redis.flushall();
// 第一种写法, 使用初始化列表描述多个键值对
// redis.mset({ std::make_pair("key1", "111"), std::make_pair("key2", "222"), std::make_pair("key3", "333") });
// 第二种写法, 可以把多个键值对提前组织到容器中. 以迭代器的形式告诉 mset
vector<pair<string, string>> keys = {
{"key1", "111"},
{"key2", "222"},
{"key3", "333"}
};
redis.mset(keys.begin(), keys.end());
auto value = redis.get("key1");
if (value)
{
std::cout << "value: " << value.value() << std::endl;
}
value = redis.get("key2");
if (value)
{
std::cout << "value: " << value.value() << std::endl;
}
value = redis.get("key3");
if (value)
{
std::cout << "value: " << value.value() << std::endl;
}
}
4.Mget
void TestMget(Redis &redis)
{
redis.flushall();
vector<std::pair<string, string>> keys = {
{"key1", "111"},
{"key2", "222"},
{"key3", "333"}};
redis.mset(keys.begin(), keys.end());
vector<sw::redis::OptionalString> result;
auto it = std::back_inserter(result);
redis.mget({"key1", "key2", "key3", "key4"}, it);
PrintContainerOptional(result);
}
5.Getrange && Setrange
void TestRange(Redis &redis)
{
redis.flushall();
redis.set("key", "DieKSnowK");
string result = redis.getrange("key", 2, 5);
cout << "result: " << result << endl;
redis.setrange("key", 2, "xyz");
auto value = redis.get("key");
cout << "value: " << value.value() << endl;
}
5.示例:list
1.Push && Range
void TestPushAndRange(Redis &redis)
{
redis.flushall();
// 插入单个元素
redis.lpush("key", "111");
// 插入一组元素, 基于初始化列表
redis.lpush("key", {"222", "333", "444"});
// 插入一组元素, 基于迭代器
vector<string> values = {"555", "666", "777"};
redis.lpush("key", values.begin(), values.end());
// lrange 获取到列表中的元素
vector<string> results;
auto it = std::back_inserter(results);
redis.lrange("key", 0, -1, it);
PrintContainer(results);
}
2.Pop
void TestPop(Redis &redis)
{
redis.flushall();
// 构造一个 list
redis.rpush("key", {"1", "2", "3", "4"});
auto 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;
}
}
3.blpop
- TIPS:对于
std::optional
类型来说,可以直接使用->
访问optional
内部包含的元素的成员void TestBlpop(Redis &redis) { redis.flushall(); auto result = redis.blpop({"key", "key2", "key3"}, 10s); // 此处可以考虑在其他redis-cli插入数据以便进一步观察效果 if (result) { std::cout << "key:" << result->first << std::endl; std::cout << "elem:" << result->second << std::endl; } else { std::cout << "result 无效!" << std::endl; } }
4.llen
void TestLlen(Redis &redis)
{
redis.flushall();
redis.lpush("key", {"111", "222", "333", "444"});
long long len = redis.llen("key");
cout << "len: " << len << endl;
}
6.示例:set
1.Sadd && Smembers
- 此处要注意保存结果的方式和之前的区别
void TestSaddAndSmembers(Redis& redis) { redis.flushall(); // 一次添加一个元素 redis.sadd("key", "111"); // 一次添加多个元素(使用初始化列表) redis.sadd("key", {"222", "333", "444"}); // 一次添加多个元素(使用迭代器) set<string> elems = {"555", "666", "777"}; redis.sadd("key", elems.begin(), elems.end()); // 获取到上述元素 // vector<string> result; // auto it = std::back_inserter(result); // 此处用来保存 smembers 的结果, 使用 set 可能更合适. set<string> result; // 由于此处 set 里的元素顺序是固定的. 指定一个 result.end() 或者 result.begin() 或者其他位置的迭代器, 都无所谓 auto it = std::inserter(result, result.end()); redis.smembers("key", it); PrintContainer(result); }
2.Sismerber
void TestSismember(Redis& redis)
{
redis.flushall();
redis.sadd("key", {"111", "222", "333", "444"});
bool result = redis.sismember("key", "555");
cout << "result: " << result << endl;
}
3.Scard
void TestScard(Redis &redis)
{
redis.flushall();
redis.sadd("key", {"111", "222", "333"});
long long result = redis.scard("key");
cout << "result: " << result << endl;
}
4.Spop
void TestSpop(Redis& redis)
{
redis.flushall();
redis.sadd("key", {"111", "222", "333", "444"});
auto result = redis.spop("key");
if (result)
{
std::cout << "result: " << result.value() << std::endl;
}
else
{
std::cout << "result 无效!" << std::endl;
}
}
5.Sinter
void TestSinter(Redis &redis)
{
redis.flushall();
redis.sadd("key1", {"111", "222", "333"});
redis.sadd("key2", {"111", "222", "444"});
set<string> result;
auto it = std::inserter(result, result.end());
redis.sinter({"key1", "key2"}, it);
PrintContainer(result);
}
6.Sinterstore
void TestSinterstore(Redis &redis)
{
redis.flushall();
redis.sadd("key1", {"111", "222", "333"});
redis.sadd("key2", {"111", "222", "444"});
long long len = redis.sinterstore("key3", {"key1", "key2"});
cout << "len: " << len << endl;
set<string> result;
auto it = std::inserter(result, result.end());
redis.smembers("key3", it);
PrintContainer(result);
}
7.示例:hash
1.Hset && Hget
void TestHsetAndHget(Redis &redis)
{
redis.flushall();
redis.hset("key", "f1", "111");
redis.hset("key", std::make_pair("f2", "222"));
// hset 能够一次性插入多个 field-value 对
redis.hset("key", {std::make_pair("f3", "333"),
std::make_pair("f4", "444")});
vector<std::pair<string, string>> fields = {
std::make_pair("f5", "555"),
std::make_pair("f6", "666")};
redis.hset("key", fields.begin(), fields.end());
auto result = redis.hget("key", "f3");
if (result)
{
std::cout << "result: " << result.value() << std::endl;
}
else
{
std::cout << "result 无效!" << std::endl;
}
}
2.Hexists
void TestHexists(Redis &redis)
{
redis.flushall();
redis.hset("key", "f1", "111");
redis.hset("key", "f2", "222");
redis.hset("key", "f3", "333");
bool result = redis.hexists("key", "f4");
std::cout << "result: " << result << std::endl;
}
3.Hdel
void TestHdel(Redis &redis)
{
redis.flushall();
redis.hset("key", "f1", "111");
redis.hset("key", "f2", "222");
redis.hset("key", "f3", "333");
long long result = redis.hdel("key", "f1");
std::cout << "result: " << result << std::endl;
result = redis.hdel("key", {"f2", "f3"});
std::cout << "result: " << result << std::endl;
long long len = redis.hlen("key");
std::cout << "len: " << len << std::endl;
}
4.Hkeys && Hvals
void TestHkeysAndHvals(Redis &redis)
{
redis.flushall();
redis.hset("key", "f1", "111");
redis.hset("key", "f2", "222");
redis.hset("key", "f3", "333");
vector<string> fields;
auto itFields = std::back_inserter(fields);
redis.hkeys("key", itFields);
PrintContainer(fields);
vector<string> values;
auto itValues = std::back_inserter(values);
redis.hvals("key", itValues);
PrintContainer(values);
}
5.Hmget && Hmset
void TestHmgetAndHmset(Redis &redis)
{
redis.flushall();
redis.hmset("key", {std::make_pair("f1", "111"),
std::make_pair("f2", "222"),
std::make_pair("f3", "333")});
vector<std::pair<string, string>> pairs = {
std::make_pair("f4", "444"),
std::make_pair("f5", "555"),
std::make_pair("f6", "666")};
redis.hmset("key", pairs.begin(), pairs.end());
vector<string> values;
auto it = std::back_inserter(values);
redis.hmget("key", {"f1", "f2", "f3"}, it);
PrintContainer(values);
}
8.示例:zset
1.Zadd && Zrange
zrange
支持两种主要的风格:- 只查询
member
, 不带score
- 查询
member
同时带score
- 只查询
- 关键就是看插入迭代器指向的容器的类型:
- 指向的容器只是包含一个
string
, 就是只查询member
- 指向的容器包含的是一个
pair
, 里面有string
和double
, 就是查询member
同时带有score
void TestZaddAndZrange(Redis &redis) { redis.flushall(); redis.zadd("key", "吕布", 99); redis.zadd("key", {std::make_pair("赵云", 98), std::make_pair("典韦", 97)}); vector<std::pair<string, double>> members = { std::make_pair("关羽", 95), std::make_pair("张飞", 93)}; redis.zadd("key", members.begin(), members.end()); vector<string> memberResults; auto it = std::back_inserter(memberResults); redis.zrange("key", 0, -1, it); PrintContainer(memberResults); vector<std::pair<string, double>> membersWithScore; auto it2 = std::back_inserter(membersWithScore); redis.zrange("key", 0, -1, it2); PrintContainerPair(membersWithScore); }
- 指向的容器只是包含一个
2.Zcard
void TestZcard(Redis &redis)
{
redis.flushall();
redis.zadd("key", "zhangsan", 90);
redis.zadd("key", "lisi", 91);
redis.zadd("key", "wangwu", 92);
redis.zadd("key", "zhaoliu", 93);
long long result = redis.zcard("key");
std::cout << "result: " << result << std::endl;
}
3.Zrem
void TestZrem(Redis &redis)
{
redis.flushall();
redis.zadd("key", "zhangsan", 90);
redis.zadd("key", "lisi", 91);
redis.zadd("key", "wangwu", 92);
redis.zadd("key", "zhaoliu", 93);
redis.zrem("key", "zhangsan");
long long result = redis.zcard("key");
std::cout << "result: " << result << std::endl;
}
4.Zscore
void TestZscore(Redis &redis)
{
redis.flushall();
redis.zadd("key", "zhangsan", 90);
redis.zadd("key", "lisi", 91);
redis.zadd("key", "wangwu", 92);
redis.zadd("key", "zhaoliu", 93);
auto score = redis.zscore("key", "zhangsan");
if (score)
{
std::cout << "score: " << score.value() << std::endl;
}
else
{
std::cout << "score 无效" << std::endl;
}
}
更多推荐
已为社区贡献12条内容
所有评论(0)