Redis中的五种数据类型及其原理

redis的五种数据类型

string类型

string是redis中的一种最基本最常用的数据类型,它的value不仅可以是字符串,还可以是数字(以字符串的方式传入进去的),可以用decrement等操作进行加减,所以它还能用来做计数功能。

原理:
redis是用c语言实现的,但是在redis中的string并不等同于c语言中的string,它使用的是一个叫sds(Simple dynamic String)的数据结构来存储字符串。

sds是由在内存地址上相邻的两部分组成,一部分是header,一部分是字符数组。

header:在sds中一共有五种类型的header,除了sdshdr5结构只有flag和buf []以外,其余的四种结构sdshdr8、sdshdr16、sdshdr32、sdshdr64都有len、alloc、flags、buf [],而这四种,就是uint8、uint16、uint32、uint64的区别,那么这五种分别能够存放的字符串长度为25 、28、216、2 32、2 64

  1. 其中len表示已使用的buf数组字节数(不包括结束符),就是字符串的长度。
  2. alloc表示已申请的buf字节数组的最大容量,不包括结束符。
  3. flags是char类型的,它的低三位bit表示header的类型,高五位没有用到(sdshdr5中用来表示字符串长度)。
  4. buf []数组是char类型的,且没有指明长度,表示在header后面地址空间紧跟一个字符数组。

在sds中,它的指针指向的是header的末尾,buf []数组的开头,因此,想要获取到真正的字符串数据部分,就得先获取header,想要正确获取header就得先通过flags获取header类型。所以首先是将指针往前八位获取flags,获取了flags后确定header类型了就可以正确的获取header开头的位置,解析出len和alloc,这样就能在buf []数组中获取数据了。

为什么要使用sds来实现redis中的字符串?

  1. 可以存储任何的二进制数据。
    c语言本省没有字符串,只能通过char [] 数组来实现,sds中实际用来存数据的和它一样,都是字符数组来存的,不过他们的规则不一样。用c语言的实现规则一般是,字符串是以 ‘\0’ 结尾的,因此,它就只能存储文本数据。而sds中在字符数组前还带了一个header,用来定义自己的规则,虽然依然以 ‘\0’ 结尾,这样可以与c语言的字符串相互转换,但并不以 ‘\0’ 作为字符串结束符而结束而是以len判断长度来结束的,那么字符串中间就也可以带 ‘\0’ ,所以可以存储任何的二进制数据。

  2. 只通过char [] 数组的话,想要获取长度还得遍历整个数组,时间复杂度为O(n),但是sds自己有一个记录长度的len可以直接获取,时间复杂度为O(1)。

  3. sds可以实现空间预分配和惰性空间释放,防止过多的进行内存的再分配。

Hash类型

redis本身就是使用键值对来进行存取数据的,而Hash类型的意思就是redis中的值也为一个键值对,相当于是两层键值对。

所以他的应用场景一般是希望通过第一个key进行访问的,也能通过两个key一起进行修改和访问其中的一小条数据的,就用Hash类型。比如购物车数据,第一个key存用户id,里面Hash的key存商品id,既能通过用户id访问整个购物车数据,也能通过用户id和商品id访问购物车中某个商品信息,或者直接往购物车中添加删除商品等,就只需要操作单条商品信息,而不用修改整个购物车。

它在底层使用了ZipList和HashTable两种数据结构来实现。

List类型

List类型就是redis键值对中的值为List集合,由于List的插入是有顺序的,所以使用这个类型可以在一个key中存储多个数据,并且是按照存入数据时指定规则的顺序来存储的。

所以对某个key的多个操作如果需要体现一定的顺序性的,就用List类型。比如每条朋友圈下的点赞按顺序显示,比如知乎的粉丝显示按照顺序排序。然后还有它也可以作为消息队列使用。

它在底层使用了ZipList或者LinkedList两种数据结构来实现。

Set类型

Set类型的一个特性就是存入它里面的数据都是唯一的,不能是重复的,并且提供了求交集并集差集、判断是否在集合中、随机取数据的API,可以高效的进行数据的整理。

它的应用场景:用交集求共同好友,共同关注。用isMember判断黑白名单。用randomMember从某个类型的热点新闻中随机去一个推荐给用户等。

它在底层使用了IntSet或者HashTable两种数据结构来实现。

Zset类型

Zset是一个可以进行排序的Set集合,它不仅拥有Set数据不可重复的特性,还在数据上附带存储一个带有分数的字段score,在Zset中的所有数据都是根据score进行排序的。它可以随时调整score的值,并且能够根据分数去整理和获取数据。

它的应用场景:利用这个score的特性,建立各种排行榜,活跃度、亲密度排行等。

原理:
Zset在底层使用了ZipList或者是SkipList两种数据结构来实现存取数据。

下面简单看一下跳表SkipList:
redis跳表中的节点:每个节点中都包含一个成员对象,存储当前value,还有一个score分值,一个后退指针,还有当前层结构,层结构中又包含前进指针和跨度。

typedef struct zskiplistNode {

    // member 对象
    robj *obj;

    // 分值
    double score;

    // 后退指针
    struct zskiplistNode *backward;

    // 层
    struct zskiplistLevel {

        // 前进指针
        struct zskiplistNode *forward;

        // 这个层跨越的节点数量
        unsigned int span;

    } level[];

} zskiplistNode;

跳表是一种特殊的链表,它的查找、插入和删除的时间复杂度都是O(logN),它有以下特征:

  1. 一个跳表应该有几个层(level)组成,通常是10-20层,leveldb中默认为12层。
  2. 跳表的第0层包含所有的元素,且节点值是有序的。
  3. 每一层都是一个有序的链表,层数越高应越稀疏,这样在高层次中能’跳过’许多的不符合条件的数据。
  4. 如果元素x出现在第i层,则所有比i小的层都包含x;
  5. 每个节点包含key及其对应的value和一个指向该节点第n层的下个节点的指针数组x->next[level]表示第level层的x的下一个节点。

跳表做查找的时候,首先从最高层开始查起,由于每一层的数据依照节点score值递增排列,那么如果节点值小于查询值时会接着往这一层的下一个查询,如果节点值大于查询值的话,就会从前一个节点往下面一层跳下去查询,一直以这个规律查,直到查到为止。所以它才叫跳表。

之所以使用跳表而不使用红黑树的原因就在于,跳表的实现比较简单,并且插入的效率高,而红黑树由于需要各种平衡而调整结构,性能就会比较低。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值