Redis常用数据结构操作与底层原理

Posted 白龙码~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis常用数据结构操作与底层原理相关的知识,希望对你有一定的参考价值。

文章目录

Redis常用数据结构操作与底层原理

一、库

Redis下有16个数据库,分别名为0~15,彼此保存的值互不干扰。

  • select dbno:选择dbno数据库。
  • flushdb:清空当前数据库的内容。
  • flushall:清空所有数据库的内容。

二、键

  • del key:删除键。
  • exists key:查看键是否存在。
  • keys:查看当前数据库下有哪些键。
  • expire key seconds:给key设置seconds秒后过期。
  • ttl key:查看键的剩余生存时间(以秒为单位),-1表示永久存活,-2表示已过期,过期的键会被删除。
  • persist key:移除键的过期时间,让其永久存活。
  • type key:查看键对应值的数据类型。
  • randomkey:从当前数据库随机返回一个键。
  • rename key newkey:给键改名。

三、核心数据类型

1、string

底层使用简单动态字符串(SDS)。

  • set key value:设置键值对,如果键已存在,则修改其对应值。
  • setnx key vlaue:只有键不存在时才设置键值对。
  • get key:获取键对应的值。
  • mget key1[ key2 …]:获取多个键对应的值。
  • incr key:将key对应的整型值加一,如果是非整型值则报错。
  • incrby key increment:将key对应的整型值加increment。
  • decr key:将key对应的整型值减一。
  • decrby key decrement:将key对应的整型值减decrement
  • append key value:将value追加到key对应值的后面,如果key不存在,则先创建一个空值,再追加。

2、list

每个元素都小于64字节,且元素个数小于512时,使用压缩列表,否则使用双链表。

  • lpush key value1[ value2 …]:将一个或多个值按顺序头插到key对应的列表中。
  • rpush key value1[ value2 …]:将一个或多个值按顺序尾插到key对应的列表中。
  • lpop key:移除并获取列表的第一个元素。
  • rpop key:移除并获取列表的第一个元素。
  • lrange key start stop:获取下标范围为[start , stop]的值。下标从0开始-1表示最后一个元素,-2表示倒数第二个,以此类推。
  • llen key:获取key对应列表的长度。
  • lindex key index:获取列表中下标为index的元素。
  • linsert key before/after pivot value:在值为pivot的节点前插入value。

3、set

当元素都是整型,且不超过512个时,使用整数集合,否则使用哈希表。

  • sadd key value1[ value2, …]:将一个或多个值插入到key对应的集合中。
  • sismember key value:判断value是否在集合key中。
  • smembers key:返回集合key中的所有值。
  • scard key:获取集合中的元素个数。
  • sunion key1 [key2 …]:返回多个集合的并集。
  • sinter key1 [key2 …]:返回多个集合的并集。
  • sdiff key1 [key2 …]:返回第一个集合与其它集合的差集。
  • spop key:移除集合key中的一个随机元素。
  • smove source destination value:将集合source中的value转移到集合destination中。
  • srem key value1 [value2 …]:将一个或多个值从集合中删除。

4、hash

当每个元素都小于64字节,且元素个数小于512时,使用压缩列表(键值对相邻存储),否则使用哈希表。

  • hset/hmset key field value [field2 value … …]:将一个或多个key中filed字段的值设为value。
  • hsetnx key field value [field2 value … …]:只有在field字段不存在时才将key中filed字段的值设为value。
  • hget key field:获取field字段的值。
  • hmget key field [field2 …]:获取多个字段的值。
  • hkeys key:获取哈希表key中的所有字段名。
  • hvals key:获取哈希表key中的所有值。
  • hdel key field [field2 …]:删除key中的一个或多个字段。
  • hexists key field:查看key中指定字段是否存在。
  • hgetall key:获取key中所有字段及其对应值。
  • hincrby key field increment:将field字段对应值加上increment。

5、zset

当每个元素都小于64字节,且元素个数小于128时,使用压缩列表,否则使用跳表。

此外,使用了一个哈希表存储 成员:分数的映射,方便以O(1)的效率获取成员的对应分数。

  • zadd key score1 member1 [score2 member2 … …]:将一个或多个成员及其分数添加至key对应的zset中,如果成员存在则更新它的分数。
  • zcard key:获取成员数量。
  • zcount key min max:计算分数在[min, max]的成员数量。
  • zincrby key increment member:将member对应的分数加上increment。
  • zrank key member:获取指定成员的下标。
  • zrange key index1 index2 [withscores]:返回指定下标区间的成员[及其分数]。下标规则与list相同。
  • zrangebyscore key min max [withscores]:返回指定分数区间的成员[及其分数]。
  • zrem key member [member1 …]:删除一个或多个成员。
  • zscore key member:返回成员的分数。
  • zrevrange key index1 index2 [WITHSCORES]:返回指定索引区间内的成员,分数从高到低。

为什么zset使用跳表而非红黑树

  1. 跳表的代码更简单,容易实现。
  2. 对于区间查询,它们都可以通过O(logn)的效率找到起始点,但是链表的遍历比红黑树要方便,且效率略高。
  3. 跳表的插入删除只需要修改部分指针,不像红黑树那样需要调节颜色和左右旋转。

四、新增数据类型

1、bitmaps

bitmap,即位图,底层使用简单动态字符串来实现,可用作统计活跃用户等数据量大且只需标记的业务。

位图只能存储0/1值。

  • getbit key offset:获取在偏移量为offset处的值。
  • setbit key offset value:设置在偏移量为offset处的值。
  • bitcount key [start end]:获取位图中值为1的二进制位的数量(计算汉明重量)。strat和end可以指明在哪个字节范围
  • bitop and destkey key1 [key2 …]:对key1 [key2 …]位图执行与操作,并将结果保存至destkey中。
  • bitop or destkey key [key2 …]:对key1 [key2 …]位图执行或操作,并将结果保存至destkey中。
  • bitop not destkey key:对key位图执行非操作,并将结果保存至destkey中。
  • bitop xor destkey key1 [key2 …]:对key1 [key2 …]位图执行异或操作,并将结果保存至destkey中。

2、hyperloglog

hyperloglog用作基数统计,即一个集合中不重复元素的数量,在计算不重复用户页面访问量等情形时应用较多。

hyperloglog基于伯努利实验的概率统计算法,每个hyperloglog都占用12KB,但是能够对2^64个作基数统计,且误差率能控制在0.81%。

  • pfadd key element [element2, …]:添加成员,时间复杂度O(1)。
  • pfcount key [key2 …]:返回一个或多个hyperloglog并集的近似基数。key有多个时,该命令为O(n),单个key则为O(1)。
  • pfmerge destkey key [key2 …]:将一个或多个hyperloglog合并到destkey中。

3、geo

geographic,底层使用zset实现,用来存储经纬度信息。

注:Redis使用geohash算法将经纬度转为score值存储到zset中。

  • geoadd key longitude latitude member [longitude2 latitude2 member2 …]:添加一个或多个成员,并设置经度和纬度。
  • geopos key member:获取指定成员的经纬度。
  • geodist key member1 member2 [m|km|ft|mi]:获取两个成员的距离,单位可指定位米、千米、英尺、英里。
  • georadius key longtitude latitude radius [m|km|ft|mi]:获取以指定经纬度为圆心,radius为半径内的成员。
  • zrem key member:删除成员。

部分底层数据结构

一、压缩列表

压缩列表:

其中zlbytes记录了整个压缩列表的内存字节数,zltail记录了尾结点距离列表起始地址的偏移量,zllen记录了列表中有多少个节点。

注:由于保存前一个节点的偏移量,因此压缩列表的遍历是从后往前。

每个节点的构成如下:

通过当前节点起始地址 - prevoid_entry_length即可算出上一个节点的起始地址。

压缩列表是一种以时间换空间的数据结构,它的插入删除平均效率为O(n),但是在最坏情况下触发连锁更新,则效率为O(N2)。

连锁更新

当前一个节点的长度小于254字节,则prevoid_entry_length为1字节;

当前一个节点的长度大于等于254字节,则prevoid_entry_length为5字节;

因此,如果插入一个较大的节点,则可能导致下一个节点的prevoid_entry_length字节数增加。一旦增加的部分刚好使下一个节点超过254字节,则下下个节点又需要更改,以此类推。

如此一来,循环更新就会导致对压缩列表的增删效率降至O(n2)。

二、整数集合

整数集合以有序、无重复的方式保存集合元素。

数组元素的类型根据最大的元素而定,从int16_tint32_t再到int64_t。当新插入的元素大小超过当前数组类型时,则将整数集合升级成能存储新元素的数组类型,同时数组中的所有元素都要进行类型升级。

注:整数集合不支持降级。

由于要保持有序,因此对整数集合的增删皆为O(n),但是查找可以使用O(logn)的二分法。

相比于哈希表,整数集合是一种以时间换空间的做法。

以上是关于Redis常用数据结构操作与底层原理的主要内容,如果未能解决你的问题,请参考以下文章

Redis技术探索「底层架构原理」探索分析服务核心数据结构介绍和案例

Redis Cluster集群搭建Cluster集群扩缩容底层原理

Redis的常用命令和8大数据类型常用操作大全

Redis的常用命令和8大数据类型常用操作大全

redis 基础数据结构实现

Redis 内存压缩原理