redis存储原理与数据模型

文章目录

redis的线程

redis的源码用C编写,在vscode启动调试可以发现redis启动了多个线程:

redis-server

bio_close_file

bio_aof_fsync

bio_lazy_free

io_thd_1

io_thd_2

io_thd_3

jemalloc_bg_thd

redis的单线程指的是核心业务命令处理单线程

命令处理单线程

单线程的局限

不能有耗时操作:CPU运算,阻塞IO。

对于redis而言会影响性能。

但是仍然不采用多线程的原因:

加锁复杂,且粒度不好控制,频繁的CPU切换会抵消多线程的优势。

redis对耗时操作的处理

io密集型:

磁盘io:fork子进程,异步刷盘

网络io:多个cli,采用了reactor网络模型,或者数据请求返回的量比较大,开启io多线程
cpu密集型:

根据数据的存储特性有数据结构切换功能,分治的方式,渐进的数据迁移。

冲突

redis以hashtable的形式存储数据,选用的hash函数siphash,能够在原key比较接近的情况下,生成差距较大的hash值,让元素分布更加均匀。

有根据负载因子(used/size)的动态扩容机制

如果负载因子>1,翻倍扩容。

正在进行fork时会阻止扩容(rdb,aof复写等情况)。

若负载因子>5,会立即扩容。

原在0的元素在扩容后要么到0,要么到4。

当负载因子<0.1,会发生缩容,缩容的规则是恰好包含(向上取,10→16)used的2^n。

渐进式rehash

如果原始hashtable的size达到了2g,那么在翻倍扩容时,将会十分耗时。

步骤:将ht[0]中的元素重新经过hash函数生成64位整数,再对ht[1]长度取余,映射到ht[1]。

①其中这个迁移过程分摊在增删改查,每有一次请求,伴随指针++,迁移old到new中,直到迁移完毕,释放掉ht[0],ht[1]赋值给ht[0],ht[1]置为nil。

②redis空闲时,使用定时器。每次rehash 1ms。

scan

就算发生

采用高位进位加法的遍历顺序,rehash后的槽位在遍历顺序上是相邻的。scan的目的是从scan那一时刻开始起已经存在的redis中数据进行遍历,就算发生扩容 ,也不会重复和遗漏。

通过连续地调用 SCAN 并使用前一次返回的游标作为下一次的输入,可以遍历整个数据库中的所有键,而不会因为一次性加载太多数据而导致 Redis 服务卡顿。

对象编码

Redis 对象编码(Object Encoding)

Redis 为了优化内存使用和性能,对不同数据类型采用不同的底层编码方式。以下是各数据类型的编码规则:

1. zset(有序集合)

编码方式 条件
skiplist(跳表) 数量大于 128 或者 有一个字符串长度大于 64
ziplist(压缩列表) 节点数量小于等于 128(zset-max-ziplist-entries 字符串长度小于等于 64(zset-max-ziplist-value

理想跳表→概率跳表(1-1/n^2)*Olog2(n)→实践跳表(p = 0.25 n >=128 max_level = 32)

2. set(集合)

编码方式 条件
intset(整数数组) 元素都为整数 节点数量小于等于 512(set-max-intset-entries
dict(字典) 元素有一个不为整数 或者 数量大于 512

3. hash(哈希)

编码方式 条件
dict(字典) 节点数量大于 512 字符串长度大于 64
ziplist(压缩列表) 节点数量小于等于 512(hash-max-ziplist-entries 字符串长度小于等于 64(hash-max-ziplist-value

4. list(列表)

编码方式 说明
quicklist(双向链表) 默认编码方式
ziplist(压缩列表) 间接使用(作为 quicklist 的节点)

5. string(字符串)

编码方式 条件
int 字符串长度小于等于 20 能转成整数
raw 字符串长度大于 44
embstr 字符串长度小于等于 44

为什么redis中字符串选择64个字节作为分界线

①内存分配器都是按照2^n进行分配的。
②cpu的cache line大小为64字节

为什么string编码方式按44字节来划分?

③redisObject使用16字节

④64字节,需要使用sdshdr8来存储,占用3字节

⑤为了兼容C语言的不安全字符串(strcpy,strlen),预留1字节'/0'

⑥buf可用空间为64 - 16 - 3 - 1

参数 默认值 说明
zset-max-ziplist-entries 128 zset 使用 ziplist 的最大元素数量
zset-max-ziplist-value 64 zset 使用 ziplist 的最大字符串长度
set-max-intset-entries 512 set 使用 intset 的最大元素数量
hash-max-ziplist-entries 512 hash 使用 ziplist 的最大元素数量
hash-max-ziplist-value 64 hash 使用 ziplist 的最大字符串长度

编码转换规则

  • 当数据量或字符串长度超过阈值时,会自动从紧凑编码(ziplist/intset/embstr)转换为通用编码(skiplist/dict/raw)
  • 转换是单向的,一旦升级为通用编码,不会自动降级回紧凑编码

IO多线程的优化

开启IO多线程优化后,所有线程的read→decode视为一个整体串行执行,然后computer(业务逻辑)穿行执行,然后encode→send视为一个整体串行执行

https://github.com/0voice

相关推荐
Lucifer三思而后行几秒前
中国移动 BCLinux 8.8 一键安装 Oracle 26ai
数据库·oracle
djjdjdjdjjdj3 分钟前
bootstrap如何修改警告框(Alert)的边框粗细
jvm·数据库·python
HalvmånEver6 分钟前
MySQL的数据类型(一)
数据库·mysql
tjc1990100516 分钟前
golang如何使用t.Cleanup清理测试_golang t.Cleanup测试清理使用策略
jvm·数据库·python
SeSs IZED19 分钟前
Redis开启远程连接
数据库·redis·缓存
214396528 分钟前
如何提升SQL数据更新的安全性_使用行级锁与悲观锁机制
jvm·数据库·python
uElY ITER36 分钟前
VS与SQL Sever(C语言操作数据库)
c语言·数据库·sql
SHoM SSER1 小时前
SQL之CASE WHEN用法详解
数据库·python·sql
Caspian Wren1 小时前
通过Logstash将MySQL数据同步到ES
数据库·mysql·elasticsearch·logstash
2401_835956811 小时前
Golang怎么做代码热更新_Golang热更新教程【精通】
jvm·数据库·python