原文地址: https://developer.aliyun.com/article/278922
简介: 业务中存在访问热点是在所难免的,redis也会遇到这个问题,然而如何发现热点key一直困扰着许多用户,redis4.0为我们带来了许多新特性,其中便包括基于LFU的热点key发现机制。
前言 业务中存在访问热点是在所难免的,redis也会遇到这个问题,然而如何发现热点key一直困扰着许多用户,redis4.0为我们带来了许多新特性,其中便包括基于LFU的热点key发现机制。
Least Frequently Used Least Frequently Used——简称LFU,意为最不经常使用,是redis4.0新增的一类内存逐出策略,关于内存逐出可以参考文章《Redis数据过期和淘汰策略详解》。
从LFU的字面意思我们很容易联想到key的访问频率,但是4.0最初版本仅用来做内存逐出,对于访问频率并没有很好的记录,那么经过一番改造,redis于4.0.3版本开始正式支持基于LFU的热点key发现机制。
LFU算法介绍 在redis中每个对象都有24 bits空间来记录LRU/LFU信息:
typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; void *ptr; } robj; 当这24 bits用作LFU时,其被分为两部分:
高16位用来记录访问时间(单位为分钟) 低8位用来记录访问频率,简称counter
16 bits 8 bits +------------------+--------+ + Last access time | LOG_C | +------------------+--------+ counter:基于概率的对数计数器 这里读者可能会有疑问,8 bits最大值也就是255,只用8位来记录访问频率够用吗?对于counter,redis用了一个trick的手段,counter并不是一个简单的线性计数器,而是用基于概率的对数计数器来实现,算法如下:
时间:
|
阅读:
752 字 ~4分钟
redis4.0新特性(四)-RDB-AOF混合持久化 简介: redis有两种持久化的方式——RDB和AOF,RDB是一份内存快照,AOF则为可回放的命令日志,他们两个各有特点也相互独立。4.0开始允许使用RDB-AOF混合持久化的方式,结合了两者的优点,通过aof-use-rdb-preamble配置项可以打开混合开关,yes则表示开启,no表示禁用,默认是禁用的,可通过config set修改。
RDB V.S. AOF 1. RDB RDB文件本质上是一份内存快照,保存了创建RDB文件那个时间点的redis全量数据。具有数据文件小创建、恢复快的优点,但是由于快照的特性无法保存创建RDB之后的增量数据。
2. AOF AOF文件本质上是一份执行日志保存所有对redis进行更改的命令,增量数据也就随命令写入AOF文件刷盘的策略,由配置项appendfsync控制可以选择”everysec”或”always”。
AOF文件基本上是human-readable的文本,所以其体积相对较大,在从AOF文件恢复数据时就是做日志回放执行AOF文件中记录的所有命令,所以相对RDB而言恢复耗时较长。
随着redis的运行AOF文件会不断膨胀,由aofrewrite机制来防止磁盘空间被撑满详见上一篇文章《redis4.0之利用管道优化aofrewrite》。
RDB-AOF混合持久化 细细想来,aofrewrite时也是先写一份全量数据到新AOF文件中再追加增量,只不过全量数据是以redis命令的格式写入。那么是否可以先以RDB格式写入全量数据,再追加增量日志呢?这样既可以提高aofrewrite和恢复速度,也可以减少文件大小,还可以保证数据的完毕性,整合RDB和AOF的优点。那么现在4.0实现了这一特性——RDB-AOF混合持久化。
aofrewrite 综上所述RDB-AOF混合持久化体现在aofrewrite时,那么我们就从这里开始来看4.0是如何实现的。
回忆下aofrewrite的过程
无论是serverCron触发或者执行BGREWRITEAOF命令,最终redis都会走到rewriteAppendOnlyFileBackground(),
rewriteAppendOnlyFileBackground函数会fork子进程子进程进入rewriteAppendOnlyFile函数来生成新的AOF文件混合持久化就从这里开始。
int rewriteAppendOnlyFile(char *filename) { ... if (server.aof_use_rdb_preamble) { int error; if (rdbSaveRio(&aof,&error,RDB_SAVE_AOF_PREAMBLE,NULL) == C_ERR) { errno = error; goto werr; } } else { if (rewriteAppendOnlyFileRio(&aof) == C_ERR) goto werr; } ... } 可以看到当混合持久化开关打开时,就会进入rdbSaveRio函数先以RDB格式来保存全量数据。 前文说道子进程在做aofrewrite时,会通过管道从父进程读取增量数据并缓存下来,那么在以RDB格式保存全量数据时也会从管道读取数据,并不会造成管道阻塞。
int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) { ... snprintf(magic,sizeof(magic),"REDIS%04d",RDB_VERSION); if (rdbWriteRaw(rdb,magic,9) == -1) goto werr; if (rdbSaveInfoAuxFields(rdb,flags,rsi) == -1) goto werr; 首先把RDB的版本(注意不是redis的版本)和辅助域写入文件
时间:
|
阅读:
1884 字 ~9分钟
redis4.0之MEMORY命令详解 前言 在过去,查看redis的内存使用状态只有info memory命令,而且也只有一些基础信息,想要获取全局信息就有些困难。4.0开始redis提供了MEMORY命令,一切都变得简单起来。
MEMORY命令 MEMORY命令一共有5个子命令,可以通过MEMORY HELP来查看:
127.0.0.1:7008> MEMORY HELP 1) "MEMORY DOCTOR - Outputs memory problems report" 2) "MEMORY USAGE <key> [SAMPLES <count>] - Estimate memory usage of key" 3) "MEMORY STATS - Show memory usage details" 4) "MEMORY PURGE - Ask the allocator to release memory" 5) "MEMORY MALLOC-STATS - Show allocator internal stats" 接下来我们从MEMORY STATS开始,一一介绍各个子命令的功能。
1. MEMORY STATS 首先,我们需要明确一个概念,redis的内存使用不仅包含所有的key-value数据,还有描述这些key-value的元信息,以及许多管理功能的消耗,比如持久化、主从复制,通过MEMORY STATS可以更好的了解到redis的内存使用状况。
这里我们启动了一个打开持久化功能并且带slave的redis,向其中随机写入了一些数据(某些数据还带有过期时间),以便读者可以更好的了解redis的内存使用,接下来执行MEMORY STATS命令:
127.0.0.1:7008> MEMORY STATS 1) "peak.
时间:
|
阅读:
169 字 ~1分钟
Redis modules make possible to extend Redis functionality using external modules, implementing new Redis commands at a speed and with features similar to what can be done inside the core itself.
Redis模块系统是一种模块化的方式,可以在Redis服务器上实现新的命令,并且可以在Redis服务器上实现新的数据类型。
Redis modules are dynamic libraries, that can be loaded into Redis at startup or using the MODULE LOAD command. Redis exports a C API, in the form of a single C header file called redismodule.h. Modules are meant to be written in C, however it will be possible to use C++ or other languages that have C binding functionalities.
时间:
|
阅读:
812 字 ~4分钟
What is active defragmentation? 什么是主动内存碎片整理?
Active (online) defragmentation allows a Redis server to compact the spaces left between small allocations and deallocations of data in memory, thus allowing to reclaim back memory.
主动内存碎片整理允许一个Redis服务器在内存中的数据分配和释放过程中对产生的空间进行压缩,以便释放内存。
Fragmentation is a natural process that happens with every allocator (but less so with Jemalloc, fortunately) and certain workloads. Normally a server restart is needed in order to lower the fragmentation, or at least to flush away all the data and create it again.
时间:
|
阅读:
996 字 ~5分钟
Database · 内存管理 · JeMalloc-5.1.0 实现分析 源文地址:https://www.bookstack.cn/read/aliyun-rds-core/4a0cdf677f62feb3.md
JeMalloc 是一款内存分配器,与其它内存分配器相比,它最大的优势在于多线程情况下的高性能以及内存碎片的减少。
这篇文章介绍 JeMalloc-5.1.0 版本(release 日期:2018年5月9日)的实现细节。
对于对老版本比较熟悉的人来说,有几点需要说明:
chunk 这一概念被替换成了 extent dirty page 的 decay(或者说 gc) 变成了两阶段,dirty -> muzzy -> retained huge class 这一概念不再存在 红黑树不再使用,取而代之的是 pairing heap 基础知识 以下内容介绍 JeMalloc 中比较重要的概念以及数据结构。
size_class 每个 size_class 代表 jemalloc 分配的内存大小,共有 NSIZES(232)个小类(如果用户申请的大小位于两个小类之间,会取较大的,比如申请14字节,位于8和16字节之间,按16字节分配),分为2大类:
small_class(*小内存*) : 对于64位机器来说,通常区间是 [8, 14kb],常见的有 8, 16, 32, 48, 64, …, 2kb, 4kb, 8kb,注意为了减少内存碎片并不都是2的次幂,比如如果没有48字节,那当申请33字节时,分配64字节显然会造成约50%的内存碎片 large_class(*大内存*): 对于64位机器来说,通常区间是 [16kb, 7EiB],从 4 * page_size 开始,常见的比如 16kb, 32kb, …, 1mb, 2mb, 4mb,最大是 $2^{62}+3^{60}$ size_index : size 位于 size_class 中的索引号,区间为 [0,231],比如8字节则为0,14字节(按16计算)为1,4kb字节为28,当 size 是 small_class 时,size_index 也称作 binind base 用于分配 jemalloc 元数据内存的结构,通常一个 base 大小为 2mb, 所有 base 组成一个链表。
时间:
|
阅读:
912 字 ~5分钟
原文地址 https://youjiali1995.github.io/allocator/jemalloc-purge/
Redis 之前一直使用 4.0.3 版本的 jemalloc,对 dirty pages 的回收比较暴力,相当于 stop the world。我们线上 Redis 偶尔也会出现 RSS 瞬间降低 1GB 并伴随大量报错的场景,之前看 jemalloc 源码也是为了解决这个问题。
最近(2018-05-24),Redis 把 jemalloc 升级到了 5.0.1 版本,这次主要看一下对 purge 的改进。
decay-based purge 4.0.3 是根据当前 active_pages : dirty_pages 的比例进行 purge,比如 lg_dirty_mult:3,当 active_parges / dirty_pages < 8 时,就会把多余的 purge 掉,当比例在 8 波动时,就会频繁 purge,造成性能下降。
4.1.0 新增了 decay-based purge,不再根据比例进行 purge,而是配置 dirty page 的存在时间,在这段时间内按照 Smootherstep 平滑的进行 purge(1->0):
若使用 decay-based purge,jemalloc 在 decay_time(默认10s) 时间内分段(200次)进行 dirty pages 的回收,避免突发的大量 dirty page 回收影响性能。因为在 purge 的过程中, 会有新的 dirty page 产生,所以将整个 purge 划分为 SMOOTHSTEP_NSTEPS(200) 组,每组分别负责 decay_interval(decay_time / SMOOTHSTEP_NSTEPS) 时间内产生的 dirty page 的回收,每组能保留的 dirty pages 数量都是 Smootherstep 曲线,总的能保留的 dirty page 数量为 200 组的叠加,超出的会 purge。
时间:
|
阅读:
7 字 ~1分钟
一、主动碎片整理 当key被频繁修改,value长度不断变化时,Redis会为key分配新的内存空间。由于Redis追求高性能,实现了自己的内存分配器来管理内存,因此并不会将原有内存释放给OS,从而导致出现内存碎片。当used_memory_rss/used_memory高于1.5,一般认为内存碎片占比过高,内存利用率低。
因此,合理规划和使用缓存数据,规范数据写入,有助于减少内存碎片的产生。
Redis3.x及以下:可以通过定期重启服务解决内存碎片问题。建议实际缓存数据不超过配置可用内存的50%。
Redis4.x:支持主动整理内存碎片,服务在运行期间进行自动内存碎片清理。同时Redis4.x支持通过memory purge命令手动清理内存碎片。
Redis5.0:第二版主动碎片整理为增强版主动碎片整理,配合Jemalloc版本更新,更快更智能,延时更低。