redis
后续源码文件名称统一通过()表示,redis底层是C语言,因此.h、.c文件可认为是源码文件
源码版本 redis-6.0.5
redis全称:REmote DIctionary Service 译为远程字典服务
每个KV键值对都存储在dictEntry(dict.h)里面,redis底层是哈希表(hashTable),结构体现在dictEntry是数组,*next指针维护链表。
key是字符串结构,redis采用sds结构(sds.h)存储,由于底层是C语言编写,C语言没有String结构。
sds与char[]的区别:
- sds长度存储在len属性当中,无需再计算
- 内存分配不足导致溢出,sds会提前检查空间剩余量并进行分配
- 减少内存分配次数,sds修改后len变为10字节,而buf数组实际长度为21,10字节空余,1字节用于存储空字符串
- 二进制安全问题:C语言通过空字符串识别结束,像视频、图片、音频等中间内容包括了空字符,会导致解析失败
value存储在redisObject当中(server.h)
对象类型可以根据命令:type key查看
基本数据结构
String
基本介绍:
字符串类型的内部编码(对应redisObject中encoding存储的值)
可通过命令object encoding key查询编码
- int:(8字节 64位)
- embstr:sds的一种格式,存储小于44字节的字符串 ,redisObject和sds连续分配,修改时需要重新全部分配,因此设置为只读
- raw:存储大于44字节的字符串,redisObject和sds分别分配。
embstr以及raw的临界值可通过配置#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44实现(object.h)超过范围则自动转换类型,修改数据会转换成raw类型,大内存编码无法退化为小内存编码。
应用场景:
- 热点内容缓存
- 分布式session
- 分布式锁:setnx
- 全局id:incrby 项目上应用:全局msgId
- 原子性操作可应用的场景:限流、计数
Hash
value只能是字符串类型
基本介绍:
- 优点:减少内存空间、减少key冲突、批量获取减少IO
- 缺点:field无法单独设置过期时间、无bit操作、数据量分布问题(都分布在key所在节点)
- 基本操作指令:hset、hmset、hscan
实现原理:(底层由两种数据结构组成)
hash的底层由两种结构组成:ziplist和hashtable
-
ziplist压缩列表(ziplist.c):
通过当前节点的长度和上一个节点的长度计算出上一个节点的地址
使用场景:hash键值都小于64byte、键值对数量小于512个
可通过redis.conf配置转成哈希表的临界值:hash-max-ziplist-value、hash-max-ziplist-entries
当超过这两个阈值的时候转换为hashtable(哈希表)
编码格式:根据字节区分
-
hashtable哈希表(dict.h):redis底层哈希表可参照此结构
根据负载因子就是链表的长度(dict_force_resize_ratio=5),若大于,则触发rehash,扩容大小为当前库大小*2
rehash过程: redis-rehash过程是采用渐进式rehash即分多次、渐进式完成的
- 扩容大小h[1]为h[0]使用的数量*2,讲rehashidex索引计数器置0,开始rehash
- 程序进行crud的过程中外,还迁移rehashidex上的节点,重新计算hash值,迁移h[0]节点到h[1]当中,rehashidx+1
- rud操作(即除了新增操作)会两个哈希表同时操作,新增操作只操作h[1]表,保证h[0]中的数据只减不增,最后变成空表释放空间
应用场景:
- 用户的任务进度:key-用户id field:任务名称 value:任务进度
- 用户的购物车:key-用户id field:商品 value:商品数量
List
左右都可取出数据
实现原理:
3.2版本之后采用quicklist来存储
应用场景:
- 由于list是有序排列,因此可以作为时间线
- 消息队列:BLPOP/BRPOP指令,阻塞等待,有数据才弹出
Set
基本介绍:无序集合(最大存储40亿数据)
实现原理:根据数据类型用不同的数据结构
- 整数类型:inset存储
- 非整数类型:hashtable
应用场景:
- 漂流瓶等从集合随机抽取的活动
- 点赞、签到、喜欢的用户等:key-like+uid value-对象id
- 商品标签:通过交集并集
Zset
基本介绍:有序集合
实现原理:源码结构(server.h)
- 元素数量小于128,长度小于64字节用ziplist
- 超过阈值后,用skiplist+dict存储(跳跃表):跳跃表就是维护了多层数据,根据当前层判断是否需要到下层查找
应用场景:key score field
- score为时间戳,可以统计指定时间段内的数据
- zrevrange:获取排名
Bitmaps
基本介绍:位运算,8位2进制组成
常用指令:bitcount k1 统计二进制位中1的个数
应用场景:
- 用户访问统计:位运算
- 在线用户统计:维护串很长的二进制,用户根据id,在线就修改指定位置为1,统计可通过bitcount
Hyperloglogs
基本介绍:位运算,8位2进制组成
基本原理:与布隆过滤器有类似作用
应用场景:海量数据统计问题
Streams
基本介绍:原理类似kafka
应用场景:可实现发布订阅功能