Redis-常见数据类型(修改ing)

news2024/11/18 21:33:45

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虽然解决了阻塞的问题,但如果在遍历期间键有所变化(增加、修改、删除),可能导致遍历时键的重复遍历或者遗漏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2178393.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【笔记】数据结构12

文章目录 2013年408应用题41方法一方法二 看到的社区的一个知识总结&#xff0c;这里记录一下。 知识点汇总 2013年408应用题41 解决方法&#xff1a; 方法一 &#xff08;1&#xff09;算法思想 算法的策略是从前向后扫描数组元素&#xff0c;标记出一个可能成为主元素的元…

Learn OpenGL In Qt之炫酷进度条

竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~ 公众号&#xff1a; C学习与探索 | 个人主页&#xff1a; rainInSunny | 个人专栏&#xff1a; Learn OpenGL In Qt 文章目录 设计实现目录结构需要哪些类接口设计关键函数 实现效果Shader解析GLSL基本函数clampsmoothstep 实现分析效…

【Python】Beaker:轻量级缓存与会话管理的解决方案

Beaker 是一个简单、灵活的 Python 库&#xff0c;主要用于缓存管理和会话管理。作为一个开源项目&#xff0c;Beaker 提供了多种缓存存储后端&#xff0c;帮助开发者在应用中高效管理缓存数据&#xff0c;同时支持会话存储&#xff0c;适合 Web 应用中的用户状态管理。其轻量级…

WebAPI编程(第一天,第二天)

WebAPI编程&#xff08;第一天&#xff0c;第二天&#xff09; day01 - Web APIs1.1. Web API介绍1.1.1 API的概念1.1.2 Web API的概念1.1.3 API 和 Web API 总结 1.2. DOM 介绍1.2.1 什么是DOM1.2.2. DOM树 1.3. 获取元素1.3.1. 根据ID获取1.3.2. 根据标签名获取元素1.3.3. H5…

端侧多模态 | 不到10亿参数的端侧Agent竟媲美GPT-4V?AI手机不远了!

引言 简介 相关工作 模型 编码视觉信息 函数token 多阶段训练 模型评估 发送邮件 发送短信 Google搜索 Amazon购物 智能回收 失物招领 室内设计 Instacart购物 DoorDash(外卖平台)示例 动物护理 总结 引言 青山一道同云雨&#xff0c;明月何曾是两乡。 小伙…

王道-数据结构

1 设数组data[m]作为循环队列的存储空间,front为队头指针,rear为队尾指针,则执行出队操作后其头指针front值为____ 答案:D 解析:队列的头指针指向队首元素的实际位置,因此出队操作后,头指针需向上移动一个元素的位置。循环队列的容量为m,所以头指针front加1以后,需…

CVPR论文《DSVT: Dynamic Sparse Voxel Transformer with Rotated Sets》

1、整体思维导图 2、个人收获 这篇论文在理论上对于我来说可能就是让我大致了解了这个领域&#xff08;因为我的研究方向不是这方面&#xff09;&#xff0c;看完以后也没有看得特别懂&#xff08;说实话&#xff09;。 更多的收获应该是在论文的写作思路上吧 3、下面欣赏论…

分享几个可以免费使用GPT的网站【2024年必备】

1、ChatGPT 链接&#xff1a;点击直达 这个网站可以免费使用GPT4.0和GPT-4o模型&#xff0c;反应速度也很快&#xff0c;还有AI绘画可以体验喔~ 推荐指数&#xff1a;⭐⭐⭐⭐⭐ 2、AI智慧岛 链接&#xff1a;点击直达 推荐指数&#xff1a;⭐⭐⭐⭐⭐ 这个网站可以免费使…

Linux基础(四):文件权限与目录配置

1.使用者、群组、其他人概念 linux下每个文件都有三种权限类别&#xff0c;分别为使用者&#xff08;User&#xff09;、群组&#xff08;Group&#xff09;、其他人&#xff08;Others&#xff09;。这三种权限类别针对的是账号&#xff0c;也就是登录这个Linux系统的用户的账…

告别盲目推广!Xinstall为社交App带来精准流量

在移动互联网时代&#xff0c;社交类App如雨后春笋般涌现&#xff0c;但如何在众多竞争者中脱颖而出&#xff0c;成为用户首选&#xff1f;这不仅是开发者们面临的难题&#xff0c;也是推广者必须攻克的难关。今天&#xff0c;我们就来揭秘一种全新的社交类App推广策略&#xf…

64.【C语言】再议结构体(下)(未完)

本文衔接第63篇 目录 6.复习 7.修改默认对齐数 8.结构体传参 01.传递非指针参数 02.传递指针参数(传递地址) 03.对比 9.结构体实现位段 01.位段的定义 02.格式 03.例题 答案速查 分析 前置知识:位段的内存分配 解析 若按浪费空间处理 验证 6.复习 20.【C语言…

《OpenCV 计算机视觉》—— Harris角点检测、SIFT特征检测

文章目录 一、Harris 角点检测1.基本思想2.检测步骤3.OpenCV实现 二、SIFT特征检测1. SIFT特征检测的基本原理2. SIFT特征检测的特点3. OpenCV 实现 一、Harris 角点检测 OpenCV中的Harris角点检测是一种基于图像灰度值变化的角点提取算法&#xff0c;它通过计算每个像素点的响…

vue2 自定义empty指令

主要思路 定义一个echarts图标&#xff0c;数据为空&#xff0c;image采用base64编码图标宽高根据父宽高自适应渲染echarts函数&#xff0c;切换清除图例定义暂无数据指令 定义option /*** 暂无数据* param {number} width* param {number} height* returns option*/ functi…

全局思维下的联合创新:华为携手ISV伙伴助推银行核心平稳升级

文 | 螳螂观察 作者 | 李永华 随着数字金融快速发展&#xff0c;对核心系统提出了“海量、高效、弹性、扩展、敏捷”等新需求&#xff0c;区域性银行面临核心系统升级的迫切需要&#xff0c;对金融科技厂商而言也催生了庞大的机遇和空间。 只是&#xff0c;银行核心系统是金…

深度学习|求导公式:梯度逆传播规律

文章目录 引言基础函数的求导常数函数幂函数指数函数对数函数三角函数反三角函数双曲函数 复合函数的梯度逆传播链式法则函数相加函数相乘函数相除 结语 引言 我们知道&#xff0c;神经网络的能够学习处理任务的核心是计算损失的梯度&#xff0c;而误差逆传播算法是求梯度的一…

基于OpenCV的实时年龄与性别识别(支持CPU和GPU)

关于深度实战社区 我们是一个深度学习领域的独立工作室。团队成员有&#xff1a;中科大硕士、纽约大学硕士、浙江大学硕士、华东理工博士等&#xff0c;曾在腾讯、百度、德勤等担任算法工程师/产品经理。全网20多万粉丝&#xff0c;拥有2篇国家级人工智能发明专利。 社区特色…

842真题上的各种简答题

线性表 1. 应选用链式存储结构&#xff0c;因为链式存储结构采取动态内存分配&#xff0c;可以在操作过程中增加或减少线性表的长度&#xff0c;且插入删除操作更方便 2应该选用顺序存储结构&#xff0c;因为顺序存储结构的访问和存取&#xff0c;都是按照元素序号的随机访问…

VisualGLM-6B——原理与部署

VisualGLM-6B技术原理介绍 VisualGLM-6B 是一种多模态预训练模型&#xff0c;它旨在将视觉和语言模型进行结合&#xff0c;使得语言模型能够理解图像信息并与文本对话无缝结合。为了更好地理解 VisualGLM-6B 的内容&#xff0c;我们可以从以下几个方面来解析它的原理、结构、训…

基于springboot+小程序的自习室选座与门禁管理系统(自习室1)(源码+sql脚本+视频导入教程+文档)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 1、管理员实现了首页、基础数据管理、论坛管理、公告信息管理、用户管理、座位管理等 2、用户实现了在论坛模块通过发帖与评论帖子的方式进行信息讨论&#xff0c;也能对账户进行在线充值…

低代码可视化-uniapp蓝牙标签打印-代码生成器

蓝牙标签打印 蓝牙标签打印技术结合了蓝牙通信与标签打印的功能&#xff0c;为用户提供了一种便捷、高效的打印解决方案。以下是对蓝牙标签打印的详细解析&#xff1a; 蓝牙标签打印机的特点 无线连接&#xff1a;蓝牙标签打印机最大的亮点在于其无线连接方式。用户可以通过蓝…