redis各类型数据结构和底层实现源码分析

一、简介和应用
redis是一个由ANSI C语言编写,性能优秀、支持网络、可持久化的K-K内存数据库,并提供多种语言的API 。它常用的类型主要是 String、List、Hash、Set、ZSet 这5种 。

redis各类型数据结构和底层实现源码分析

文章插图
Redis在互联网公司一般有以下应用:
String:缓存、限流、计数器、分布式锁、分布式Session
Hash:存储用户信息、用户主页访问量、组合查询
List:微博关注人时间轴列表、简单队列
Set:赞、踩、标签、好友关系
Zset:排行榜
再比如电商在大促销时,会用一些特殊的设计来保证系统稳定,扣减库存可以考虑如下设计:
redis各类型数据结构和底层实现源码分析

文章插图
上图中,直接在Redis中扣减库存,记录日志后通过Worker同步到数据库,在设计同步Worker时需要考虑并发处理和重复处理的问题 。
通过上面的应用场景可以看出Redis是非常高效和稳定的,那Redis底层是如何实现的呢?
二、Redis的对象redisObject
当我们执行set hello world命令时,会有以下数据模型:
redis各类型数据结构和底层实现源码分析

文章插图
dictEntry:Redis给每个key-value键值对分配一个dictEntry,里面有着key和val的指针,next指向下一个dictEntry形成链表,这个指针可以将多个哈希值相同的键值对链接在一起,由此来解决哈希冲突问题(链地址法) 。
sds:键key“hello”是以SDS(简单动态字符串)存储,后面详细介绍 。
redisObject:值val“world”存储在redisObject中 。实际上,redis常用5中类型都是以redisObject来存储的;而redisObject中的type字段指明了Value对象的类型,ptr字段则指向对象所在的地址 。
redisObject对象非常重要,Redis对象的类型、内部编码、内存回收、共享对象等功能,都需要redisObject支持 。这样设计的好处是,可以针对不同的使用场景,对5中常用类型设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率 。
无论是dictEntry对象,还是redisObject、SDS对象,都需要内存分配器(如jemalloc)分配内存进行存储 。jemalloc作为Redis的默认内存分配器,在减小内存碎片方面做的相对比较好 。比如jemalloc在64位系统中,将内存空间划分为小、大、巨大三个范围;每个范围内又划分了许多小的内存块单位;当Redis存储数据时,会选择大小最合适的内存块进行存储 。
前面说过,Redis每个对象由一个redisObject结构表示,它的ptr指针指向底层实现的数据结构,而数据结构由encoding属性决定 。比如我们执行以下命令得到存储“hello”对应的编码:
【redis各类型数据结构和底层实现源码分析】
redis各类型数据结构和底层实现源码分析

文章插图
redis所有的数据结构类型如下:
redis各类型数据结构和底层实现源码分析

文章插图
三、String
字符串对象的底层实现可以是int、raw、embstr(上面的表对应有名称介绍) 。embstr编码是通过调用一次内存分配函数来分配一块连续的空间,而raw需要调用两次 。
redis各类型数据结构和底层实现源码分析

文章插图
int编码字符串对象和embstr编码字符串对象在一定条件下会转化为raw编码字符串对象 。embstr:<=39字节的字符串 。int:8个字节的长整型 。raw:大于39个字节的字符串 。
简单动态字符串(SDS),这种结构更像C++的String或者JAVA的ArrayList<Character>,长度动态可变:
structsdshdr{ // buf 中已占用空间的长度 intlen; // buf 中剩余可用空间的长度 intfree; // 数据空间 charbuf[]; // ’0’空字符结尾 };get:sdsrange---O(n)
set:sdscpy—O(n)
create:sdsnew---O(1)
len:sdslen---O(1)
常数复杂度获取字符串长度:因为SDS在len属性中记录了长度,所以获取一个SDS长度时间复杂度仅为O(1) 。
预空间分配:如果对一个SDS进行修改,分为一下两种情况:
1、SDS长度(len的值)小于1MB,那么程序将分配和len属性同样大小的未使用空间,这时free和len属性值相同 。举个例子,SDS的len将变成15字节,则程序也会分配15字节的未使用空间,SDS的buf数组的实际长度变成15+15+1=31字节(额外一个字节用户保存空字符) 。
2、SDS长度(len的值)大于等于1MB,程序会分配1MB的未使用空间 。比如进行修改之后,SDS的len变成30MB,那么它的实际长度是30MB+1MB+1byte 。


推荐阅读