一、编写 helloworld
需要先使用 redis-plus-plus 连接一下 Redis 服务器,再使用 ping 命令检测连通性。
1、Makefile
Redis 库最多可以支持到 C++17 版本。(如果是用 Centos,需要注意 gcc/g++ 的版本,看是否支持 C++17。不支持的话,选用 C++11 版本也是可以的)
hello:hello.cc
g++ -std=c++17 -o $@ $^ /usr/local/lib64/libredis++.a /usr/local/lib/libhiredis.a -pthread
.PHONY:clean
clean:
rm hello
编译程序时,需要引入的库文件(由于不同系统中安装好的库所在位置会存在差异,因此要注意⽂件路径是否存在。可以用 find 命令来查找库所在的目录):
- Redis++ 自己的静态库
- hiredis 静态库
- 线程库
也可以选择使用动态库链接:
2、hello.cc
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <sw/redis++/redis++.h>
using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;
int main()
{
// 创建Redis对象时,需要在构造函数中指定redis服务器的地址和端口
sw::redis::Redis redis("tcp://127.0.0.1:6379");
// 调用ping方法,让客户端给服务器发了一个PING,然后服务器就会返回一个PONG,就通过返回值获取到
string result = redis.ping();
std::cout << result << std::endl;
return 0;
}
- #include <> 在系统目录中搜索头文件
- #include "" 在项目目录中搜索头文件
这里需要包含 redis-plus-plus 的头文件:redis++.h
6379 是 Redis 服务器的默认端口:
3、运行程序
二、详细 API
参考 Github:
redis-plus-plus/src/sw/redis++/redis.h at master · sewenew/redis-plus-plus (github.com)
参考 Gitee:
src/sw/redis++/redis.h · peixinchen/redis-plus-plus - Gitee.com
三、使用通用命令
1、get & set
(1)get
此处的 Optional 可以表示:“非法值” / “无效值”(nil)
如果直接使用 std::string 来表示,则不方便来表现 nil(无效值);如果使用 std::string* 来标识,虽然可以用 nullptr 表示无效,但是返回指针又会涉及到内存问题。
boost 很早就引入了 optional 类型,在 C++14 版本中就正式是纳入标准库了。因为用户可能选用的是 C++11 版本,所以作者自己封装了一个 OptionalString。
使用 get 获取到 key 对应的 value:
也可以不选择 if 来做判断,而是选用 try-catch。但在实际上,C++ 中不太会经常使用 try-catch。原因如下:
- try-catch 会存在额外的运行时开销,对于性能追求极致的 C++ 来说不太合适。
- try-catch 相比于其他语言(Java、Python)来说有点弱。
上面的 value1 ~ value4 都是 optional 类型,sw::redis::OptionalString 不支持 << 运算符的重载。这里也没有必要给 optional 搞一个 << 的重载,只需要把 optional 里面包含的元素取出来即可(optional 可以当作只包含一个元素的容器)。
(2)set
这里用到了 StringView 这样的类型(是在 sw::redis 命名空间里的).
- std::string 是可修改的,既能读,也能写。
- StringView 是只读的(不能修改),针对只读操作,做很多的优化工作,效率比 std::string 更高。(在 C++17 标准库中也提供了一个 std::string_view)
StringView 中的各种操作和 string 类似,只不过是包含了一些只读方法。
(3)运行程序
A. Makefile
B. generic.cc
C. 运行结果
2、exists & del
(1)exists
(2)del
进行单元测试时,清除数据操作是放在开始,还是结束呢?
放在开始更好一些。因为如果放在结束的位置,一旦执行的程序中间出现异常,就可能导致清理代码没有执行到。另一方面,放在开始的位置,当执行完一个操作之后,此时 Redis 里仍然是有数据的,也方便我们手工来检查数据的内容。
3、keys
通常,一个迭代器主要是表示一个 “位置”。
STL 中的 5 种迭代器类型:
- 输入迭代器('*',"++" 对它来说不会产生影响,主要操作是 '='(把另一个迭代器赋值给这个插入迭代器),例如:it:插入迭代器;it2:普通迭代器。此时 it=it2 相当于是获取到 it2 指向的元素,然后把元素按照 it 当前插入迭代器的位置和动作来执行插入操作,比如 it 是一个 back_insert_iterator,就是把 it2 指向的元素插入到 it 指向的容器末尾,相当于调用了一次 push_back)
- 输出迭代器(上面的插入迭代器(“位置” + “动作”)本质上也是一种输出迭代器,标准库中主要有三个:font_insert_iterator(区间开头,往前面插入)、back_insert_iterator(区间末尾,往后面插入)、insert_iterator(区间任意位置,往该位置之前插入),一般不会直接构造出这几个对象(构造函数写起来比较麻烦),一般会使用一些辅助函数来进行构造)
- 前向迭代器
- 双向迭代器
- 随机访问迭代器(最大的优势:以 O(1) 快速定位到指定位置,主要也是由于内存支持随机访问)
这里直接使用容器作为参数,keys 内部直接操作容器进行插入不就行了吗,为什么还要通过迭代器绕一个大圈子呢?
解耦合。代码要追求高内聚、低耦合。
keys 这个操作看不到容器,容器也看不到 keys 操作。此时,它们双方都使用迭代器这样一个中间媒介来去进行交互。此时,keys 的迭代器可以指向 vector,也可以指向其它的容器。换句话说,给你一个 vector 容器,它可以搭配 keys 使用,也可以搭配其他函数使用。也就是说,任何一个容器可以和任意函数搭配使用。
4、expire & ttl
- Linux:sleep 是以 s 做单位的
- Windows:Sleep 是以 ms 做单位的
系统函数是和系统相关的,同样的功能在不同的系统上可能是完全不同的函数。更好的选择是使用标准库的函数,C++ 提供了 thread -> sleep_for。
5、type
四、string
1、get & set
(1)相关代码
(2)运行程序
A. Makefile
B. 运行程序
a. test1
b. test2
c. test3
2、mget & mset
(1)mget
(2)mset
3、getrange & setrange
4、incr & decr
(1)incr
(2)decr
(3)运行程序
- incr 和 decr 得到的都是 long long 类型。
- get 得到的是 OptionalString 类型(需要手动转换成数字)
五、list
1、lpush & rpush & lrange
(1)lpush & lrange
redis-plus-plus 这个库的接口风格设计非常统一。当一个函数参数需要传递多个值的时候,往往都是支持初始化列表或者是一对迭代器的方式,来进行实现的。当一个函数的返回值需要表示多个数据的时候,也往往会借助插入迭代器,来实现往一个容器中添加元素的效果。当某些场景涉及到无效值时,往往会搭配 std::optional 来进行使用。
lpush 是头插,所以每次新的元素都是插入到 list 开头,越后来的元素就越是排在前面。
(2)rpush & lrange
2、lpop & rpop
3、blpop
optional 里面包裹着一个 pair,pair 里面是 string。
blpop 要返回的内容是两个部分:
- 这个元素从属于哪个 list(blpop 可以同时监听 list)。
- 当前被删除的元素。
blpop 可以设定超时时间。如果在指定时间内,还没有其它客户端往指定的 list 中插入元素,此时 blpop 就直接返回了,此时返回的就是一个无效值。
对于 std::optional 类型来说,可以直接使用 -> 来访问 optional 内部包含的元素的成员变量。
4、llen
六、set
1、sadd & smembers
back_inserter 是把末尾位置和 push_back 两个操作绑定到一起了,但是 set 里没有 push_back,但是 set 有 insert 方法,和 insert 对应的是 insert_iterator。
2、sismember
3、scard
4、spop
5、sinter
6、sinterstore
七、hash
1、hset & hget
(1)hset
hset 也能够一次性插入多个 field-value。
(2)hget
(3)运行程序
2、hexists
3、hdel & hlen
4、hkeys & hvals
(1)hkeys
(2)hvals
(3)运行程序
5、hmset & hmget
(1)hmset
(2)hmget
(3)运行程序
八、zset
1、zadd & zrange
(1)zadd
(2)zrange
zrange 支持两种主要的风格:
- 只查询 member,不带 score
- 查询 member 同时带 score
关键就是看插入迭代器指向的容器的类型。指向的容器只是包含一个 string,就是只查询 member。指向的容器包含的是一个 pair,里面有 string 和 double,就是查询 member 同时带有 score。