简介
redis共包含八种数据类型:
数据类型 | type |
---|---|
string | 字符串 |
list | 链表 |
hash | 哈希表 |
set | 无序集合 |
sorted set | 有序集合 |
geospatial | 地理空间 |
hyperloglogs | 计数统计 |
bitmaps | 位操作 |
根据官网,redis包含了五种基础类型和三种特殊类型,本系列着重介绍redis的五种基本类型数据,本篇章以问题的答疑的形式先介绍有关redis存储相关概念,具体存储原理后续分篇章介绍
什么是Redis
- 基于内存的远程字典服务,以key-value方式存储数据(NoSQL数据库),由C语言编写
- key,value最大存储容量 512M
- db之间不是完全隔离,可以直接清除所有db数据
什么是dictEntry
redis是字典服务,数据存储是以key-value形式
每个key- value都是以dictEntry来存储的
存储源码如下:
typedef struct dictEntry {
void *key;//string,通过SDS存储
union {
void *val;// 指向redisObject
uint64_t u64;
int64_t s64;
} v;
struct dictEntry *next;
} dictEntry;
解析:
key是字符串,用SDS存储
val:指向redisObject,在五大常用基本类型中,val都是用redisObject存储
假设value也是个字符串,那么会接着指向一个SDS
什么是redisObject
可简单理解为存储所有数据类型的一个容器或者一个封装好的数据结构,源码解析如下:
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:REDIS_LRU_BITS;
int refcount;
void *ptr;
} robj;
解析:
属性 | 含义 |
---|---|
type | (4字节)对外暴露的类型名,例如:string list等等 |
encoding | 底层存储结构,实际使用的编码,例如:int,embstr,raw |
lru | 内存回收策略 |
refcount | 当前对象的引用计数,为0时可进行内存回收 |
*ptr | 指向真正的数据结构,例如:指向value的SDS |
什么是SDS
- simple dynamic string: 简单动态字符串
typedef struct sdshdr{X}{// X可以为5 8 16 32 64含义是2的X次方
uint{X}_t len;
uint{X}_t alloc;
unsingned char flags;
char buf[];
}
解析:
新版本redis中,sds有五种类型
sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64
len:存储的长度
alloc:分配的内存
flags: 选用的类别标记,是 8 16 还是32
buf:真正存储字符串的序列
为什么要有SDS
- C语言中没有字符串,只能用字符数组实现字符串char[]
- 字符数组可能会出现内存溢出(数组不可变,高级语言中会自动扩容实现动态数组,例如:java中的arraylist和go中的slice)
- 获取长度,需要统计数组,时间复杂度O(n)
- 当出现元素变化时,需要重新创建字符串,存在重新分配内存情况
- 以\0来标记字符串的结尾,如果存入二进制数据包含\0,可能会出问题(二进制不安全)
- redis为解决以上问题,新创建了一种内置数据结构SDS
- 长度发生变化,会自动扩容
- 带有len属性,获取长度的复杂度为O(1)
- 有空间与分配和惰性回收策略(删除并不是直接回收内存,而是让其为空字符,等待将来使用)
- 根据len判断字符是否结束,而不再是根据\0判断(二进制安全)
基础类型存储简介
数据结构总结
对象 | type输出 | encoding输出 |
---|---|---|
string | string | int,embstr,raw |
list | list | quicklist |
hash | hash | ziplist,hashtable |
set | set | intset,hashtable |
sorted set | sorted set | ziplist,skiplist |
底层编码总结
对象 | 原始编码 | 升级编码 | 升级条件 |
---|---|---|---|
string | int | embstr,raw | 整数并且小于2^63-1 情况下使用原始编码 |
hash | ziplist | hashtable | 键和值都小于64byte并且键值对个数不超过512个情况下使用原始编码 |
list | quicklist | quicklist | - |
set | intset | hashtable | 元素是整数,并且数量小于512个情况下使用原始编码 |
sorted set | ziplist | skiplist | 元素数量不超过128个,并且每个长度都小于64byte情况下,使用原始编码 |
redis为什么这么快
- 纯内存key-value存储,时间复杂度O(1)
- 单线程优势
- 节省了创建和销毁线程的消耗
- 避免上下文切换的消耗
- 避免资源竞争问题
- 异步非阻塞I/O 多路复用(后续原理解析中会提到)
什么是LRU,LFU策略
- LRU:Least Recently Used 最近最少使用原则
- 通过维护一个24位的全局时钟,新增对象会把全局时钟赋给对象的内部时钟,每隔一定时间更新全局时钟,执行lru时对比全局时钟和内部时钟,差值最大的进行淘汰,194是临界点(24位秒级别时钟只能存194天)超过194天,算法逻辑会改成加法求最久的key
- LFU:Least Frequently Used 最少使用原则
- 用时钟和计数器(基于概率的对数计数器)双重算法来淘汰最不频繁使用key对象,具体衰减算法后续系列会详细讲解
redis持久化机制有哪些
- RDB :默认的持久化机制,采用dump.rdb快照形式
- AOP:append only file;默认不开启
- 类似于bin log,以事件的方式恢复redis的命令(会压缩redis的命令)