Redis数据结构底层原理及Redis 6 特性
1.Redis 基本特性
- 非关系型的键值对数据库,可以根据键以O(1) 的时间复杂度取出或插入关联值
- 数据是存在内存中的
- 键的类型可以是字符串,整型,浮点型等,且键是唯一的
- 值类型可以是string,hash,list,set,sorted set 等
- 内置了复制,磁盘持久化,LUA脚本,事务,SSL, ACLs,客户端缓存,客户端代理等功能
- 通过Redis哨兵和Redis Cluster 模式提供高可用性
2. 应用场景
- 计数器:可以对 String 进行自增自减运算,从而实现计数器功能。Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量
- 分布式ID生成:利用自增特性,一次请求一个大一点的步长如 incr 2000 ,缓存在本地使用,用完再请求
- 海量数据统计:位图(bitmap):存储是否参过某次活动,是否已读谋篇文章,用户是否为会员, 日活统计
- 会话缓存:可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性
- 分布式队列/阻塞队列:List 是一个双向链表,可以通过 lpush/rpush 和 rpop/lpop 写入和读取消息。可以通过使用brpop/blpop 来实现阻塞队列
- 分布式锁实现:在分布式场景下,无法使用基于进程的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁
- 热点数据存储:最新评论,最新文章列表,使用list 存储,ltrim取出热点数据,删除老数据
- 社交类需求:Set 可以实现交集,从而实现共同好友等功能,Set通过求差集,可以进行好友推荐,文章推荐
- 排行榜:sorted_set可以实现有序性操作,从而实现排行榜等功能
- 延迟队列:使用sorted_set,使用 【当前时间戳 + 需要延迟的时长】做score, 消息内容作为元素,调用zadd来生产消息,消费者使用zrangbyscore获取当前时间之前的数据做轮询处理。消费完再删除任务 rem key member
3.数据结构底层原理
3.1 String
redis 3.2 以前:
struct sdshdr {
int len;
int free;
char buf[];
};
redis 3.2 后:
typedef char *sds;
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
........
3.2 list
List是一个有序(按加入的时序排序)的数据结构,Redis采用quicklist(双端链表) 和 ziplist 作为List的底层实现
可以通过设置每个ziplist的最大容量,quicklist的数据压缩范围,提升数据存取效率
list-max-ziplist-size -2 // 单个ziplist节点最大能存储 8kb ,超过则进行分裂,将数据存储在新的ziplist节点中
list-compress-depth 1 // 0 代表所有节点,都不进行压缩,1, 代表从头节点往后走一个,尾节点往前走一个不用压缩,其他的全部压缩,2,3,4 ... 以此类推
3.3 Hash
Hash 数据结构底层实现为一个字典( dict ),也是RedisBb用来存储K-V的数据结构,当数据量比较小,或者单个元素比较小时,底层用ziplist存储,数据大小和元素数量阈值可以通过如下参数设置
hash-max-ziplist-entries 512 // ziplist 元素个数超过 512 ,将改为hashtable编码
hash-max-ziplist-value 64 // 单个元素大小超过 64 byte时,将改为hashtable编码
3.4 Set
Set 为无序的,自动去重的集合数据类型,Set 数据结构底层实现为一个value 为 null 的 字典( dict ),当数据可以用整形表示时,Set集合将被编码为intset数据结构。两个条件任意满足时,Set将用hashtable存储数据
1, 元素个数大于 set-max-intset-entries
2 , 元素无法用整形表示
set-max-intset-entries 512 // intset 能存储的最大元素个数,超过则用hashtable编码
3.5 ZSet
ZSet 为有序的,自动去重的集合数据类型,ZSet 数据结构底层实现为 字典(dict) + 跳表(skiplist) ,当数据比较少时,用ziplist编码结构存储
zset-max-ziplist-entries 128 // 元素个数超过128 ,将用skiplist编码
zset-max-ziplist-value 64 // 单个元素大小超过 64 byte, 将用 skiplist编码
4.Redis 6 特性
4.1 多线程
redis 6.0 提供了多线程的支持,redis 6 以前的版本,严格来说也是多线程,只不过执行用户命令的请求时单线程模型,还有一些线程用来执行后台任务, 比如 unlink 删除 大key,rdb持久化等
redis 6.0 提供了多线程的读写IO, 但是最终执行用户命令的线程依然是单线程的,这样,就没有多线程数据的竞争关系,依然很高效
redis 6.0 线程执行模式(配置):
io‐threads 4 // 这里说有三个IO线程,还有一个线程是main线程,main线程负责IO读写和命令执行操作
默认情况下,如上配置,有三个IO线程, 这三个IO线程只会执行 IO中的write 操作,也就是说, read 和 命令执行 都由main线程执行。最后多线程将数据写回到客户端。
开启配置:
io‐threads‐do‐reads yes // 将支持IO线程执行读写任务
4.2 client side caching 客户端缓存
redis 6 提供了服务端追踪key的变化,客户端缓存数据的特性,这需要客户端实现。
执行流程为, 当客户端访问某个key时,服务端将记录key 和 client ,客户端拿到数据后,进行 客户端缓存,这时,当key再次被访问时,key将被直接返回,避免了与redis 服务器的再次交 互,节省服务端资源,当数据被其他请求修改时,服务端将主动通知客户端失效的key,客户端 进行本地失效,下次请求时,重新获取最新数据。
4.3 ACL(对命令的访问和执行权限的控制,默认情况下,可以有执行任意的指令,兼容以前版本)
ACL设置方式:
命令方式: ACL SETUSER + 具体的权限规则, 通过 ACL SAVE 进行持久化
ACL SETUSER alice // 创建一个 用户名为alice的用户
对 ACL 配置文件进行编写,并且执行 ACL LOAD 进行加载
ACL存储有两种方式,但是两种方式不能同时配置,否则直接报错退出进程:
1.redis 配置文件: redis.conf
2.ACL配置文件, 在redis.conf 中通过 aclfile /path 配置acl文件的路径
还没有评论,来说两句吧...