Redis-底层的数据结构

Dcr 1年前 ⋅ 963 阅读

string

Redis命令

0-0.png

命令说明备注
set key value设置键值对
get key通过键获取值
del key通过key,删除键值对删除命令,返回删除数
strlen key求key执行字符串长度
getset key value修改原来key的对应值,并将旧值返回如果原来值为空,则返回空
getrange key start end获取子串既字符串长度为len,把字符串看作一个数组,而Redis是以0开始计数的,所以start和end的取值范围0到len-1
append key value将新的字符串value加入到原来key指向的字符串返回key指向新字符串的长度

string的内部编码

  • int 8个字节的长整型
  • embstr 小于等于39个字节的字符串
  • raw 大于39个字节的字符串

Redis会根据当前值的类型和长度决定使用那种内部编码实现

list

有两种实现方式:

  • 压缩链表

压缩列表(ziplist)是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,

一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。重点是内存连续.

  • 双端链表 prev和next两个指针 , 重点是可以从前往后也可以从后往前 , 这就可以实现lpush rpush这些指令了

因为用的链表 , 所以这也就导致了lindex指令 , 获取某个索引值的元素 , 需要遍历链表才可以获取到 , 时间复杂度是 O(n)

当列表对象可以同时满足下列两个条件时,列表对象采用压缩链表编码:

(1)列表对象保存的所有字符串元素的长度都小于64字节;

(2)列表元素保存的元素数量小于512个;

以上两个条件的上限值可以在配置文件中修改 list-max-ziplist-value选项和 list-max-ziplist-entries选项

否则采用双端链表编码

redis3.2版本以后采用的快速列表

quicklist 是一个双向链表,并且是一个ziplist的双向链表,也就是说quicklist的每个节点都是一个ziplist。结合了两者的优点

hash

概念

Redis 中哈希结构就如同 Java 的 map 一样 , 一个对象里面有许多键值对,它是特别适合存储对象的.

如果内存足够大 ,那么一个 Redis 的 hash 结构可以存储2的32次方-1个键值对 ( 40多亿)。

在 Redis 中, hash 是一个 String 类型的 field 和 value 的映射表,因此我们存储的数据实际在 Redis 内存中都是一个个字符串而己。

hash 结构命令

1-1.png

命令说明备注
hdel key feild 1[field2 ...]删除hash结构中的某个(些)字段可以进行多个字段的删除
hexists key field判断hash结构中是否存在field字段存在返回1,否则返回0
hgetall key获取所有hash结构中的键值返回键和值
hincrby key field increment指定给hash结构中的某一字段加上一个整数要求该字段也是整数字符串
hincrbyfloat key field increment指定给hash结构中的某一字段加上一个浮点数要求该字段是数字型字符串
hkeys key返回hash中所有键
hlen key返回hash中键值对的数量
hmget key field 1 [field2 ...]返回hash中指定的键的值,可以是多个依次返回值
hmset key field1 value1 [field2 value2 ...]hash结构设置多个键值对
hset key field value在hash结构中设置键值对单个设置
hsetnx key field value当hash结构中不存在对应的键,才设置值
hvals key获取hash结构中所有的值

在Redis中的hash结构和字符串有着比较明显的不同.

  • 命令都是以h开头,代表操作的是hash结构
  • 大多数命令多了一个层级field,这是hash结构的一个内部键,也就是说Redis需要通过key索引到对应的hash结构,再通过field来确定使用hash结构的哪个键值对

注意事项

  • hash结构的大小,如果hash结构是个很大的键值对,那么使用它就要十分注意.尤其是关于hkeys,hgetall,hvals等返回所有hash结构数据的命令,会造成大量数据的读取.需要考虑性能和读取数据大小对JVM内存的影响
  • 对于数字的操作命令hincrby而言,要求存储的也是整数型字符串
  • 对于hincrbyfloat而言,则要求使用浮点数或整数,否则命令失败
  • 在Redis中,hash是一个String类型的field和value的映射表.Spring对Redis进行了封装,所以有必要对RedisTemplate的配置项进行修改.修改defaultSerializer-ref.

linked-list

概念

  • 链表结构是Redis中一个常用的结构,它可以存储多个字符串
  • 它是有序的
  • 能够存储2的32次方减一个节点(超过40亿个节点)
  • Redis链表是双向的,因此即可以从左到右,也可以从右到左遍历它存储的节点
  • 链表结构查找性能不佳,但是插入和删除速度很快

由于是双向链表,所以只能够从左到右,或者从右到左地访问和操作链表里面的数据节点。 但是使用链表结构就意味着读性能的丧失,所以要在大量数据中找到一个节点的操作性能是不佳的,因为链表只能从一个方向中去遍历所要节点,比如从查找节点 10000 开始查询,它需要按照节点1 、节点 2、节点 3……直至节点 10000,这样的顺序查找,然后把一个个节点和你给出的值比对,才能确定节点所在。如果这个链表很大,如有上百万个节点,可能需要遍历几十万次才能找到所需要的节点,显然查找性能是不佳的。

链表结构的优势在于插入和删除的便利 ,因为链表的数据节点是分配在不同的内存区域的,并不连续,只是根据上一个节点保存下一个节点的顺序来索引而己,无需移动元素。

因为是双向链表结构,所以 Redis 链表命令分为左操作和右操作两种命令,左操作就意味着是从左到右,右操作就意味着是从右到左。

1-3.png

命令说明备注
lpush key node1 ...把节点node1加入到链表最左边如果是node1,node2...node n这样加入,那么链表开头从左到右就是 node n... node2 node1
rpush key node1 ...同上只是方向相反
lindex key index读取下标为index 的节点返回节点字符串,从0开始
llen key返回链表长度返回链表节点数
lpop key删除左边第一个节点,并将其返回
rpop key同上方向相反
linsert key before/after pivot node插入一个节点node,并且可以指定在值为pivot的节点前面或后面如果list不存在报错,如果没有对应的值pivot也会插入失败返回-1
lpushx list node如果存在key为list的链表,则插入节点node,并且作为从左到右的最后一个节点如果list不存在则报错
rpushx list node同上方向相反
lrange list start end获取链表list 从start下标到end下标的节点值包含start和end
lrem list count value如果count为0,则删除所有值等于value的节点,count不为0,则先对count取绝对值,假设记为abs,然后从左到右删除不大于abs个等于value的节点注意,count为整数
lset key index node设置列表下标为index的节点为node
ltrim key start stop修剪链表,只保留start到stop的区间的节点,其余的都删除掉包含start和end的下标

对于很多个节点同时操作的,需要考虑其花费的时间,链表数据结构对于查找而言并不适合于大数据。我们需要考虑插入和删除内容的大小,因为这将是十分消耗性能的命令,会导致 Redis 服务器的卡顿 。对于不允许卡顿的一些服务器,可以进行分批次操作,以避免出现卡顿。

set

概念

Redis 的集合不是一个线性结构,而是一个哈希表结构,它的内部会根据 hash 分子来存储和查找数据,理论上一个集合可以存储2的32次方减一(约42亿)个元素。

因为采用哈希表结构,所以对于 Redis 集合的插入、删除和查找的复杂度都是 0(1),只是需要注意 3 点

  • 对于集合而言,它的每一个元素都是不能重复的,当插入相同记录的时候会失败
  • 集合是无序的
  • 集合的每一个元素都是String数据结构类型

1-2.png

Redis的集合可以对于不同的集合进行操作,比如求出两个或者以上集合的交集,差集和并集等

命令说明备注
sadd key member1 ...给键为key的集合增加成员可以同时增加多个
scard key统计键为key的集合成员数
sdiff key1 [key2]找出两个集合的差集参数如果是单个key,那么Redis就返回这个key的所有元素
sdiffstore des key1 [key2]先按sdiff命令的规则,找出key1和key2的差集,然后将其保存到des集合
sinter key1 [key2]求key1和key2的交集参数如果是单key,那么Redis就返回这个key的所有元素
sinterstore des key1 key2先按sinter命令的规则,找出key1和key2的交集,然后保存到des中
smembers key返回集合所有成员
smove src des member将成员member从集合src迁移到集合des中
spop key随机弹出集合的一个元素注意其随机性,因为集合是无序的
srandmember key [count]随机返回集合中一个或者多个元素,count为限制返回总数,如果count为负数,则先求其绝对值count为整数,默认为1,如果count大于等于集合总数,则返回整个集合
srem key member1 ....移除集合中的元素,可以是多个元素对于很大的集合可以通过它删除部分元素,避免大量数据引发Redis停顿
sunion key1 [key2]求两个集合并集参数如果是单key,那么Redis就返回这个key的所有元素
sunionstore des key1 key2先执行sunion命令求出并集,然后保存到键为des的集合中

上述命令的前缀都包含 了 一个 s,用来表达这是集合的命令 , 集合是无序的 , 并且支持并集 、 交集和差集的运算。

zset

概念

有序集合和集合类似,只是说它是有序的,和无序集合的主要区别在于每一个元素除了值之外,它还会多一个分数。

  • 分数是一个浮点数,在 Java 中是使用双精度表示的,根据分数, Redis 就可以支持对分数从小到大或者从大到小的排序
  • 和无序集合一样,对于每一个元素都是唯一的 ,但是对于不同元素而言,它的分数可以一样
  • 元素也是 String 数据类型,也是一种基于 hash 的存储结构。
  • 集合是通过哈希表实现的,所以添加、删除、 查找的复杂度都是 0(1)
  • 集合中最大的成员数为 2的32次方减 1 ( 40 多亿个成员)

有序集合是依赖 key 标示它是属于哪个集合,依赖分数进行排序,所以值和分数是必须的,而实际上不仅可以对分数进行排序,在满足一定的条件下,也可以对值进行排序 。

Redis有序集合的部分命令

有序集合和无序集合的命令是接近的,只是在这些命令的基础上,会增加对于排序的操作,这些是我们在使用的时候需要注意的细节.

有些时候 Redis 借助数据区间的表示方法来表示包含或者不包含,比如在数学的区间表示中[2,5 ]表示包含 2,但是不包含 5 的 区间。

1-4.png

命令说明备注
zadd key score1 value1 [score2 value2 ...]向有序集合的key,增加一个或多个成员如果不存在对应的key,则创建键为key的有序集合
zcard key获取有序集合的成员数
zcount key min max根据分数分会对应的成员列表min最小值,max最大值,默认包含min和max,采用数据区间表示法,如果需要不包含则在分数前加"("不支持"["
zincrby key increment member给有序集合成员值为member的分数增加increment
zinterstore desKey nurnKeys key1 ...求多个有序集合的交集,并且将结果保存到des key中numkeys是一个整数,表示多少个有序集合
zlexcount key min max求有序集合key成员值在min和max的范围这里范围为key的成员值,Redis借助数据区间表示法
zrange key start stop[withscores]按照分值的大小从小到大返回成员,加入start和stop参数可以截取某一段返回.如果输入withscores,则连同分数一起返回这里既集合最大长度为len,Redis会将集合排序后,形成一个从0到len-1的下标,然后根据start和stop控制的下标(包含start和stop)返回
zrank key member按从小到大求有序集合的排行排名第一为0,第二为1...
zrangebylex key min max[limit offset count]根据值的大小,从小到大排序,min为最小值,max为最大值;limit选项可选,当Redis求出范围集合后,会产生下标0到n,然后根据偏移量offset和限定返回数count,返回对应成员这里范围为key的成员值,redis借助数学区间的表示方法.
zrangebyscore key min max [withscores] [limit offset count]根据分数大小,从小到大取值范围
zremrangebyscore key start stop根据分数区间进行删除根据score进行排序,然后排除0到len-1的下标,然后根据start和stop进行删除
zremrangebyrank key start stop按照分数排行从小到大的排序删除,从0开始计算
zremrangebylex key min max按照值的分布进行删除
zrevrange key start stop[withscores]从大到小的按照分数排序与zrange相同,只是排序方式相反
zrevrangebyscore key max min [withscores]按从大到小的排序,求元素排行
zscore key member返回成员的分数值
zunionstore desKey numKeys key1 ...求多个有序集合的并集,其中numKeys 是有序集合的个数

在对有序集合、下标、区间的表示方法进行操作的时候,需要十分小心命令,注意它是操作分数还是值,稍有不慎就会出现问题。

HyperLogLog

概念

基数是一种算法。

举个例子 , 一本英文著作由数百万个单词组成,你的内存却不足以存储它们,那么我们先分析一下业务。英文单词本身是有限的,在这本书的几百万个单词中有许许多多重复单词 ,扣去重复的单词,这本书中也就是几千到一万多个单词而己,那么内存就足够存储它们 了。比如数字集合{1,2,5,7,9, 1,5,9 }的基数集合为{ 1,2,5,7,9}那么基数(不重复元素)就是 5 , 基数的作用是评估大约需要准备多少个存储单元去存储数据,但是基数的算法一般会存在一定的误差(一般是可控的)。

Redis 对基数数据结构的支持是从版本 2.8.9 开始的。

基数并不是存储元素,存储元素消耗内存空间比较大,而是给某一个有重复元素的数据集合( 一般是很大的数据集合〉评估需要的空间单元数,所以它没有办法进行存储 ,加上在工作中用得不多 ,所以简要介绍一下 Redis的HyperLogLog 命令就可以了.

Redis的Hyperloglog命令

命令说明备注
pfadd key element添加指定元素到HyperLogLog中如果已经存在,则返回0,添加失败
pfcount key返回HyperLogLog的基数值
pfmerge desKey key1 ...合并多个HyperLogLog,并将其保存在desKey中

全部评论: 0

    我有话说: