您的位置:首页 > 汽车 > 新车 > 最适合穷人的四种保险_北京做网站最牛的公司_公关公司经营范围_东莞网站关键词优化排名

最适合穷人的四种保险_北京做网站最牛的公司_公关公司经营范围_东莞网站关键词优化排名

2024/11/17 14:29:39 来源:https://blog.csdn.net/qq_74812898/article/details/142623986  浏览:    关键词:最适合穷人的四种保险_北京做网站最牛的公司_公关公司经营范围_东莞网站关键词优化排名
最适合穷人的四种保险_北京做网站最牛的公司_公关公司经营范围_东莞网站关键词优化排名

1. 预备知识

redis按照键值对的方式存储数据

1.1 基本全局命令

KEYS

返回所有满⾜样式(pattern)的key,⽀持如下统配样式:

  • h?llo 匹配hello,hallo,hxllo
  • h*llo 匹配hllo,heeeello
  • h[ae]llo 只匹配hallo hello
  • h[^e]llo 匹配除hello,heee..llo以外的
  • h[a-b]llo 配置hallo,hbllo

KEYS pattern                O(N)

注意:

keys命令的时间复杂度是O(N),

所以,在生产环境上,一般禁止使用此命令,尤其是keys *

 

EXISTS

判断某个key是否存在

EXISTS key [key ...]               O(1)

 

注:

分开的写法,会产生更多轮次的网络通信,效率比较低,成本比较高

DEL

删除指定的key

DEL key [key ...]                   O(1)

 

EXPIRE

为指定的key添加秒级的过期时间

EXPIRE key seconds            O(1)

 

TTL

获取指定key的过期时间,秒级

TTL key           O(1)

[面试题]redis的key的过期策略是怎么实现的?

一个redis中可能同时存在很多key,这些key中可能有很大一部分都有过期时间,那redis服务器怎么知道哪些key已经过期要被删除,哪些key还没过期?

如果之间遍历所有的key,显然不行,效率太低

整体的策略为:定期删除+惰性删除

定期删除:

每次抽取一部分,进行验证过期时间,保证这个抽取检查的过程,足够快

这里对定期删除的时间,为什么有明确的要求呢?

因为redis是单线程程序,如果扫描过期key消耗的时间太多,就可能导致正常处理请求命令被阻塞

惰性删除:

假设这个key已经到过期时间了,但是暂时还没删除,key还存在

紧接着,后面又一次访问,正好用到了这个key,于是这次访问就会让redis服务器触发删除这个key的操作,返回一个nil

虽然有上述的两种策略结合,但是整体效果一般,仍然会有很多过期的key残留,redis为了进行补充,提供了一系列的内存淘汰策略

定时器(Redis并未使用)

①基于优先级队列/堆

正常的队列是先进先出

优先级队列是按照指定的优先级先出

在redis过期key的场景中,就可以通过"过期时间越早,优先级越高"

现在假定有很多key设置了过期时间,就可以把这些key加入到一个优先级队列中,指定优先级规则是过期时间早的,先出队列

队首元素,就是最早过期的key

此时定时器只要分配一个线程,让这个线程去检查队首元素,看是否过期即可,如果队首还没过期,后续元素一定没过期

此时,扫描线程只需要盯住这一个队首元素即可

另外在扫描线程检查队首元素过期时间时,也不能太频繁

可以根据当前时刻和队首元素的过期时间,设置一个等待,当时间差不多了,系统再唤醒这个线程

此时扫描线程不需要高频扫描队首元素,把cpu的开销也节省下来了

万一在线程休眠的时候,来了一个新任务,是11:00执行

可以在新任务添加的时候,唤醒一下刚才的线程,重新检查一下队首元素,再根据时间差距重新调整阻塞时间即可

②基于时间轮实现的定时器

把时间划分成很多小段

每个小段上都挂着一个链表,每个链表都代表一个要执行的任务

(相当于一个函数指针,以及对应的参数什么的)

此时这个指针,就会每隔固定的间隔,每次走到一个格子,就把这个格子上链表的任务尝试执行一下

对于时间轮来说,每个格子是多少时间,一共多少个格子,都是需要根据实际场景设置

 

TYPE 

返回key对应的数据类型

TYPE key             O(1)

 

1.2 数据结构和内部编码

Redis的5种数据类型:

string(字符串)、list(列 表)、hash(哈希)、set(集合)、zset(有序集合)

有序集合,是除了存储member之外,还需要存储一个score 

实际上Redis针对每种数据结构都有⾃⼰的底层内部编码实现,⽽且是多种实现,这样Redis会在合适的场景选择合适的内部编码

内部编码:

数据结构内部编码
string

raw

int

embstr

hash

hashtable

ziplist

list

linkedlist

ziplist

set

hashtable

intset

zset

skiplist

ziplist

  • ziplist:压缩列表,能够节省空间 
  • quicklist:兼顾linkedlist和ziplist的优点,把空间和效率都兼顾
  • skiplist:跳表,也是链表,不同于普通链表,每个节点上有多个指针域,巧妙地搭配这些指针域地指向,就可以做到,时间复杂度O(logN)

通过object encoding命令查询内部编码

Redis 这样设计有两个好处:

1可以改进内部编码,⽽对外的数据结构和命令没有任何影响,这样⼀旦开发出更优秀的内部编码, ⽆需改动外部数据结构和命令

2) 多种内部编码实现可以在不同场景下发挥各⾃的优势,例如ziplist⽐较节省内存,但是在列表元素⽐较多的情况下,性能会下降,这时候Redis会根据配置选项将列表类型的内部实现转换为 linkedlist,整个过程⽤⼾同样⽆感知

1.3 单线程架构

引入

redis只使用一个线程,处理所有的命令请求,不是说一个redis服务器进程内部真的就只有一个线程,其实也有多个线程,多个线程在处理网络IO

假设,有多个客户端,同时操作一个服务器:

 

当前这两个客户端,也相当于"并发"发起请求了

那服务器是否会存在类似的线程安全问题呢?

并不会,redis服务器实际上是单线程模型,保证了当前收到的这多个请求是串行执行的

多个请求同时到达redis服务器,也是要现在在队列中排队,在等待redis服务器一个一个的取出里面的命令再执行

微观上讲,redis服务器是串行/顺序执行这多个命令的

redis虽然是单线程模型,为什么效率这么高,速度这么快?

  • redis访问内存,数据库则访问硬盘
  • redis的核心功能,比数据库简单.数据库对于数据的插入删除查询,都有更复杂的功能支持,会花费更多的开销
  • 单线程模型,避免了一些不必要的线程竞争开销.redis每个基本操作,都是短平快的,不会特别消耗cpu
  • 处理网络IO的时候,使用了epoll这样的IO多路复用机制 (一个线程,管理多个socket)

注:

虽然单线程给Redis带来很多好处,但还是有⼀个致命的问题:

对于单个命令的执⾏时间都是有要求的。如果某个命令执⾏过⻓,会导致其他命令全部处于等待队列中,迟迟等不到响应,造成客⼾端的阻塞,对于Redis这种⾼性能的服务来说是⾮常严重的,所以Redis是⾯向快速执⾏场景的数据库

2. String字符串

2.1 常见命令

SET

如果key之前存在,则覆盖,⽆论原来的数据类型是什么

之前关于此key的TTL也全部失效

SET key value [ EX seconds|PX milliseconds] [NX|XX]

  • EX seconds⸺使⽤秒作为单位设置key的过期时间。
  • PX milliseconds⸺使⽤毫秒作为单位设置key的过期时间。
  • NX ⸺只在key不存在时才进⾏设置,即如果key之前已经存在,设置不执⾏。
  • XX ⸺只在key存在时才进⾏设置,即如果key之前不存在,设置不执⾏

 

 

GET

获取key对应的value

如果key不存在,返回nil

如果value的数据类型不是string,会报错

GET key

 

MGET

⼀次性获取多个key的值。

如果对应的key不存在或者对应的数据类型不是string,返回nil 

MGET key [key ...]

 

MSET

⼀次性设置多个key的值 

MSET key value [key value ...]

 

SETNX

设置key-value但只允许在key之前不存在的情况下

SETNX key value

 

2.2 计数命令

INCR

针对value+1

此时key对应的value必须是整数

如果key不存在,则视为key对应的value是0

INCR key

 

INCRBY

针对value+n

此时key对应的value必须是整数

如果key不存在,则视为key对应的value是0

INCRBY key decrement

 

DECR 

针对value-1

此时key对应的value必须是整数

如果key不存在,则视为key对应的value是0

DECR key

 

DECYBY

针对value-n

如果key不存在,则视为key对应的value是0

此时key对应的value必须是整数

DECRBY key decrement 

 

INCRBYFLOAT

将key对应的string表⽰的浮点数加减上对应的值

只能用加上负数的形式实现减法

运算的操作数可以是浮点数

INCRBYFLOAT key increment

 

2.3 其它命令

APPEND

如果key已经存在并且是⼀个string,命令会将value追加到原有string的后边

如果key不存在, 则效果等同于SET命令 

APPEND KEY VALUE

 

注意:

qppend返回值,长度的单位是字节

redis的字符串,不会对字符编码做出任何处理

当前xshell终端,默认的字符编码是utf8

一个汉字在utf8字符集中,通常是三个字节

在启动redis客户端时,加上--raw,就可以使客户端自动把二进制数据尝试翻译

 

GETRANGE

返回key对应的string的⼦串,左闭右闭。

可以使⽤负数表⽰倒数: -1代表倒数第⼀个字符

超过范围的偏移量会根据string的⻓度调整成正确的值 

GETRANGE key start end

 

 

SETRANGE

覆盖字符串的⼀部分,从指定的偏移开始

offset 偏移量,从第几个字节,开始进行替换

SETRANGE key offset value

如果当前的value是一个中文字符串,进行setrange的时候,可能会出问题: 

 

setrange对不存在的key也可以进行操作,不过会把offset之前的内容填充为0x00:

 

STRLEN

获取key对应的string的⻓度,单位是字节

STRLEN key

2.4 命令总结

命令执行结果时间复杂度
set设置key的值是valueO(k),k是键个数
get获取key的值O(1)
del删除指定的keyO(k),k是键个数
mset批量设置指定的key和valueO(k),k是键个数
mget批量获取key的值O(k),k是键个数
incr指定的key的值+1O(1)
decr指定的key的值-1O(1)
incrby指定的key的值+nO(1)
decrby指定的key的值-nO(1)
incrbyfloat指定的key的值+nO(1)
append指定的key的值追加valueO(1)
strlen获取指定key的值的⻓度O(1)
setrange覆盖指定key的从offset开始的部分值O(n),n是字符 串⻓度,通常视 为O(1)
getrange获取指定key的从start到end的部分值O(n),n是字符 串⻓度,通常视 为O(1)

2.5 内部编码

  • int:64位/8个字节的⻓整型
  • embstr:压缩字符串,⼩于等于39个字节的字符串
  • raw:普通字符串,⼤于39个字节的字符串 

 

2.6 典型使用场景

缓存功能 

 

整体思路:

应用服务器访问数据的时候,先查询redis

如果redis上数据存在,就直接从redis取数据交给服务器,不再访问数据库

如果redis上数据不存在,再读取mysql,把读到的数据给应用服务器,通过把数据写入redis 

redis这样的缓存,经常用来存储"热点数据"


上述策略,存在一个问题:

随着时间的推移,会有更多的key在redis上访问不到,从而会读取mysql写入redis,那么redis的数据不是会更多么?

1)把数据写入redis的同时,设置一个过期时间

2)redis在内存不足时,提供"淘汰策略"

计数功能

使⽤Redis作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,同时数据可以异步处理或者落地到其他数据源

例如:

⽤⼾每播放⼀次视频,相应的视频播放数就会⾃增1

 

redis不擅长数据统计:

比如,统计播放量前100的视频有哪些,基于redis就会很麻烦,而mysql一个sql就搞定了 

 

共享会话

①Session分散存储: 

 

如果每隔应用服务器,维护自己的会话数据,彼此之间不共享,用户请求访问到不同的服务器上,就会出现一些不能正确处理的情况 

②Redis集中管理Session:

 

 

手机验证码

为了短信接⼝不会频繁访问,会限制⽤⼾每分钟获取验证码的频率

例如:⼀分钟不能超过5次 

 

3. Hash哈希

 

3.1 常见命令

HSET

设置hash中指定的字段(field)的值(value)

HSET key field value [field value ...]

 

HGET

获取hash中指定字段的值

HGET key field

 

 

HEXISTS

判断hash中是否有指定的字段

HEXISTS key field

 

HDEL

删除hash中指定的字段

HDEL key field [field ...] 

 

HKEYS

获取hash中的所有字段

HKEYS key 

  

HVALS

获取hash中的所有的值

HVALS key

 

 

HGETALL

获取hash中的所有字段以及对应的值

HGETALL key

 

HMGET

⼀次获取hash中多个字段的值

HMGET key field [field ...] 

 

HLEN

获取hash中的所有字段的个数

HLEN key 

 

HSETNX

在字段不存在的情况下,设置hash中的字段和值 

HSETNX key field value

 

HINCRBY

将hash中字段对应的数值添加指定的值

HINCRBY key field increment 

 

HINCRBYFLOAT

HINCRBY的浮点数版本

HINCRBYFLOAT key field increment 

 

3.2 命令总结

命令执⾏效果时间复杂度
hset 设置值O(1)
hget获取值O(1)
hdel删除fieldO(k), k 是field 个数
hlen计算field个数O(1)
hgetall获取所有的field-valueO(k), k 是field 个数
hmget批量获取field-valueO(k), k 是field 个数
hmset批量获取field-valueO(k), k 是field 个数
hexists判断field是否存在O(1)
hkeys获取所有的fieldO(k), k 是field 个数
hvals获取所有的valueO(k), k 是field 个数
hsetnx设置值,但必须在field不存在时才能设置成功O(1)
hincrby对应field-value+nO(1)
hincrbyfloat对应field-value+nO(1)
hstrlen计算value的字符串⻓度O(1)

3.2 内部编码

  • ziplist(压缩列表):当哈希类型元素个数⼩于hash-max-ziplist-entries配置(默认512个)、 同时所有值都⼩于hash-max-ziplist-value配置(默认64字节)时,Redis会使⽤ziplist作为哈 希的内部实现,节省内存空间。进行读写元素时,速度比较慢
  • hashtable(哈希表):当哈希类型⽆法满⾜ziplist的条件时,Redis会使⽤hashtable作为哈希 的内部实现,因为此时ziplist的读写效率会下降,⽽hashtable的读写时间复杂度为O(1)。 

1)当field个数⽐较少且没有⼤的value时,内部编码为ziplist:

 

2)当有value⼤于64字节时,内部编码会转换为hashtable:

3)当field个数超过512时,内部编码也会转换为hashtable

 

3.4 典型使用场景

用户信息 

关系型数据表保存⽤⼾信息:

映射关系表⽰⽤⼾信息:

使用hash的方式表示UserInfo,,就可以使用field表示对象的每个属性(数据表的每个列),此时就可以很方便的修改/获取任何一个属性的值,但是付出了更多空间的代价

使用string的方式表示UserInfo,万一只想获取或者修改其中的某个field, 就需要把整个json都读出来,解析成对象,操作field,再重写转成json字符串写回去

相⽐于使⽤JSON格式的字符串缓存⽤⼾信息,哈希类型变得更加直观,并且在更新操作上变得更灵活

哈希类型是稀疏的,⽽关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的field,⽽ 关系型数据库⼀旦添加新的列,所有⾏都要为其设置值,即使为null

关系数据库可以做复杂的关系查询,⽽Redis去模拟关系型复杂查询,例如联表查询、聚合查询等 基本不可能,维护成本⾼

4. List列表

 

列表中允许有重复元素

元素是有序的(位置颠倒后的list不等价)

4.1 常见命令

LPUSH

头插

LPUSH key element [element ...]

 

LPUSHX

在key存在时,头插

不存在,直接返回

LPUSHX key element [element ...]

 

 

RPUSH

尾插

RPUSH key element [element ...]

 

RPUSHX 

在key存在时,尾插

RPUSHX key element [element ...]

 

LRANGE

获取从start到end区间的所有元素,左闭右闭

LRANGE key start stop

 

 

LPOP

头删

LPOP key

 

RPOP

尾删

RPOP key

 

LINDEX

获取从左数第index位置的元素

LINDEX key index

 

LINSERT

在特定位置插⼊元素

LINSERT key <BEFORE|AFTER> pivot element  

 

LLEN

获取list⻓度 

LLEN key

 

4.2 阻塞版本命令

在列表中有元素的情况下,阻塞和⾮阻塞表现是⼀致的

如果列表中没有元素,⾮阻塞版本会返回nil

但阻塞版本会根据timeout,阻塞⼀段时间,期间Redis可以执⾏其他命令,但要求执⾏该命令的客⼾端会表现为阻塞状态,如果没有阻塞时间,就阻塞到不为空

此处的blpop和brpop看起来阻塞很久,但实际上不会对redis服务器产生负面影响

可以同时尝试获取多个key的列表的元素,多个key对应多个list,哪个list有元素,就返回哪个元素

多个客户端同时执行pop,则最先执行命令的客户端会得到弹出的元素

BLPOP

LPOP的阻塞版本

BLPOP key [key ...] timeout 

BRPOP 

RPOP的阻塞版本

BRPOP key [key ...] timeout

1)针对一个非空的列表进行操作:

返回的结果相当于一个pair,一方面告诉我们当前的数据来自哪个key,一方面告诉我们取得的数据是什么

2)针对一个空的列表进行操作:

3)针对多个key进行操作:

此时,只有key3有元素

4.3 命令总结

操作类型命令时间复杂度
添加

rpush

lpush

linsert

lrange

O(k),k是元素个数

O(k),k是元素个数

O(n),n是pivot距离头尾的距离

查找

lrange

lindex

llen

O(s+n),s是start偏移量,n是start到end的范围

O(n),n是索引的偏移量

O(1)

删除

lpop

rpop

lremkey

ltrim

O(1)

O(1)

O(k),k是元素个数

O(k),k是元素个数

修改lsetO(n),n是索引的偏移量
阻塞操作blpop/brpopO(1)

 

4.4 内部编码

  • ziplist(压缩列表):当列表的元素个数⼩于list-max-ziplist-entries配置(默认512个),同时 列表中每个元素的⻓度都⼩于list-max-ziplist-value配置(默认64字节)时,Redis会选⽤ ziplist来作为列表的内部编码实现来减少内存消耗
  • linkedlist(链表):当列表类型⽆法满⾜ziplist的条件时,Redis会使⽤linkedlist作为列表的内 部实现

1)当元素个数较少且没有⼤元素时,内部编码为ziplist:

quicklist相当于链表和压缩列表的结合,整体还是一个链表,链表的每个节点,是一个压缩列表

2)当元素个数超过512时,内部编码为linkedlist

3)当某个元素的⻓度超过64字节时,内部编码为linkedlist

4.5 典型使用场景 

消息队列

生产者消费者模型

①Redis阻塞消息队列模型:

谁先执行的这个brpop指令,谁就能拿到这个新来的元素

这样的设定,构成一个"轮询"式的效果

消费者1拿到元素之后,就从brpop中返回了,如果消费1还想继续消费,就需要重写执行brpop~ 

②分频道的消息队列:

不同的消费 者可以通过brpop不同的键值,实现订阅不同频道的理念 

 

多个频道的场景十分常见:

比如,一个通道用来发送视频,一个用来点赞,一个用来评论~

搞成多个频道,就可以在某种数据发生问题的时候,不会对其它数据造成影响(解耦合)

选择列表类型时:

同侧存取(lpush+lpop或者rpush+rpop)为栈

异侧存取(lpush+rpop或者rpush+lpop)为队列

数组 

①mysql表示学生和班级信息:

②redis:

就可以很方便的实现"查询指定班级中有哪些学生"

5. Set集合

元素无序

不能重复(唯一) 

5.1 常见命令

SADD

重复的元素⽆法添加到set中

SADD key member [member ...] 

SMEMBERS

获取⼀个set中的所有元素

元素间的顺序是⽆序的 

SMEMBERS key

 

SISMEMBER

判断⼀个元素在不在set中 

SISMEMBER key member

 

SCARD

获取⼀个set中的元素个数

SCARD key

 

SPOP

从set中删除并返回⼀个或者多个元素

由于set内的元素是⽆序的,所以取出哪个元素实际是随机的

SPOP key [count]

 

SMOVE

将⼀个元素从源set取出并放⼊⽬标set中

要移动的元素在source中不存在,返回0,移动失败

SMOVE source destination member

 

SREM

将指定的元素从set中删除 

SREM key member [member ...]

 

5.2 集合间操作

SINTER(交集) 

获取给定set的交集中的元素

SINTER key [key ...]

 

SINTERSTORE

获取给定set的交集中的元素并保存到⽬标set中

SINTERSTORE destination key [key ...]

SUNION(并集)

 获取给定set的并集中的元素

SUNION key [key ...]

SUNIONSTORE

获取给定set的并集中的元素并保存到⽬标set中 

SUNIONSTORE destination key [key ...]

 

SDIFF(差集)

获取给定set的差集中的元素 

SDIFF key [key ...]

SDIFFSTORE

获取给定set的差集中的元素并保存到⽬标set中

SDIFFSTORE destination key [key ...] 

5.3 命令总结

命令时间复杂度
saddO(k),k是元素个数
sremO(k),k是元素个数
scardO(1)
sismemberO(1)
srandmemberO(n),n是count
spopO(n),n是count
smembersO(k),k是元素个数
sinterO(m*k),k是⼏个集合中元素最⼩的个数,m是键个数
sunionO(k),k是多个集合的元素个数总和
sdiffO(k),k是多个集合的元素个数总和

5.4 内部编码

  • intset(整数集合):当集合中的元素都是整数并且元素的个数⼩于set-max-intset-entries配置 (默认512个)时,Redis会选⽤intset来作为集合的内部实现,从⽽减少内存的使⽤。
  • hashtable(哈希表):当集合类型⽆法满⾜intset的条件时,Redis会使⽤hashtable作为集合 的内部实现。

1)当元素个数较少并且都为整数时,内部编码为intset:

2)当元素个数超过512个,内部编码为hashtable

3)当存在元素不是整数时,内部编码为hashtable:

5.5 典型使用场景

保存用户的"标签

用户画像:根据信息分析用户的特征,转化为"标签"(简短的字符串), 保存在set里

计算用户之间的共同好友

 基于集合求交集

统计UV

去重~

一个互联网产品,根据PV,UV衡量用户量,每个用户访问服务器,就会产生一个UV,但是同一个用户多次访问, 不会使UV增加,通过set来实现去重

6. Zset有序集合

给zset的中member引入了一个属性score(浮点类型)

进行排序的时候,就是依照此处的分数大小进行升序/降序,默认升序

score可以重复,member唯一

不能把member和score理解为键值对,键值对有明显的角色区分,一定是键=>值,而有序集合可以互相匹配

6.1 常见命令

ZADD

ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...] 

  • XX:仅仅⽤于更新已经存在的元素,不会添加新元素。 
  • NX:仅⽤于添加新元素,不会更新已经存在的元素。 
  • CH:默认情况下,ZADD返回的是本次添加的元素个数,但指定这个选项之后,就会还包含本次更新的元素的个数。 
  • INCR:此时命令类似ZINCRBY的效果,将元素的分数加上指定的分数。此时只能指定⼀个元素和 分数。

 

ZCARD

获取zset中的元素个数

ZCARD key 

ZCOUNT

返回分数在min和max之间的元素个数 

ZCOUNT key min max

 

ZRANGE

返回指定区间⾥的元素,分数按照升序

带上WITHSCORES可以把分数也返回

ZRANGE key start stop [WITHSCORES] 

ZREVRANGE

返回指定区间⾥的元素,分数按照降序

带上WITHSCORES可以把分数也返回,,并且功能合并到ZRANGE中

ZREVRANGE key start stop [WITHSCORES]

 

ZRANGEBYSCORE

返回分数在min和max之间的元素 

ZRANGEBYSCORE key min max [WITHSCORES]

ZPOPMAX

删除并返回分数最⾼的count个元素 

ZPOPMAX key [count]

 

此处疑问:

此处删除的是最大值,而有序集合,最大值就相当于最后一个元素(尾删),那为什么不把最后一个元素的位置记录下来,后续删除就可以o(1)了??

O(logN)=>此处如果N不是特别的大,基本可以近似看作O(1)的

优化空间??

BZPOPMAX

ZPOPMAX的阻塞版本

BZPOPMIN key [key ...] timeout

key标识了有序集合

ZPOPMIN

删除并返回分数最低的count个元素

ZPOPMIN key [count]

BZPOPMIN

ZPOPMIN的阻塞版本

BZPOPMIN key [key ...] timeout

ZRANK

返回指定元素的排名,升序

ZRANK key member

 

ZREVRANK

返回指定元素的排名,降序

ZREVRANK key member

ZSCORE

返回指定元素的分数

ZSCORE key member

 

ZREM

删除指定的元素 

ZREM key member [member ...]

ZREMRANGEBYRANK

按照排序,升序删除指定范围的元素,左闭右闭

ZREMRANGEBYRANK key start stop

ZREMRANGEBYSCORE

按照分数删除指定范围的元素,左闭右闭

ZREMRANGEBYSCORE key min max

ZINCRBY

为指定的元素的关联分数添加指定的分数值 

ZINCRBY key increment member

 

6.2 集合间操作

ZINTERSTORE 

求出给定有序集合中元素的交集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元素对应的分数按照不同的聚合⽅式和权重得到新的分数

ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE ]

 

ZUNIONSTORE

求出给定有序集合中元素的并集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元 素对应的分数按照不同的聚合⽅式和权重得到新的分数

ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE ]

 

 

6.3 命令总结

命令时间复杂度

zadd

O(k *log(n)),k 是添加成员的个数,n是当前有序集合的元 素个数
zcardO(1)
zscoreO(1)
zrankO(log(n)),n是当前有序集合的元素个数
zremO(k*log(n)),k是删除成员的个数,n是当前有序集合的元 素个数
zincrbyO(log(n)),n是当前有序集合的元素个数
zrangeO(k+log(n)),k是获取成员的个数,n是当前有序集合的元 素个数
zrevrangeO(k+log(n)),k是获取成员的个数,n是当前有序集合的元 素个数
zrangebyscoreO(k+log(n)),k是获取成员的个数,n是当前有序集合的元 素个数
zrevrangebyscoreO(k+log(n)),k是获取成员的个数,n是当前有序集合的元 素个数
zcountO(log(n)),n是当前有序集合的元素个数
zremrangebyrankO(k+log(n)),k是获取成员的个数,n是当前有序集合的元 素个数
zremrangebyscoreO(k+log(n)),k是获取成员的个数,n是当前有序集合的元 素个数
zinterstorO(n*k)+O(m*log(m)),n是输⼊的集合最⼩的元素个数, k是集合个数,m是⽬标集合元素个数
zunionstoreO(n)+O(m*log(m)),n是输⼊集合总元素个数,m是⽬标 集合元素个数

6.4 内部编码

  • ziplist(压缩列表):当有序集合的元素个数⼩于zset-max-ziplist-entries配置(默认128个), 同时每个元素的值都⼩于zset-max-ziplist-value配置(默认64字节)时,Redis会⽤ziplist来作 为有序集合的内部实现,ziplist可以有效减少内存的使⽤。
  • skiplist(跳表=>复杂链表o(logN)):当ziplist条件不满⾜时,有序集合会使⽤skiplist作为内部实现,因为此时 ziplist的操作效率会下降 

1)当元素个数较少且每个元素较⼩时,内部编码为ziplist:

2)当元素个数超过128个,内部编码skiplist

3)当某个元素⼤于64字节时,内部编码skiplist:

6.5 典型使用场景

排行榜系统 

①比如游戏玩家排行:

只需要把玩家信息和对应的分数放到有序集合即可,自动就形成了排行榜

随时可以按照下标,分数进行范围查询

修改分数也是比较方便的,排行顺序也会自动调整

但也有的排行榜要复杂一些

②如微博热度:

要考虑浏览量,点赞量等多个因素的综合数值,计算权重weight 

此时可以借助zinterstore/zunionstore按照加权方式处理

就可以把上述每个维度的数值都放到一个有序集合中,member就是微博id,score就是各自维度的数值,通过集合间运算得到结果的集合的分数就是热度,从而排行榜就出来了

7. 补充类型

Streams

模拟实现事件传播的机制

那么事件是干什么的?我们也不知道它什么时候出现,只能等这个事情出现了之后,采取动作

比如:一旦着火,立即灭火,没着火,就等着

此处的着火就是"事情",灭火就是"事件触发的回调函数"

Geospatial

用来存储坐标(经纬度)

存储一些点之后,就可以让用户给定一个坐标,去从刚才存储的点进行查找

这个功能在地图中很重要

HyperLogLog

估算集合中的元素个数

之前在Set应用场景中提到了统计UV,但是有一个问题,UV数据量很大,就会消耗更多内存空间

而HyperLogLog不存储元素的内容,但是能记录"元素的特征",从而在新增元素的时候,能够知道当前新增的元素是一个已经存在的,还是新的

存储元素的时候,提取特征的过程是不可逆的,不能通过特征得到内容(信息量丢失了)

Bitmaps

使用bit位表示整数

位图本质上,还是一个集合,是set类型针对整数的特化版,会存储元素

比较高效,更节省空间

Bitfields 

和C中的位域,十分相似,更精确地进行位操作

 

可以理解为一串二进制序列(字节数组),同时把这个字节数组的某几个位,赋予特定的含义,并且可以进行读取/修改/算术运算等操作

相比于String/hash,节省空间

8. 渐进式遍历

keys *一次性把整个redis中所有的key都获取到,这个操作比较危险,可能会阻塞redis服务器 

Redis 使⽤scan命令进⾏渐进式遍历,进⽽解决直接使⽤keys获取键时可能出现的阻塞问题

每执行一个命令,只获取其中的一小部分

每次scan命令的时间复杂度是O(1),但是要完整地完成所有键的遍历,需要执⾏多次scan

SCAN

 

注意:

  • cursor不能理解成"下标",不是一个连续递增的整数,仅仅就是一个字符串
  • 光标这个概念,客户端是不能热认识的,redis服务器则知道对应的元素位置

 

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

count限制这一次遍历能够获取到多少个元素,默认是10 

count只是一个建议,但是写入的count和实际返回的key的个数不一定相同,也不会差太多

时间复杂度:O(1)

返回值:下⼀次scan的游标(cursor)以及本次得到的键 

注意:

  • 这里的渐进式遍历,在遍历过程中,不会在服务器这边存储任何状态信息,此处的遍历是可以随时终止的,不会对服务器产生任何的副作用
  • 渐进性遍历scan虽然解决了阻塞的问题,但如果在遍历期间键有所变化(增加、修改、删除),可能导致遍历时键的重复遍历或者遗漏

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com