Redis学习-Redis的九种数据结构

String (字符串)

虽然redis是用C语言编写,但是redis中的string是redis自己实现的字符串结构,叫Simple Dynamic String简称(SDS),因为redis做为中间件会接受不同语言编写的程序传过来的字符串,它们都可能和C语言中的字符串结构不一样,例如C语言中以’\0’表是字符串的结尾,当传过来的二进制数据转换成C语言中的字符串中间有’\0’那么C语言就会认为这个字符串已经结束了,则后面的数据就丢失了。redis中的string包含四部分:一个char类型的数组buff、一个len记录已经使用的长度、一个flags标识当前string的头信息以及一个alloc记录给当前string分配的大小。当客户端通过append或者setbit命令对string进行操作时,如果增加的字符串长度小于等于alloc-len则还是在当前string内存空间中直接添加即可,如果超出了string中剩余空间,则会重新分配一块内存,大小为新的字符串的长度*2(如果字符串的大小超过1mb则之后的扩容不会翻倍扩容而是每次增加1mb)。string最大的大小为512MB

使用场景

  • 计数器:Redis的原子递增操作可以用来实现Web应用中的各种计数功能,如页面访问计数、点赞数、评论数等。
  • 分布式锁:在需要保证操作原子性的场景下,比如防止多个用户同时修改同一资源,可以使用Redis的String类型实现分布式锁。
  • 会话共享:在多实例部署的Web应用中,可以使用Redis来存储用户的会话信息,实现会话共享,避免因会话问题导致的登录失效。
  • 限流:通过设置特定的key来限制用户在一定时间内的请求次数,如短信验证码的发送频率控制,防止滥用。
  • 全局序列号生成:在分布式系统中,可以使用Redis生成全局唯一的序列号,适用于订单号、票据号等。
  • 消息队列:虽然不是String类型的直接应用,但可以通过List或Pub/Sub功能来实现消息队列,处理异步任务或事件通知。
  • 存储JSON对象:将序列化后的JSON对象存储在Redis的String类型中,用于快速读取和修改,如用户配置、系统状态等,(如果对象中的某些字段经常修改时,建议用hash存储)。
  • Webhook触发器:存储Webhook URL,当特定事件发生时,通过调用这些URL来触发外部服务。
  • 分布式系统间的通信:在微服务架构中,Redis可以作为不同服务间的通信桥梁,传递轻量级的消息或数据。
  • 缓存静态资源:将不经常变动的静态资源如图片、CSS和JavaScript文件缓存到Redis中,以减少数据库和文件系统的压力。
  • 用户个性化设置:存储用户的个性化设置,如主题偏好、界面布局等,以便快速加载用户定制的界面。

常用命令

set key value//存储字符串键值对
mset key value [key value....]//批量存储字符串键值对
setnx  key value//存入一个不存在的字符串键值对
get key//通过key获取value
mget key [key....]//批量获取value
del key [key....]//删除键值对
expire key seconds//设置过期时间
setex key seconds value//存储键值对并设置过期时间(秒)
psetex key milliseconds value//存储键值对并设置过期时间(毫秒)
append key value//key存在则追加字符串,不存在则设置
incr key//将key对应的数字值加一
decr key//将key对应的数字值减一
incrby key increment//将key对应的数字值加increment 
decrby key increment//将key对应的数字值减increment 

hash(散列)

redis的K-V键值对用的结构是dict,hash用的也是这个结构,类似于java中的hashtalbe,先有一个数组假设长度是4,当hset key field value时,将field通过hash算法如CRD16(field)计算得到一个0-65535之间的一个整数,再和数组长度取模得到这个field在数组中的位置,如果这个位置已经有了数据则将当前的value加入对应的链表中(当前的value为首节点)。
hash表中有一个重要的参数叫负载因子(负载因子 = used / size ; used 是哈希数组存储的元素个数,size 是哈希数组的长度)。当负载因子过大或过小,hash表会进行扩展或缩容,这两种都是通过渐进式rehash的方式完成的。
渐进式rehash:当哈希表中的元素过多时,如果一次性rehash到新的hashtable里,庞大的计算量,可能导致redis服务在一段时间不可用。为了避免rehash对服务器带来的影响,redis分多次、慢慢的将原本的哈希表中的键值对rehash到新的哈希表,这就是渐进式rehash
注:使用hash结构时,应避免单个hash结构过大,否则使用hgetall的操作时可能会导致线程阻塞,而且field是无法单独设置过期时间的

使用场景

类似于电商购物车这样的需要对对象中某一属性频繁修改的数据。

常用命令

HSET key field value//存储一个哈希表key的键值
HSETNX key field value//存储一个不存在的哈希表key的键值
HMSET key field value [field value ...]//在一个哈希表key中存储多个键值对
HGET key field//获取哈希表key对应的field键值
HMGET key field [field ...]//批量获取哈希表key中多个field键值
HDEL key field [field ...]//删除哈希表key中的field键值
HLEN key//返回哈希表key中field的数量
HGETALL ey//返回哈希表key中所有的键值
HINCRBY key field increment//为哈希表key中field键的值加上增量increment

list(列表)

一个有序的链表结构,使用quicklist和ziplist实现。
当List满足以下两个条件时,使用Ziplist编码:

  • 列表中的元素数量小于或等于list-max-ziplist-entries配置(默认512个)。
  • 列表中每个元素的长度小于或等于list-max-ziplist-value配置(默认64字节)

在这里插入图片描述
ZipList虽然节省内存,但申请内存必须是连续空间,如果内存占用较多,申请内存效率很低。所以我们必须限制ZipList的长度和entry大小。当list中的元素大小超过list-max-ziplist-value或者元素个数超过list-max-ziplist-entries,就会使用quciklist编码,将一个list拆分成多个小的ziplist,ziplist作为quicklist的节点,由quicklist来控制多个ziplist之间的联系。
配置参数:

  • list-max-ziplist-size:控制着每个ziplist的最大长度,超过这个长度时,Redis会创建新的ziplist。
  • list-compress-depth:控制着压缩的层数,即在进行列表操作时,Redis会从外到内压缩多少层ziplist。

quickList结构如下:

  • head:quickListNode头节点
  • tail:quickListNode尾节点
  • count:所有的ziplist中的entry总数量
  • len:ziplist节点数量
  • fill:ziplist中entry的上限,默认为-2
  • compress:首尾节点不压缩的个数,若设置为1,则首尾节点各有一节点不压缩,中间节点压缩,中间节点压缩为0x4…等乱码,读的时候需要解压缩;写的时候需要重新压缩;设置为0,则代表所有节点不压缩

quickListNode结构如下:

  • prev: 前一个节点指针
  • next:后一个节点指针
  • zl: 当前节点ziplist指针
    具体结构如图
    在这里插入图片描述

使用场景

  1. Stack(栈) = LPUSH + LPOP
  2. Queue(队列)= LPUSH + RPOP
  3. Blocking MQ(阻塞队列)= LPUSH + BRPOP

常用命令

LPUSH key value [value ...]//将一个或多个值value插入到key列表的表头(最左边)
RPUSH key value [value ...]//将一个或多个值value插入到key列表的表尾(最右边)
LPOP key//移除并返回key列表的头元素
RPOP key//移除并返回key列表的尾元素
LRANGE key  start stop//返回列表key中指定区间内的元素,区间以偏移量start和stop指定
BLPOP key [key ...] timeout//从key列表表头弹出一个元素,若列表中没有元素,阻塞等待					timeout秒,如果timeout=0,一直阻塞等待
BRPOP key [key ...] timeout//从key列表表尾弹出一个元素,若列表中没有元素,阻塞等待					timeout秒,如果timeout=0,一直阻塞等待

set (无序集合)

set是一个无序的、自动去重的集合数据类型,它的基本数据结构就是value为null的dict,当set中存储的元素都是整型,且set中元素的个数没有超过set-max-intset-entries(默认512,intset能存储的最大元素个数)的大小,则set集合编码为intset结构。

使用场景

  1. 抽奖
  2. 微信朋友圈点赞用户列表
  3. 微博我关注的人、共同关注、朋友推荐

常用命令

SADD key member [member ...]//往集合key中存入元素,元素存在则忽略,若key不存在则新建
SREM key member [member ...]//从集合key中删除元素
SMEMBERS key//获取集合key中所有元素 点赞用户列表
SCARD key//获取集合key的元素个数 点赞数量
SISMEMBER key member//判断member元素是否存在于集合key中 是否点赞
SRANDMEMBER key [count]//从集合key中选出count个元素,元素不从key中删除 单次抽奖
SPOP key [count]//从集合key中选出count个元素,元素从key中删除 一等奖 二等奖 三等奖 且不能重复获奖的情况
SINTER key [key ...]//交集运算 共同关注
SINTERSTORE destination key [key ..]//将交集结果存入新集合destination中
SUNION key [key ..]//并集运算
SUNIONSTORE destination key [key ...]//将并集结果存入新集合destination中
SDIFF key [key ...]//差集运算 朋友推荐 
SDIFFSTORE destination  key [key ...]//将差集结果存入新集合destination中

Sort set (有序集合)

sortset是一个有序且去重的集合,当数据量较小时使用ziplist结构编码,当到达一定数量时使用dict+skiplist的结构去实现。
ziplist结构编码使用条件

  • 元素数量小于zset_max_ziplist_entries,默认值128
  • 每个元素都小于zset_max_ziplist_value,默认值64byte

ZipList本身没有排序功能,而且没有键值对的概念,因此存储sortset时score和element是紧挨在一起的两个entry,element在前,score在后,Score越小越接近队首,score越大越接近队尾,按照score值升序排列。
skiplist基于score进行排序,每个节点存有索引数组,所以有多个索引层级,优先从最高的层级查询,依次往下级索引查询,每个节点都有指针(每个指针的跨度可能不同)指向上一个和下一个节点,所以skiplist还是一个双向链表,具体结构如图:

  • 逻辑图:
    在这里插入图片描述
  • 实际结构:
    在这里插入图片描述

使用场景

  1. 排行榜,例如微博热搜top10

常用命令

ZADD key score member [[score member]]//往有序集合key中加入带分值元素
ZREM key member [member …]//从有序集合key中删除元素
ZSCORE key member//返回有序集合key中元素member的分值
ZINCRBY key increment member//为有序集合key中元素member的分值加上increment 
ZCARD key//返回有序集合key中元素个数
ZRANGE key start stop [WITHSCORES]//正序获取有序集合key从start下标到stop下标的元素
ZREVRANGE key start stop [WITHSCORES]//倒序获取有序集合key从start下标到stop下标的元素
ZUNIONSTORE destkey numkeys key [key ...]//并集计算
ZINTERSTORE destkey numkeys key [key …]//交集计算

bitmap(位图)

基于string类型实现,相当于是对一个二进制的字符串进行操作,每个位都表示一个数据,0和1表示不同的两种状态。

使用场景

  1. 布隆过滤器:将数据经过哈希算法和取模,最终数据可以落在bitmap的某一个位上,将这个位设为1,查询时如果存在,可能因为哈希冲突的问题不能确认当前记录是否存在,但是如果获取不到则一定不存在,可用于缓存穿透问题中对大部分系统内没有的数据进行过滤
  2. 用户登录:一个string最大长度是512MB,则一个bitmap最大可以记录一天内51210241024*8 = 4294967296个用户的登录情况

常用命令

setbit key offset value//对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。位的设置或清除取决于 value 参数,可以是 0 也可以是 1 。当 key 不存在时,自动生成一个新的字符串值。未设置的位置以 0 填充。
getbit key offset //对 key 所储存的字符串值,获取指定偏移量上的位(bit)。当 offset 比字符串值的长度大,或者 key 不存在时,返回 0
bitop operation destkey key [key ...]//对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种
bitop and destkey key [key ...]//获取指定的天数都有登录的用户
bitop or destkey key [key ...]//指定天数中登录过的用户
bitcount key [start] [end]//计算给定字符串中,被设置为 1 的比特位的数量。

HyperLogLog(超日志)

HyperLogLog(HLL)是一种概率数据结构,用于估计集合中唯一元素的数量,而不需要存储这些元素本身。

使用场景

  1. 可用于统计大数据量且不重复的数据,例如一个页面的用户访问量,用户需要去重的情况

常用命令

PFADD key elemet [elemet...]# 添加元素到HyperLogLog
PFCOUNT key#获取HyperLogLog的基数估计值
PFMERGE destkey key[key...]# 合并多个HyperLogLog

Geospatial(地理空间)

地图经纬度信息,基于Sort set实现,存入时会将经纬度通过GeoHash算法转换为整数作为score存入sort set

使用场景

  1. 计算两点之间的距离
  2. 附件的人

常用命令

GEOADD key longitude latitude member [longitude latitude member ...]//将一个或多个指定的地理位置(经度和纬度)添加到给定的键(key)中。
GEOPOS key member [member ...]//返回一个或多个在键(key)中已存储的地理位置的坐标(经度和纬度)。
GEODIST key member1 member2 [m|km|mi|ft]//计算两个地理位置之间的距离。
GEORADIUS key longitude latitude radius m|km|mi|ft [WITHCOORD] [WITHDIST]//返回给定中心坐标和半径内的所有地理位置。
GEORADIUSBYMEMBER key member radius m|km|mi|ft [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]//返回与给定位置名称为中心的指定半径内的所有地理位置。
GEOHASH key member [member ...]//返回一个或多个位置对象的Geohash表示。

Stream (流)

  1. redis最早可以使用list来实现消息队列,但是list只能由一个消费者消费,不能广播。
  2. 之后redis引入了发布/订阅模式,l引入一个概念叫channel,生产者通过publish接口投递消息时会指定channel,消费者通过subscribe接口订阅它关心的channel,调用subscribe后这条连接会进入一个特殊的状态,通常不能在发送其他请求,当有消息投递到这个channel时Redis服务端会立刻通过该连接将消息推送到消费者。这里一个channel可以被多个应用订阅,消息会同时投递到每个订阅者,做到了消息的广播。另一方面,消费者也可以订阅一批channel。这解决了List结构单播的局限,允许一个消息被多个客户端接收。但是pub/sub模式中,是不管消费者是否接受到消息的,发完广播消息就删掉了,所以容易出现消息丢失的问题。
  3. redis在5.0之后引入了stream,提供了消息持久化,消费者群组,消费者待确认消息列表以及消费者确认消息的功能
  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值