Redis使用Jemalloc(默认编译)来进行内存的管理:
Jemalloc将内存分成许多不同的区域,每个区域成为arena,areana之间相互独立。Jemalloc通过创建多个arena来减少线程申请内存的操作冲突。一般arena数量为cpu数量*4.
arena以chunk为单位向操作系统申请空间,默认为2MB。Jemalloc会把chunk分割成多个run。run里面有多个page,其默认大小为4kb。run必须是page的整数倍大小。(mysql的page为16kb大小,页 →区(1MB)→段(256MB))
Jemalloc会将run划分成若干个大小相同的region,并将region分配给应用使用。按大小region分为small、large、huge三类。
Jemalloc使用bin来管理small region,large region直接从chunk中申请,对于huge region,Jemalloc直接向系统申请内存,Jemalloc使用红黑树管理huge region。
Jemalloc在arena分配内存时,需要对arena或者region对应的bin进行加锁(通过划分不同区域,来最小化锁的竞争,这一点在mysql上也很常见,比如mysql的undo表有多个undo段,可以支持数量极多的并发)。为了减少同步,Jemalloc会为每个线程分配一个私有缓存tcache,缓存当前线程已申请的内存块(small/large)。在线程申请内存块时,Jemalloc会优先查找私有缓存空间对应的内存块,避免到arena中申请内存导致线程同步。
用户每次请求的内存大小会被向上对齐最接近的region:
1. 将应用申请的内存大小向上对齐最接近的region
2. 如果申请的是small region或小于私有缓存(tcache)的large region,则尝试先从large region开始分配,分配成功则结束
3. 如果申请的是small region,则从对应的bin找一块合适的run,从run中划分一块region(对bin加锁)
4. 如果申请的是large region,则从对应的chunk中申请相应大小的run并返回(对arena加锁)
5. 如果申请是huge region,直接向下同申请内存,并添加到对应的红黑树进行管理
如果arena中没有可用的chunk了,Jemalloc会向操作系统申请chunk。如果一个chunk的所有内存已经释放,Jemalloc会将chunk归还给系统。
Jemalloc会统计脏页(待回收)的数量,当脏页的数量超过一定的比例,会启动GC清除脏页,并尝试合并相邻的空间,将其返回给系统或者可用区域。Redis的碎片整理机制就是通过Jemalloc来进行的。
Redis数据过期机制
redis有两种机制删除过期数据:定时删除和惰性删除,无论是定时删除还是惰性删除,都需将命令传播给AOF文件和从节点。
- 定时删除
- 遍历指定数量的数据库
- 获取数据库,记录当前正处理的的数据库
- 处理该数据库中的过期字典
- 执行一次删除采样操作,通过采样数据量中已过期健的比例,预估整个数据库中的过期健比例
- 计算待处理的Hash表数组索引,按顺序处理
- 检查索引上所有的健,删除过期健,每次检查一个健,采样健加一
- 没执行16次采样操作删除动作,就检查处理时间是否超出限制
- 根据采样结果统计已过期健所占比例,若小于配置,则不再处理
- 惰性删除
- 获取当前内存使用量并判断是否需要淘汰数据
- 处理LRU、LFU算法
- 从给定的数据集(数据字典或过期字典)采集样本填充到样本池。样本池中的数据按淘汰优先级排序,越优先淘汰的数据放到最后。
- 从样本池中获取淘汰优先级最高的数据作为待淘汰健
- 如果使用的是随机淘汰算法,则从指定的数据集里随机选择一个健作为淘汰健
- 删除前面选择的淘汰健