码迷,mamicode.com
首页 > 其他好文 > 详细

redis-数据结构

时间:2021-04-28 11:49:51      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:detail   图形学   ring   更新   ref   解决   计数   hash   基本   

redis的基本数据结构用过的都比较了解。大多数也就是考察一下这些数据结构的特点和使用方法。因为redis就是个缓存层,而且基本不会去手改它。但是有的就会比较深入的问询一些更底层的东西,例如了解各种数据结构是怎么实现的么。问这种问题的一般都是对redis相当了解的。虽然考察到的概率比较小,但是还是知道一些比较好,而且这块东西并不难,有些数据结构基础的都比较容易理解。

redis底层的数据结构总共有八种,依靠这八种数据结构来实现redis所有数据类型。
1.整数,就是一个long型的基本数据结构,用于存放一个整形数值。
2.raw动态字符串,使用sds数据结构实现的,它的结构就是一个header结构体和一个char数组,header先被创建,然后创建char数组并赋值给header引用,所以需要两次内存分配。它可以存储大字符串,而且其根据不同长度定义多种结构而优化内存占用。
3.embstr字符串,结构类似于sds但是它的header和char数组是存放在同一个结构里面的,所以仅需要一次内存分配。他们可以放在一起是因为这个char数组比较小,与header连接起来一般最多占用64位,所以它能存储的字符串最多只有39字节。
4.linkedlist双端链表,这个就是一个链表结构,有头和尾两个元素的指针。redis中双端链表元素数量上限是2^32-1个(就是无符号int类型上限值)。
5.dict字典,提供字典结构,它的结构大致包含一个公共方法库引用(提供字典中各种算法,例如获取key的hash值函数等)、迭代器计数器、重新哈希索引值、有两个元素的哈希表数组。
5.1.首先重新哈希索引值的存在就说明该结构在重新哈希计算的时候不是一次性完成的,而是分步完成的,避免长时间卡顿。
5.2.其次它的哈希表有两个就是为了重新哈希使用的,0号哈希表就是正常使用的哈希表,1号的是重新哈希的时候临时存放的,当重新哈希完全结束的时候把1号元素赋值到0号元素并且清空。
5.3.哈希表的结构体就是比较常见的结构了。一个哈希实体数组组成。
5.4.哈希实体结构也是比较常见的结构,看到该结构就可以看到redis解决哈希冲突是使用拉链法解决的,用next指针指向下一个元素。
6.skiplist跳跃表,跳跃表的结构也是经常见到的数据结构,通过多个level(层)来加快查询速度。
7.intset整数集合,数据结构是一个编码方式、长度和int8类型的数组,但是它可以存储int16、int32类型,那么也就是用两个或者四个数组元素来存储数据即可,所以会涉及到编码升级。
8.ziplist压缩表,数据结构分为header和数据节点数组两部分,header里面存了占用内存大小、尾节点偏移量和元素个数(2字节,所以压缩列表最大也就是65535个元素);数据节点的结构主要就是自己的长度、编码类型、还有值的指针。它只能存储一些比较短的集合。

redis基本数据结构的实现
1.string类型,【整数、raw动态字符串、embstr字符串】若内容可以转换成为整数类型,则使用整数类型;长度<=39字节,则使用embstr类型;其他情况都是用raw动态字符串结构。
2.list类型,【linkedlist、ziplist】若存储的字符串元素长度小于64字节且元素数量小于512,则使用ziplist;其他情况都使用linkedlist双端列表。
3.hash类型,【dict字典、ziplist】若存储的所有键值对的键和值字符串元素长度小于64字节且键值对数量小于512,则使用ziplist;其他情况都使用dict字典。
4.set集合类型,【intset、dict字典】若集合所有元素都是整数且元素数量小于512,则使用intset;其他情况都是用dict字典实现。
5.zset有序集合类型,【ziplist、skiplist+dict】若集合保存的元素长度都小于64且元素数量小于128,则使用ziplist;其他情况都是用skiplist和dict结合的方式,就是每次加入元素,两种数据类型都添加,跳表的作用是根据分值查元素,字典的作用是根据元素查分值。

扩展
其他数据结构
bitmaps,位图在很多语言中都有类似的实现,特别是图形学里面,用它可以方便统计某些事情是否发生或者状态是否存在等。例如:记录某天电影《机器人总动员》是否被点播,则key为play20210426,机器人总动员计算完成后的偏移量为52(可以用自增主键作为偏移量,也可以用算法来计算出一个偏移量但是不能和其他的电影重复啊)。则有人点播的时候使用命令:setbit play20210426 52 1;获取是否点播过:getbit play20210426 52;统计当天有多少电影被点播过:bitcount play20210426。
它如果直接设置很高位的话会比较慢,因为要补0。
它的底层实现其实就是用字符串实现的,使用字符串来存储所有的bit数组,然后各种方法就是操作或者查询这个数组。

hyperloglog,基数统计,简单说就是往里面扔各种字符串,然后它统计出你总共扔进去多少个不同的字符串,例如: {1, 3, 5, 7, 5, 7, 8},最后的结果就是5。他的操作都是pf开头的:pfadd pfcount pfmerge等。但是它统计的不是绝对准确的。会有0.81%的误差。所以需要绝对准确就只能考虑使用set或者bitmap了。
它的底层实现原理看上去好像还挺复杂的。数据结构就是它分成了2^14个桶,每个桶有6bit所以可以记录整数最高到64。每次添加元素都是用hash函数计算出一个64bit的值,最低14位转为10进制就是桶的编号。剩下的50位从低位到高位数到第一个出现1的位数作为值,若大于现在桶的值则更新桶的值,否则不更新。最后统计所有桶的调和平均数就是这个结构的基数计数,由于这种统计方法,所以这个计数不是绝对准确的,但是在如此小的空间如此小的计算量这个误差还是可以接受的。(其实我也是看了半天才大概看的半懂吧,只能说是了解到方法了,但是真的用来写还是有难度的,作为一个思路扩展吧)

geo,地理位置计算,通过设置经纬度和地名,然后就可以做很多地理的计算,例如:计算两个点之间的距离 geodist,或者计算以某点为中心的固定半径内所有的点 georadius/georadiusbymember。看很多地图上的常用计算都有了吧。美团饿了么、共享单车、游戏地图行军、微信附近的人。
它的底层实现是使用zset数据结构,而且可以使用zset的相关命令来操作geo的数据,使用geohash的算法将经纬度转换为score存放在zset之中即可。

参考资料
Redis 概念以及底层数据结构
Redis的8种底层数据结构
Redis源码中hyperloglog结构的实现原理是什么?
GeoHash原理及redis geo相关操作
【十五】redis衍生数据结构之GEO

redis-数据结构

标签:detail   图形学   ring   更新   ref   解决   计数   hash   基本   

原文地址:https://www.cnblogs.com/ijay/p/14597762.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!