平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“详细解析Redis终端操作和Redis-plus-plus接口采用教程”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
redis简介
- 从实现思路看,相比如mysql,redis也能够当作数据库来采用,而且更快,因为是内存中的数据库。但是和mysql相比,具有存储空间小
在这个场景下,的问题,因为内存空间远小于硬盘空间,同时也是因为这个问题,redis的功能相比mysql要少,比如不存在多表查询等等操作 - “二八原则”:20%的数据满足80%的访问需求,典型的方案:redis结合mysql采用,采用redis存储经常采用的热点数据,剩下的数据存储在mysql中,只在需的时候去取。其中涉及系统中复杂度的增加、redis和mysql数据的同步问题等
- redis最初是想作为一个消息队列(实现分布式系统中的生产者、消费者模型)
- 实际处理时,msyql数据库的主从分离与读写分离:读的需求是要远大于写的需求的,所以只有主库接受写的请求,同时将发生改变的内容同步到从库,从库不负责处理写的请求,负责读的请求,上层也能够引入负载均衡的策略对读节点进行选择
redis存放、拿到键值的操作
1. 存放键值对
在这个场景下,(1) redis中,key都是字符串,value能够是各种类型。存放键值对,采用set keyname keyvalue;redis不区分大小写,也不需
注意单双引号的问题,写不写都能够。
(2) set keyname value [ex seconds | px milliseconds] [nx|xx],ex指定超时时间以秒为单位,px以毫秒为单位;nx表示有设置keyname则不插入,xx表示只在keyname存在时修改value
(3) mset keyname value [keyname value] 设置多组键值对
(4) setnx setex psetex即设置时就加上选项,setnx表示只在不存在时设置,setex表示设置过期时间,秒为单位,psetex也是设置过期时间,但是以毫秒为单位
2. 拿到键值对
在这个场景下,(1) redis中,拿到键值对采用get,借助get keyname的方式拿到
(2) mget,拿到多组键值对
拿到键值对时的匹配原则
?表示任意一个字符,如key可由ke?检索出来*表示任意多个字符,采用其实和Linux检索文件名时采用一样[abcdefg]表示从中括号中的选项中匹配任意一个[^e]表示不匹配e[a-c]表示范围匹配
落到代码里,但是真实开发场景下,几乎不会采用(禁止)*的方式,因为会检索整个redis中的所有key,而redis又是单线程的,
理解这一步时,所以可能会导致该线程阻塞在检索的过程中,导致大量请求从redis的缓存数据中取不出结果,都去向数据库发出请求,
导致数据库压力过大挂掉
3. 查看键值对是否存在
结合项目来看,查看一个键值对(多个)是否存在,采用exists keyname1 keyname2 ...
从实现思路看,关于同时兼容多个keyname的问题:redis是客户端、服务器形式的应用,所以对于服务器的请求操作都要借助网络进行,这意味着经由网络传输、各个层级之间协议的封装与解包,都是较大的时间消耗,所以redis兼容一次请求携带多个key值去操作,以节省传输的时间(网络的速度一般网卡和硬盘速度对比还是网络慢很多的
4. 删除键值对
采用del删除键值对,del keyname1 keyname2
5. 设置过期时间
- 秒为单位,
expire keyname seconds - 毫秒为单位,
pexpire keyname microseconds
6. 查看存活时间
ttl(time to live) + keyname,查看剩余存活时间,如果没有设置过期时间,那么就得到-1;如果查无此key,
就得到-2
Redis怎样删除已经过期的key呢?
- 落到代码里,定期删除。定期查看一部分key是否过期,这个过程很快(因为redis是单线程的,耗费时间过长就会导致类似于前面说的*通配符采用的问题
- 从实现思路看,惰性删除,当一个key过期了,也不进行删除,而是在采用设个key的时候,发现这个key过期了,随后再删除
- 从实现思路看,Redis其实同时没有采用定时器的方式去删除过期的key,有些猜测是早期的redis是单线程的,便奠定了单线程的基调,后面也维持了这种风格,但是定时器的实现方案,也是值得了解的,有优先级队列和时间轮两种方式
优先级队列
- 结合项目来看,将有过期时间的任务和过期时间放在一个根据过期时间建立的小根堆当中,根据堆顶元素的过期时间,去让
理解这一步时,线程休眠一段时间,随后唤醒线程节省cpu资源;如果期间有新的事件进入队列,那么就再次唤醒线程检查是否需
处理堆顶事件
时间轮
实际处理时,首先搭建一个环形队列,其中每个节点都指向一个链表的头节点,从头节点往后的每个节点都代表过期时间处在
实际处理时,某个范围的事件,让检测线程每个一段事件往后走一个检查该格中是否有需处理的事件(结合周期和环形队列
从实现思路看,循环的概念,不难得出一个链表中可能有多种时间),但是这种方式对于时间粒度和环形队列的长度的设计
都有很高的要求
7. 查看类型
采用type + keyname查看key对应的value的类型
redis数据类型与实现方法
实际处理时,很多类型,redis承诺对外是某种类型,但是具体实现的时候可能采用了其他的方式
1. string
(1) raw,就是字符串的形式存储
(2) int,如果字符串中存储的是数字的话,就直接作为数字存储
(3) embstr,对于短字符串做出优化的存储方式,也负责存储小数,这也意味着性能的损失,因为整数借助int的方式存储直接就能够
比较,但是借助embstr的话就要转化为数字之后再进行比较
2. hash
(1) hashtable,以redis自己的方式做的哈希表
(2) ziplist,压缩表,当value的类型是哈希表但是其中的元素又很少时,就能够采用ziplist来遍历(因为元素少所以时间差异不大)压缩表借助内部的优化,将数据按照更紧凑的方式进行表示,当数据增多时,会导致效率下降
3. list
(1) 以前的版本采用的是linkedlist + ziplist实现,redis3.2以后,采取quicklist的实现方法,类似于std::deque
4. set
(1) hashtable (2) intset整数集合,当集合中数据较少而且只存储整数的时候,就是用这种结构
5. zset
(1) skiplist,跳表,类似于链表,但是每个节点有多个指针域,使之能够实现logN的查询效率
(2) ziplist,元素个数比较少或者元素大小比较小
查看具体存储方式
object encoding keyname,查看某个value具体的存储方式
redis的单线程工作模型
- 落到代码里,redis的客户端发送请求是同时发的,但是redis服务器的核心工作逻辑是单线程的,这样请求也就再redis服务器上串行执行,
没有并发的问题 - 理解这一步时,所以说一方面,如果一个操作阻塞过久,就可能导致redis服务端挂掉;另一方面,也不是所有业务场景都适合单线程,因为
从实现思路看,redis需处理的业务基本上都是短平快的,对于cpu同时行的要求很低,也就能够采用单线程的方式处理核心业务
为什么说redis快,效率高呢?
- 理解这一步时,首先redis效率高是和mysql这种关系型数据库相比,因为后者是对磁盘的操作,前者是对内存的操作,硬件上的操作速度
就差了几个数量级,因此redis自然更快 - 理解这一步时,redis提供的功能相对要少一些,比如mysql插入的时候如果有约束的话还要射击先查询的过程,查询的时候还有多表联查
等等操作,在功能和业务上的区别,也是造成差异的原因
string 类型
1. incr和incrby
(1) incr,表示给相应value + 1,只有实际类型是int的才能够
(2) incrby,表示给相应value + n,n必须是整数,能够是负数
(3) 如果key不存在,就从零开始处理
2. decr和decrby
从实现思路看,(1) 除了进行的是减操作以外,其他的都是和incr与incrby一样的
3. incrbyfloat
能够进行小数和整数的操作,如果需减操作,采用负数即可
4. append
(1) 对字符串进行追加操作,并得到追加之后的长度
实际处理时,(2) 对于redis来说,不会对我们的数据进行字符集上的处理,所以输入中文的话默认打出的就是原编码,所以能够在启动客户端
从实现思路看,的时候加上—raw指令,让客户端尝试解读得到的二进制数据同时展示,就能够完成中文的显示了
5. getrange
(1) getrange keyname start end,表示拿到[start, end]的内容,以字节为单位,这意味着可能会出现中文被解析成乱码的情况
,因为无论是utf8还是gbk都是多字节编码一个中文字符
从实现思路看,(2) 当end是负数时,表示倒数第几个字符,比如-1表示倒数第一个字符
理解这一步时,(3) 根据两个端点的情况,redis会自动截取内容(数字同理),但是如果最后出现start > end时,会导致解析不出内容
6. setrange
(1) setrange key start value,设置从start开始的内容为value,具体设置到哪里取决于value的长度
从实现思路看,(2) 如果设置的key不存在,那么就根据start的值,用空格填充前面的内容;如果start超过了当前内容的长度,也是同理往后
添加用空格补全长度
7. strlen
在这个场景下,(1) 拿到字符串类型的value的长度,单位是字节,对于中文也是具体字节而不是字符的个数
Redis中的hash类型
从实现思路看,redis中的hash,采用field-value表示键值的结构,其中,value的类型只允许是字符串
1. hset、hget、hexists、hdel
(1) hset keyname fieldname value,表示keyname用来查找这个哈希表,fieldname是哈希表的键值。
(2) hget keyname fieldname,拿到keyname的哈希表的fieldname所对应的值
(3) hexists keyname fieldname,判断keyname对应的哈希表是否具有fieldname的键值
(4) hdel keyname fieldname,删除keyname对应的哈希表的fieldname键值
2. hkeys、hvals
落到代码里,hkeys key,拿到key对应的哈希表中的所有field;hvals key,拿到key对应的哈希表中的所有value值。尽量不要采用这些操作,
和keys*有异曲同工之妙
hlen、hsetnx、hincrby、hincrbyfloat
hlen keyname,用来查询哈希表的大小hsetnx,不存在则插入,存在则失败hincrby,哈希表中某个value + n,只适合处理数字类型的valuehincrbyfloat,哈希表中某个值增加,能够是小数
采用哈希存储的场景
- 结合项目来看,当作结构体/对象采用,存储一个具有多个属性的对象,能够节省分开存的key的个数,适合存结构化的数据,而且只需
改一个字段,不需将整个字段读出来再写回去 - 在这个场景下,与数据库相比,采用哈希存储具有稀疏性,不需的字段就不存;但是对于数据库而言,在一个表中,即使某个对象不具有某个值也需用null来占位
列表
- 列表类似于顺序表的采用,实现容器类似于deque,提升插入、删除的效率
- 列表是有序的,意思是元素相同,顺序不一样就是两个列表
lpush lpop rpush rpop
push_front pop_front push_back pop_back
lrem lindex lrange
lrem key count value,表示将key列表中的count个value(有多少删多少)删除同时得到lrange key start stop,表示将key列表中[start, stop]的下标的内容得到(显示查询出结果的时候,前面带的数字不是下标,是结果集的序号),当下标超出的时候会取余调整lindex key index,表示获得key列表中下标为index的元素
lpushx 和 rpushx
- x代表exist,如果列表存在的话插入,否则插入失败
lpop 和 rpop
lpop keyname [count],从左端删除,后面的count在低版本的不兼容rpop keyname [count],从右端删除- 与lpush和rpush搭配就能够当作栈、队列采用
linsert
linsert keyname [before|after] pivot value, pivot是从左到右寻找的一个值(如果没有找到就插入失败)
lrem
lrem keyname count element,count > 0时,从左到右移除count个元素;count == 0,移除列表中所有element相等的元素;count < 0时,从右向左移除count个元素
ltrim 和 lset
ltrim,ltrim keyname start stop截取[start, stop]的区间之后,剩下部分丢弃lset,lset keyname index element将index下标设置为element,其中index超过范围时,会报错
blpop 和 brpop
- 理解这一步时,两者都是阻塞等待,但是在redis中,首先第一点,这两个阻塞是特殊的,同时不会影响redis核心逻辑的工作,第二点就是这个阻塞队列并不会考虑队列满的情况,只会考虑队列为空的阻塞
blpop keyname timeout,0表示一直阻塞等待,以秒为单位等待- 两者能够同时等待多个队列,从左到右第一个有数据出队列时得到
list列表的应用场景
- 时间轴、消息轴等,借助lpush插入最新内容,借助lrange完成分页拿到,借助ltrim完成部分内容的保留
- 消息队列,借助lpush + brpop就能够完成阻塞队列的搭建
set集合
结合项目来看,set与列表相比,是无序的,意思是顺序不重要,同时set中不允许有相同元素,但是列表能够
sadd smember sismember scard
sadd key elements往set中添加元素smembers key,拿到set中的所有元素sismember key member,判断一个value是否是key’s set中的元素scard keyname,拿到集合中的元素个数
spop srandmember
spop keyname,从set中随机拿到一个元素同时且从set中删除srandmember keyname,从set中随机拿到一个元素,但是同时不会删除set中的得到元素
srem smove
srem keyname element,删除指定set中的指定元素smove source destination element,将source set中的element移动给element,如果移动destination中已经存在的元素也能移动成功,但是发生替换
sinter sinternstore 交集
sinter setname1 setname2 ...,求出两者的交集sinterstore destination setname1 setname2 ...,求出两者的交集,放到destination集合中
sunion sunionstore sdiff sdiffstore
分别取求并集和差集,采用方式与求交集相同
set的应用场景
- 在这个场景下,用来存储用户画像,借助一个一个标签(其实就是小字符串)的形式存储在set当中
- 好友的建议,因为set很便于求交集,所以能够更快建议共同好友等
- 落到代码里,UV的统计(对于一个产品的评价,能够用两种方法来统计,一个是PV,另一个是UV;PV是page view,统计出用户每次访问服务器的次数,UV是userview,统计有多少用户访问了服务器,这意味着需大量的去重操作,也是set擅长的)
zset
结合项目来看,zset也是有序的,但是zset的有序是真正的有顺序的,借助在插入时基于score的结构实现。score和member一一对应,类似std::pair
zadd
zadd key [nx|xx] [gt|lt] [ch] [incr] score member [score member ...]
- 结合项目来看,得到值的问题,zadd默认的得到值是新增元素的个数,但是如果设置了ch,就会得到修改元素的个数
- 结合项目来看,gt|lt,greater than | less than,前者是只有当前更新的score大于原来的score时才更新,后者反之
- nx|xx,nx表示只新增不修改,xx表示只修改不新增
- incr采用类似于incrby,不过作用对象是score
zrange
zrange key start end [withscore],展示zset中的全部元素,withscores表示展示相应元素分数zrevrange key start end [withscore],表示逆序展示(start, range)的memberzrangebyscore key start end [withscore],表示根据score从低到高排序展示member,前面两个start、end表示的是下标
zcount
zcount key start end,用来统计某个score区间中member的个数,如果要表示一个数字是开区间的一端的话就在前面加上(,比如(2, 5)表示为(2 (5- zcount兼容采用正负无穷大作为最大值、最小值(-inf, inf);
zcard
zcard key,统计member个数
zpopmax
- 实际处理时,zset在redis中默认排的是升序,zpopmax会删除score最大的member,同时得到删除的member和score
zpopmax key [count]表示连续取count个最大member,时间复杂度O(logN * M);
bzpopmax
bzpopmax key [key...] timeout,阻塞的取最大member,超时时间为timeout(0为一直阻塞),能够等待多个key,当任意一个有得到时,阻塞结束,所以时间复杂度是O(logN);
zpopmin bzpopmin
实际处理时,两者采用和zpopmax、bapopmax相同,区别是操作的是zset中的最小值
zrank zrevrank zscore
zrank key member [withscore],查询member在有序列表中的下标,从前往后算,就和正常数组一样zrevrank key member withscore,查询member在有序数组中从后往前的下标,即最后一个元素下标为0zscore key member,查询member的分数,为了优化效率,redis牺牲空间,换取了该操作O(1)的时间复杂度
zrem zremrangebyrank zremrangebyscore
zrem key member,删除member成员,时间复杂度O(logN);zremrangebyrank key start end,删除下标[start, end]的成员,时间复杂度O(logN + M),因为只需找到一个七点就能够往后删除zremrangebyscore key min max,删除分数[min, max]的成员,兼容采用’('去指定开区间
zincrby
zincrby key increment member,给member的分数增加increment,increment能够是小数,也能够是负数,表示减小分数
zinterstore
zinterstore destination keynum key [key...] weights [weight...] [aggregate sum | min | max]
- 从实现思路看,destination表示存储交集的名称,keynum用来指定一共有多少个key制定了要求交集的zset(类似于http协议报头中的content-length),weights表示下面的内容用来指定权重,能够是整数也能够是小数,其与共同的member的score相乘后得到结果作为新的score,而具体怎么作用有aggregate后的内容决定,默认是sum,新的score由所有的相加形成,min为所有的score的最小值,max为最大值
- 在最坏情况下,时间复杂度为
O(N*K) + O(M*log(M)),其中N为最小集合的元素个数,K为求交集的元素个数,M为最后交集中
元素的个数
zunionstore
zunionstore destination keynum key [key...] weights [weight] [aggregate sum | min | max]
- 求并集,采用方法与zinterstore类似
- 时间复杂度是
O(N) + O(M*log(M)),N是元素总个数,M是同时集中元素的个数
zset的应用场景
- 排行榜系统,微博热搜、游戏天梯排行、成绩排行
- 在这个场景下,加权的排行,能够维护多个zset,按照不同的权重采用zinterstore zunionstore等
其他类型
- stream,阻塞队列,用来让redis作为消息队列采用
- 从实现思路看,geospatial,用来存储坐标(经纬度),适合存储一些点之后,根据用户给定的坐标,按照半径或者矩阵区域等进行查找
- 从实现思路看,hyperloglog,用来估算集合中的元素个数,借助存储元素的特征而不是元素本身的方式,最多采用12kb的空间,就能够完成与真实数据浮动至多0.8%的差异的数据量统计,对于精准度要求不是那么高的UV PV统计就能够采用hyperloglog
- 落到代码里,bitmap,实际上是优化过的set,既能够存储元素,有节省了一定空间(当然没有hyperloglog节省的多),但是只兼容整形的存储
bitfield,位域,能够理解成是一个字节数组,和C语言中的位段类似,能够为几个比特位赋予特殊的含义同时进行读取、存储、计算等操作,相对于hash String,目的仍然是省空间
渐进式遍历
- 不同于直接采用类似于
keys *的方式去查找整个redis中的键,渐进式遍历采取一次查找一小部分的方式来查找到需的内容。这样每一次花费的时间都较少,能够避免redis服务器被阻塞 scan cursor [MATCH match] [COUNT count] [TYPE type],表示从cursor光标开始,寻找count个元素。这里的cursor同时不是下表的意思,而是redis服务器在每次渐进式遍历结束后得到的下次渐进式遍历的开始位置,输入0表示从头开始遍,得到的新位置是0表示此次遍历结束;match表示通配符匹配,采用规则和keys的一样;type表示检索出的结果的类型- 在这个场景下,scan不指明的话,默认count为10;同时检索在redis服务器不会保留任何结果,所以检索到一半不再进行是能够的;渐进式遍历不一定是可靠的,如果出现遍历过程中已经完成遍历的数据又发生修改的情况可能会无法从检索结果中反馈
redis数据库操作
- 从实现思路看,redis中也是有数据库的概念的,但是没有mysql中那么强;默认redis与16个数据库,不允许增加或删除,默认数据的操作都是在0号数据库中进行
- redis中切换库采用
select index,表示切换0~15号数据库;采用dbsize来查看当前数据库中key的个数;采用flushdb [async|sync]来选择同步或者异步清空数据库;采用flushall来清空所有数据库的所有内容
redis自定义客户端编写
RESP协议
- 在这个场景下,redis客户端和服务器之间采取的是为redis定制的RESP协议,基于TCP建立连接,采取一问一答的方式完成cs之间的信息交互
- 理解这一步时,对于客户端,发送请求借助bulk string数组将命令写入请求;服务器得到结果时,根据请求命令的得到情况,得到不同内容
协议格式
+OKrn
Simple String,以 + 开头
-ERRrn
Error,以 - 开头
:1000rn
整形,以:开头
$5rnhellorn
在这个场景下,BulkString,以$开头,相比Simple String能够传输二进制数据
*2rn$5rnhelloworld$5rnhellorn
Array,以*开头
Redis-plus-plus
声明redis连接对象
#include <sw/redis++/redis.h>
sw::redis::Redis redis("tcp://127.0.0.1:6379");
声明ip和Redis服务器端口号
采用ping方法检查连通性
std::string ret = redis.ping(); std::cout << ret << std::endl;
String
get、set方法
sw::redis::Redis redis("tcp://127.0.0.1:6379");
using namespace std::chrono_literals;
redis.set("key1", "val1", 0s, sw::redis::UpdateType::NO_EXIST); //0s表示不设置过期时间,没有提供只传递更新策略的set函数
auto ret = redis.get("key1");
if(ret) std::cout << ret.value() << std::endl;set方法基本采用借助传入sw::redis::StringView的key、val即可设置轻松键值对,StringView采用类似于std::string,但是他是只读的而且效率更高;得到值的类型是sw::redis::OptionalString,其中Optional表示无效值,没有采用std::string的原因是一方面如果直接采用对象的话,那么无法很好的表示nil,另一方面如果得到对象指针,还要设计内存指向空间是否有效的问题。sw::redis::OptionString在结果有效时,能够借助value()接口拿到得到内容,同时失败的情况也可借助他隐式转换为bool来得知
在这个场景下,set还有第三、四个参数,分别用来表示过期时间和更新策略。过期时间采用std::chrono::milliseconds,更新策略分为sw::redis::UpdateType::EXIST、sw::redis::UpdateType::NOT_EXIST sw::redis::UpdateType::ALWAYS,分对应setnx setxx 和直接set
exists和del
sw::redis::Redis redis("tcp://127.0.0.1:6379");
auto ret = redis.exists("key1");
ret = redis.del({"key1", "key2"});exists判断一个key是否存在,存在得到1,失败得到0del删除key,得到删除key的数目
keys
sw::redis::Redis redis("tcp://127.0.0.1:6379");
vector<string> ret; auto it = std::back_inserter(ret);
redis.keys("*", it);keys用来拿到所有的key,借助第一个参数的匹配规则来检索,方法和前面命令行中介绍的五种一样;同时为了解耦合,第二个参数要求传入输出型数据结构的尾插迭代器
expire和ttl
sw::redis::Redis redis("tcp://127.0.0.1:6379");
using namespace std::chrono_literals;
bool ret = redis.expire("key", 10s); std::this_thread::sleep_for(5s);
long long len = redis.ttl("key");expire用来设置过期时间,借助std::chrono给定ttl用来判断剩余生存时间,以秒为单位。已经过期的key,得到值为-1;不存在的key,得到值为-2
type
sw::redis::Redis redis("tcp://127.0.0.1:6379");
redis.set("key", "val");
std::string ret = redis.type("key");mget mset
sw::redis::Redis redis("tcp://127.0.0.1:6379");
redis.mset({std::make_pair("key1", "val1"), std::make_pair("key2", "val2")});
std::vector<std::string> keys;
redis.mset(keys.begin(), keys.end());
std::vector<sw::redis::OptionalString> vals; auto bit = std::back_inserter(vals);
redis.mget({"key1", "key2"}, bit);
std::vector<std::string> vals2 = {"key1", "key2"};
redis.mget(vals2.begin(), vals2.end(), bit);mget方法兼容借助初始化列表出入多个pair来表示要插入的多个键值对,或者能够采用迭代器的方式采用string数组的起止迭代器传参mset方法兼容借助初始化列表或者sw::redis::OptionalString数组来传入多个key,也需在最后传入一个尾插迭代器来带出结果
getrange setrange
sw::redis::Redis redis("tcp://127.0.0.1:6379");
std::string ret = redis.getrange("key", 5, 10);
long long val = redis.setrange("key", 5, "ins_val");getrange得到[start, end]范围中的内容,得到的时普通的std::stringsetrange将从offset开始的内容,设置为给定的字符串,同时得到完成更改后新的字符串的长度
incr decr
sw::redis::Redis redis("tcp://127.0.0.1:6379");
long long ret1 = redis.incr("key");
long long ret2 = redis.decr("key");incr和decr得到更改过后的值,两者都是以一为单位;相比get方法,后者得到的是sw::redis::OptionalString,如果想要当作数字采用的话还要转换
list
- lpush lrange rpush rpop lpop
sw::redis::Redis server("tcp://127.0.0.1:6379");
server.lpush("list1", "1");
server.lpush("list1", {"1", "2", "3"});
std::vector<std::string> vals = "1", "2", "3";
server.lpush("list1", vals.begin(), vals.end());
std::vector<std::string> rets;
auto bit = std::back_inserter(rets);
server.lrange("list1", bit);
auto ret1 = server.rpop("list1"); auto ret2 = server.lpop();lpush和rpush都是借助先指定列表名称,再指定元素的方式采用的。指定元素有三种方式:1. 直接给予,这样能够给一个;2. 借助初始化列表给予多个值;3. 现在外部容器构造完成,随后给予起止迭代器lrange,需给予一个尾插迭代器,来带出遍历结果rpop和lpop,两个都是借助直接给予列表名称,随后得到sw::redis::OptionalString的方式拿到两端元素
- brpop blpop
sw::redis::Redis server("tcp://127.0.0.1:6379");
auto ret = server.blpop("list1", std::chrono::seconds(1));
if(ret)
{
std::cout << ret.value().first << ret.value().second << std::endl;
}- 理解这一步时,两者都是阻塞式的拿到列表收尾的元素。对于传参,能够直接给字符串,也能够借助初始化列表,也能够借助迭代器的方式;得到结果采用的是
OptionalString+pair的方式得到,因为能够等待多个队列,所以既得到列表名,也得到删除元素;也能够设置超时时间,超过仍未拿到元素就直接得到
- llen
sw::redis::Redis server("tcp://127.0.0.1:6379");
long long server.llen("list");llen用来拿到列表长度
Set
- sadd smembers
sw::redis::Redis server("tcp://127.0.0.1:6379");
server.sadd("set", "1");
set<string> mems; auto in = std::inserter(mems, mems.end());
server.smembers("set", in);- 结合项目来看,Redis-plus-plus的接口风格统一性是很高的。
sadd前面提到的三种方式都能够,smembers也是借助迭代器的方式将遍历结果带出
- sismember spop scard
sw::redis::Redis server("tcp://127.0.0.1:6379");
bool exists = server.ismember("set", "1");
auto ret = spop("set"); if(ret) std::cout << ret.value() << std::endl;
long long num = scard("set");sismember,用来判断某个成员是否属于某个集合spop,用来随机得到集合中的一个元素scard,用来得到某个集合中的元素个数
Hash
- hget hset
sw::redis::Redis server("tcp://127.0.0.1:6379");
long long ret = server.hset("hash1", "1", "zhangsan");
ret = server.hset("hash1", {std::make_pair("2", "lisi"), std::make_pair("3", "wangwu")});
auto val = server.hget("hash1", "1"); if(val) std::cout << val.value() << std::endl;hset用来向哈希表中添加元素,依然能够借助直接给予、初始化列表、迭代器三种方式给予field-valuehget用拿到哈希表中field-value中的value
- hexists hdel hlen
sw::redis::Redis server("tcp://127.0.0.1:6379");
bool exists = server.hexists("hash1", "1");
long long ret = server.hdel("hash1", {"1", "2"});
ret = server.hlen("hash1");hexists用来判断某个field是否存在hdel用来删除多个field,同时得到删除的个数hel,得到一个哈希表中field的个数
- hmset hmget hkeys hvals
std::vector<std::string> keys;
auto it = std::back_inserter(keys);
server.hkeys("hash1", it);
std::vector<std::string> vals;
it = std::back_inserter(vals);
server.hvals("hash1", it);
server.hmget("hash1", {"3", "4", "5"}, it);
hmsethmget用来拿到、设置多个元素,hkeyshvals用来拿到一个哈希表中所有的fieldvalue
ZSet
- zadd zrange
server.zadd("zset1", "zhangsan", 1);
server.zadd("zset1", {std::make_pair("lisi", 2), std::make_pair("wangwu", 3)});
std::vector<std::pair<std::string, std::string>> pairs = {
std::make_pair("zhouliu", "4"),
std::make_pair("tianqi", "5")
};
server.zadd("zset1", pairs.begin(), pairs.end());
std::vector<std::string> withnoscore;
auto it = std::back_inserter(withnoscore);
std::vector<std::pair<std::string, double>> withscore; //通过模板类型来指定是否需要带出分数
auto it2 = std::back_inserter(withscore);
server.zrange("zset1", 0, -1, it2);zadd,往有序列表中加入成员和分数,依然兼容前面的三种方法zrange,遍历拿到有序列表中的元素,借助不同的输出容器来间接指定是否需拿到分数
- zcard zscore zrank zrem
long long ret = server.zcard("zset1");
auto score = server.zscore("zset1", "tianqi");
auto rank = server.zrank("zset1", "zhangsan");
server.zrem("zset1", "lisi");zcard,拿到有序集合中成员的个数zscore,根据成员拿到分数zrank,根据成员拿到排名(在整个有序列表中的数组下标)zrem,删除某个有序列表中的成员
到此这篇关于详解Redis终端操作和Redis-plus-plus接口采用教程的文章就介绍到这了,更多相关Redis终端和Redis-plus-plus接口内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多兼容脚本之家!
- 采用redis-plus-plus库连接redis的实现方法