一、什么是缓存击穿、缓存穿透、缓存雪崩?
缓存穿透 【针对大量非法访问的请求,缓存中没有,直接访问DB】
缓存穿透指的查询缓存和数据库中都不存在的数据,这样每次请求直接打到数据库,就好像缓存不存在 一样。 对于系统A,假设一秒 5000 个请求,结果其中 4000 个请求是黑客发出的恶意攻击。 黑客发出的那 4000 个攻击,缓存中查不到,每次你去数据库里查,也查不到。 举个栗子。 数据库 id 是从 1 开始的,结果黑客发过来的请求 id 全部都是负数。 这样的话,缓存中不会有,请求每次都“绕过缓存”,直接查询数据库。 这种恶意攻击场景的缓存穿透就会直接把数据库给打死。
缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义。 缓存穿透可能会使后端存储负载加大,如果发现大量存储层空命中,可能就是出现了缓存穿透问题。 缓存穿透可能有两种原因:
1. 自身业务代码问题 2. 恶意攻击,爬虫造成空命中。
缓存穿透解决办法:
①对空值缓存:如果一个查询数据为空(不管数据是否存在),都对该空结果进行缓存,其过期时间会 设置非常短。
②采用布隆过滤器:布隆过滤器可以判断元素是否存在集合中,他的优点是空间效率和查询时间都比一 般算法快,缺点是有一定的误识别率和删除困难。
③设置可以访问名单:使用bitmaps类型定义一个可以访问名单,名单id作为bitmaps的偏移量,每次 访问时与bitmaps中的id进行比较,如果访问id不在bitmaps中,则进行拦截,不给其访问。
④进行实时监控:对于redis缓存中命中率急速下降时,迅速排查访问对象和访问数据,将其设置为黑名单。
缓存空值/默认值 一种方式是在数据库不命中之后,把一个空对象或者默认值保存到缓存,之后再访问这个数据,就会从 缓存中获取,这样就保护了数据库。
缓存空值有两大问题: 1. 空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间(如果是攻击,问题更严 重),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。 2. 缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置 为5分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致。 这时候可以利用消息队列或者其它异步方式清理缓存中的空对象。
布隆过滤器 除了缓存空对象,我们还可以在存储和缓存之前,加一个布隆过滤器,做一层过滤。 布隆过滤器里会保存数据是否存在,如果判断数据不不能再,就不会访问存储。
缓存击穿【针对极少数并发量很高的key,缓存过期了,直接请求DB】
一个并发访问量比较大的key在某个时间过期,导致所有的请求直接打在DB上。 具体来是,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况, 当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一 个洞。 结果是: 请求会直接访问数据库,并回设到缓存中,高并发访问数据库会导致数据库崩溃。
缓存击穿解决方案:
(1)预先设置热门数据: 在redis高峰访问时期,提前设置热门数据到缓存中,或适当延长缓存中key过期时间。
(2)实时调整: 实时监控哪些数据热门,实时调整key过期时间。
(3)对于热点key设置永不过期。
(4)加锁更新 ⽐如请求查询A,发现缓存中没有,对A这个key加锁,同时去数据库查询数据,写⼊缓存,再返回给⽤ 户,这样后⾯的请求就可以从缓存中拿到数据了。
缓存雪崩【针对大面积缓存同时间段失效,大面积同时访问DB】
某⼀时刻发⽣⼤规模的缓存失效的情况,例如缓存服务宕机、大量key在同一时间过期,这样的后果就 是⼤量的请求进来直接打到DB上,db无响应,最后可能导致整个系统的崩溃,称为雪崩。 对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求, 但是缓存机器意外发生了: 缓存全盘宕机,缓存挂了, 大量key在同一时间过期 此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后db无响应,最后导致整 个系统的崩溃。 此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被 新的流量给打死了。
缓存雪崩解决方案:
缓存雪崩是三大缓存问题里最严重的一种,我们来看看怎么预防和处理。
提高缓存可用性
1. 集群部署:通过集群来提升缓存的可用性,可以利用Redis本身的Redis Cluster或者第三方集群方 案如Codis等。
2. 多级缓存:设置多级缓存,设置一级缓存本地 guava 缓存,第一级缓存失效的基础上再访问二级 缓存 redis,每一级缓存的失效时间都不同。
过期时间
1. 均匀过期:为了避免大量的缓存在同一时间过期,可以把不同的 key 过期时间随机生成,避免过 期时间太过集中。
2. 热点数据永不过期。
熔断降级 1. 服务熔断:当缓存服务器宕机或超时响应时,为了防止整个系统出现雪崩,可以使用hystrix 类似 的熔断,暂时停止业务服务访问db, 或者其他被依赖的服务,避免 MySQL 被打死。 2. 服务降级:当出现大量缓存失效,而且处在高并发高负荷的情况下,在业务系统内部暂时舍弃对一 些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。
RedisCluster
Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。 Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。
RedisCluster介绍:
自动将数据进行分片,每个 master 上放一部分数据 提供内置的高可用支持,部分 master 不可用时,还是可以继续工作的 .
在 Redis cluster 架构下,每个 Redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w 的端 口号,比如 16379。 16379 端口号是用来进行节点间通信的,也就是 cluster bus 的东西,cluster bus 的通信,用来进行故 障检测、配置更新、故障转移授权。cluster bus 用了另外一种二进制的协议, gossip 协议,用于节 点间进行高效的数据交换,占用更少的网络带宽和处理时间。
redis在3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储 不同的数据。 cluster模式为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器,内存/QPS不受 限于单机,可受益于分布式集群高扩展性。 RedisCluster 是 Redis 的亲儿子,它是 Redis 作者自己提供的 Redis 集群化方案。 相对于 Codis 的不同,它是去中心化的,如图所示,该集群有三个 Redis 节点组成, 每个节点负责整 个集群的一部分数据,每个节点负责的数据多少可能不一样。这三个节点相 互连接组成一个对等的集 群,它们之间通过一种特殊的二进制协议相互交互集群信息
如上图,官方推荐,集群部署至少要 3 台以上的master节点,最好使用 3 主 3 从六个节点的模式。 Redis Cluster 将所有数据划分为 16384 的 slots,它比 Codis 的 1024 个槽划分得更为精细,每个节点 负责其中一部分槽位。槽位的信息存储于每个节点中,它不像 Codis,它不 需要另外的分布式存储来存 储节点槽位信息。 Redis Cluster是一种服务器Sharding技术(分片和路由都是在服务端实现),采用多主多从,每一个分区 都是由一个Redis主机和多个从机组成,片区和片区之间是相互平行的。 Redis Cluster集群采用了P2P的模式,完全去中心化。
三主三从的RedisCluster集群
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。 集群的每个节点负责一部分hash槽,如图中slots所示。 为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模 型,每个节点都会有1-n个从节点。 例如master-A节点不可用了,集群便会选举slave-A节点作为新的主节点继续服务。
集群创建
Redis集群一般由多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。 每个节点需要开启配置cluster-enabled yes,让Redis运行在集群模式下。 建议为集群内所有节点统一目录,一般划分三个目录:conf、data、log,分别存放配置、数据和日志 相关文件。 把6个节点配置统一放在conf目录下,集群相关配置如下:
# 节点端口
port 6379
# 开启集群模式
cluster-enabled yes
# 节点超时时间,单位毫秒
cluster-node-timeout 15000
# 集群内部配置文件
cluster-config-file "nodes-6379.conf"
集群模式的Redis除了原有的配置文件之外又加了一份集群配置文件。 当集群内节点信息发生变化,如添加节点、节点下线、故障转移等。 第一次启动时如果没有集群配置文件,它会自动创建一份,文件名称采用cluster-config-file参数项控 制,建议采用node-{port}.conf格式定义,通过使用端口号区分不同节点,防止同一机器下多个节点彼 此覆盖,造成集群信息异常。 如果启动时存在集群配置文件,节点会使用配置文件内容初始化集群信息。启动过程如图所示。
节点握手 Redis集群一般由多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。 节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通信,达到感知对方的过程。 节点握手是集群彼此通信的第一步,由客户端发起命令:cluster meet{ip}{port},如图所示。
图中执行的命令是:cluster meet127.0.0.16380让节点6379和6380节点进行握手通信。cluster meet 命令是一个异步命令,执行之后立刻返回。内部发起与目标节点进行握手通信,如图所示。 1)节点6379本地创建6380节点信息对象,并发送meet消息。 2)节点6380接受到meet消息后,保存6379节点信息并回复pong消息。 3)之后节点6379和6380彼此定期通过ping/pong消息进行正常的节点通信。 这里的meet、ping、pong消息是Gossip协议通信的载体,之后的节点通信部分做进一步介绍,它的主 要作用是节点彼此交换状态数据信息。6379和6380节点通过meet命令彼此建立通信之后,集群结构如 图所示。对节点6379和6380分别执行cluster nodes命令,可以看到它们彼此已经感知到对方的存在。
分配槽(slot) Redis集群把所有的数据映射到16384个槽中。 每个节点对应若干个槽,只有当节点分配了槽,才能响应和这些槽关联的键命令。通过 cluster addslots命令为节点分配槽。
虚拟槽分片的映射步骤:
1.把16384槽按照节点数量进行平均分配,由节点进行管理
2.对每个key按照CRC16规则进行hash运算
3.把hash结果对16383进行取余
4.把余数发送给Redis节点
5.节点接收到数据,验证是否在自己管理的槽编号的范围 如果在自己管理的槽编号范围内,则把数据保存到数据槽中,然后返回执行结果 如果在自己管理的槽编号范围外,则会把数据发送给正确的节点,由正确的节点来把数据保存在对 应的槽中。
虚拟槽分布方式中,由于每个节点管理一部分数据槽,数据保存到数据槽中。 当节点扩容或者缩容时,对数据槽进行重新分配迁移即可,数据不会丢失。
虚拟槽分片特点: 虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有数据映射到一个固定范围的整数 集合中,整数定义为槽(slot)。槽是集群内数据管理和迁移的基本单位。 槽的范围一般远远大于节点数,比如Redis Cluster槽范围是0~16383。 采用大范围槽的主要目的是为了方便数据拆分和集群扩展,每个节点会负责一定数量的槽。 Redis虚拟槽分区的优点: 解耦数据和节点之间的关系,简化了节点扩容和收缩难度。 节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据。 支持节点、槽、键之间的映射查询,用于数据路由,在线伸缩等场景。 无论数据规模大,还是小,Redis虚拟槽分区各个节点的负载,都会比较均衡 。而一致性哈希在大 批量的数据场景下负载更加均衡,但是在数据规模小的场景下,会出现单位时间内某个节点完全空 闲的情况出现。
Redis集群提供了灵活的节点扩容和收缩方案,可以在不影响集群对外服务的情况下,为集群添加节点 进行扩容也可以下线部分节点进行缩容。
其实,集群扩容和缩容的关键点,就在于槽和节点的对应关系,扩容和缩容就是将一部分 槽 和 数据 迁 移给新节点。 例如下面一个集群,每个节点对应若干个槽,每个槽对应一定的数据,如果希望加入1个节点希望实现 集群扩容时,需要通过相关命令把一部分槽和内容迁移给新节点。
缩容也是类似,先把槽和数据迁移到其它节点,再把对应的节点下线。
集群节点间通信:
集群通信原理
集群元数据的维护有两种方式:集中式、Gossip 协议。Redis cluster 节点间采用 gossip 协议进行通 信。 集中式是将集群元数据(节点信息、故障等等)几种存储在某个节点上。集中式元数据集中存储的一个 典型代表,就是大数据领域的 storm 。它是分布式的大数据实时计算引擎,是集中式的元数据存储的 结构,底层基于 zookeeper(分布式协调的中间件)对所有元数据进行存储维护。
Redis 维护集群元数据采用另一个方式, gossip 协议,所有节点都持有一份元数据,不同的节点如果 出现了元数据的变更,就不断将元数据发送给其它的节点,让其它节点也进行元数据的变更。
Redis 维护集群元数据采用另一个方式, gossip 协议,所有节点都持有一份元数据,不同的节点如果 出现了元数据的变更,就不断将元数据发送给其它的节点,让其它节点也进行元数据的变更。
集中式:
好处在于,元数据的读取和更新,时效性非常好,一旦元数据出现了变更,就立即更新到集中 式的存储中,其它节点读取的时候就可以感知到;不好在于,所有的元数据的更新压力全部集中在一个 地方,可能会导致元数据的存储有压力。
gossip去中心化:
好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续打到所有节点上 去更新,降低了压力;不好在于,元数据的更新有延时,可能导致集群中的一些操作会有一些滞后。 10000 端口:每个节点都有一个专门用于节点间通信的端口,就是自己提供服务的端口号 +10000,比如 7001,那么用于节点间通信的就是 17001 端口。每个节点每隔一段时间都会往另 外几个节点发送 ping 消息,同时其它几个节点接收到 ping 之后返回 pong 。 交换的信息:信息包括故障信息,节点的增加和删除,hash slot 信息等等。
gossip协议:
gossip 协议包含多种消息,包含 ping , pong , meet , fail 等等。 meet:某个节点发送 meet 给新加入的节点,让新节点加入集群中,然后新节点就会开始与其它 节点进行通信。
其实内部就是发送了一个 gossip meet 消息给新加入的节点,通知那个节点去加入我们的集群。 ping:每个节点都会频繁给其它节点发送 ping,其中包含自己的状态还有自己维护的集群元数 据,互相通过 ping 交换元数据。
pong:返回 ping 和 meeet,包含自己的状态和其它信息,也用于信息广播和更新。
fail:某个节点判断另一个节点 fail 之后,就发送 fail 给其它节点,通知其它节点说,某个节点宕机啦。
ping信息原理:
ping 消息深入 ping 时要携带一些元数据,如果很频繁,可能会加重网络负担。 每个节点每秒会执行 10 次 ping,每次会选择 5 个最久没有通信的其它节点。当然如果发现某个节点通 信延时达到了 cluster_node_timeout / 2 ,那么立即发送 ping,避免数据交换延时过长,落后的时 间太长了。比如说,两个节点之间都 10 分钟没有交换数据了,那么整个集群处于严重的元数据不一致 的情况,就会有问题。所以 cluster_node_timeout 可以调节,如果调得比较大,那么会降低 ping 的频率。 每次 ping,会带上自己节点的信息,还有就是带上 1/10 其它节点的信息,发送出去,进行交换。至少 包含 3 个其它节点的信息,最多包含 总节点数减 2 个其它节点的信息。
三、Redis内存
Redis 内存不足有这么几种处理方式:
修改配置文件 redis.conf 的 maxmemory 参数,增加 Redis 可用内存 也可以通过命令set maxmemory动态设置内存上限.
修改内存淘汰策略,及时释放内存空间.
使用 Redis 集群模式,进行横向扩容,扩容节点数目.
四、Redis大Key问题
BigKey产生的背景
1、redis数据结构使用不恰当 将Redis用在并不适合其能力的场景,造成Key的value过大,如使用String类型的Key存放大体积二进制 文件型数据。
2、未及时清理垃圾数据 没有对无效数据进行定期清理,造成如HASH类型Key中的成员持续不断的增加。即一直往value塞数 据,却没有删除机制,value只会越来越大。
3、对业务预估不准确 业务上线前规划设计考虑不足没有对Key中的成员进行合理的拆分,造成个别Key中的成员数量过多。
Redis使用过程中,有时候会出现大key的情况
单个简单的key存储的value很大,size超过10KB
hash, set,zset,list 中存储过多的元素(以万为单位)
Big Key定义
Big Key就是某个key对应的value很大,占用的redis空间很大,本质上是大value问 题。 key往往是程序可以自行设置的,value往往不受程序控制,因此可能导致value很大。 redis中这些Big Key对应的value值很大,在序列化/反序列化过程中花费的时间很大,因此当我们操作 Big Key时,通常比较耗时,这就可能导致redis发生阻塞,从而降低redis性能。 BigKey指以Key的大小和Key中成员的数量来综合判定,用几个实际的例子对大Key的特征进行描述: Key本身的数据量过大:一个String类型的Key,它的值为5MB Key中的成员数过多:一个ZSET类型的Key,它的成员数量为10000个 Key中成员的数据量过大:一个Hash类型的Key,它的成员数量虽然只有1000个但这些成员的 Value值总大小为100MB。
大key会造成什么问题呢?
1、阻塞请求 Big Key对应的value较大,我们对其进行读写的时候,需要耗费较长的时间,这样就可能阻塞后续的请 求处理。Redis的核心线程是单线程,单线程中请求任务的处理是串行的,前面的任务完不成,后面的 任务就处理不了。
2、内存增大 读取Big Key耗费的内存比正常Key会有所增大,如果不断变大,可能会引发OOM(内存溢出),或达 到redis的最大内存maxmemory设置值引发写阻塞或重要Key被逐出。
3、阻塞网络 读取单value较大时会占用服务器网卡较多带宽,自身变慢的同时可能会影响该服务器上的其他Redis实 例或者应用。
4、影响主从同步、主从切换 删除一个大Key造成主库较长时间的阻塞并引发同步中断或主从切换。
解决Big Key问题
无非就是减小key对应的value值的大小,也就是对于String数据结构的话,减少存 储的字符串的长度;对于List、Hash、Set、ZSet数据结构则是减少集合中元素的个数。
1、对大Key进行拆分 将一个Big Key拆分为多个key-value这样的小Key,并确保每个key的成员数量或者大小在合理范围内, 然后再进行存储,通过get不同的key或者使用mget批量获取。
2、对大Key进行清理 对Redis中的大Key进行清理,从Redis中删除此类数据。 Redis自4.0起提供了UNLINK命令,该命令能够以非阻塞的方式缓慢逐步的清理传入的Key, 通过UNLINK,你可以安全的删除大Key甚至特大Key。
3、监控Redis的内存、网络带宽、超时等指标 通过监控系统并设置合理的Redis内存报警阈值来提醒我们此时可能有大Key正在产生,如:Redis内存 使用率超过70%,Redis内存1小时内增长率超过20%等。
4、定期清理失效数据 如果某个Key有业务不断以增量方式写入大量的数据,并且忽略了其时效性,这样会导致大量的失效数 据堆积。 可以通过定时任务的方式,对失效数据进行清理。
5、压缩value 使用序列化、压缩算法将key的大小控制在合理范围内,但是需要注意序列化、反序列化都会带来一定 的消耗。 如果压缩后,value还是很大,那么可以进一步对key进行拆分。
如何查找BigKey
Redis4.0 及以上版本提供了--Bigkeys, --hotkeys 命令,可以分析出实例中每种数据结构的 top 1 的 Bigkey,同时给出了每种数据类型的键值个数以及平均大小。
查看bigkey:redis-cli -a 登录密码 --bigkeys
查看hotkey:redis-cli -a 登录密码 --hotkeys
通过公司自研或者云平台查找Key.
五、Redis缓存预热
所谓缓存预热,就是提前把数据库里的数据刷到缓存里,通常有这些方法:
1、直接写个缓存刷新页面或者接口,上线时手动操作
2、数据量不大,可以在项目启动的时候自动进行加载
3、定时任务刷新缓存.
六、Redis过期数据如何删除
Redis主要有2种过期数据回收策略:
惰性删除 惰性删除指的是当我们查询key的时候才对key进⾏检测,如果已经达到过期时间,则删除。显然,他有 ⼀个缺点就是如果这些过期的key没有被访问,那么他就⼀直⽆法被删除,⽽且⼀直占⽤内存。
定期删除 定期删除指的是Redis每隔⼀段时间对数据库做⼀次检查,删除⾥⾯的过期key。由于不可能对所有key 去做轮询来删除,所以Redis会每次随机取⼀些key去做检查和删除。
七、Redis内存淘汰策略
当 Redis 内存使用达到最大内存限制时,如果继续进行写入操作会导致 Redis 服务崩溃。因此,为了保证 Redis 服务的稳定性,Redis 在内存使用达到最大限制时采取一系列措施,如内存淘汰、警告等。
Redis 内存淘汰策略主要有如下:
(1)noeviction
不执行任何的淘汰策略,直接返回错误信息。这种方式可能导致内存使用过多而导致 Redis 崩溃。
(2)allkeys-lru
从所有的键中选取最近最少使用的数据淘汰。这种方式通常可以保留热点数据,但是可能会出现内存碎片,导致内存浪费。
(3)allkeys-lfu
从所有的键中选取访问频率最少的数据进行淘汰。这种方式适用于处理访问分布相对均匀的数据。
(4)volatile-lru
只从设定了过期时间(ttl)的键中选取最近最少使用的数据淘汰。这种方式适用于缓存等使用场景。
(5)volatile-lfu
只从设定了过期时间(ttl)的键中选取访问频率最少的数据进行淘汰。
(6)volatile-ttl
只从设定了过期时间(ttl)的键中选取即将过期的数据进行淘汰。这种方式适用于缓存等使用场景。
样例设置参数
maxmemory 2gb
maxmemory-policy allkeys-lru
可以从系统的容忍度、缓存数据的重要性、内存的使用状况。
各种淘汰策略简单说明如下:
1 noeviction(默认):不淘汰任何key,内存满时不写入新的key。
2 volatile-ttl:对设置了TTL的key,比较key的剩余TTL值,值越小越先被淘汰
3 allkeys-random:对所有key进行随机淘汰
4 volatile-random:对设置了TTL的key进行随机淘汰
5 allkeys-lru:对所有key基于LRU算法进行淘汰
6 volatile-lru:对设置了TTL的key基于LRU算法进行淘汰
7 allkeys-lfu:对全体key给予LFRU算法进行淘汰
8 volatile-lfu:对设置了TTL的key基于LFU算法进行淘汰
注:
TTL( time to live):键值对的过期时间
LRU(Least Recently Used):最近最少使用,用当前时间剪去最后一次访问时间,值越大越先被删除,
例:key1在3s前访问,key2在5s前访问,那么是删除的时key2
LFU(Least Frequently Used):最少频率使用,统计每个key的访问频率,值越小越先被删除
例:key1在5s内访问了5次,key2在5s内访问了10次,key1被删除
八、Redis的内存碎片
Redis 内存碎片产生比较常见的 2 个原因:
1、Redis 存储数据的时候向操作系统申请的内存空间可能会大于数据实际需要的存储空间
2、频繁修改 Redis 中的数据也会产生内存碎片。
当 Redis 中的某个数据删除时,Redis 通常不会轻易释放内存给操作系统。
另外,Redis 可以使用多种内存分配器来分配内存( libc、jemalloc、tcmalloc),默认使用 jemalloc[1],而 jemalloc 按照一系列固定的大小(8 字节、16 字节、32 字节……)来分配内存的。jemalloc 划分的内存单元如下图所示:
当程序申请的内存最接近某个固定值时,jemalloc 会给它分配相应大小的空间,就比如说程序需要申请 17 字节的内存,jemalloc 会直接给它分配 32 字节的内存,这样会导致有 15 字节内存的浪费。不过,jemalloc 专门针对内存碎片问题做了优化,一般不会存在过度碎片化的问题。
3、如何查看内存碎片
使用info memory命令
Redis 内存碎片率的计算公式:mem_fragmentation_ratio
(内存碎片率)= used_memory_rss
(操作系统实际分配给 Redis 的物理内存空间大小)/ used_memory
(Redis 内存分配器为了存储数据实际申请使用的内存空间大小)
也就是说,mem_fragmentation_ratio
(内存碎片率)的值越大代表内存碎片率越严重。
一定不要误认为used_memory_rss
减去 used_memory
值就是内存碎片的大小!!!这不仅包括内存碎片,还包括其他进程开销,以及共享库、堆栈等的开销。
通常情况下,我们认为 mem_fragmentation_ratio > 1.5
的话才需要清理内存碎片。mem_fragmentation_ratio > 1.5
意味着你使用 Redis 存储实际大小 2G 的数据需要使用大于 3G 的内存。
直接通过 config set
命令将 activedefrag
配置项设置为 yes
即可
config set activedefrag yes
具体什么时候清理需要通过下面两个参数控制:
# 内存碎片占用空间达到 500mb 的时候开始清理
config set active-defrag-ignore-bytes 500mb
# 内存碎片率大于 1.5 的时候开始清理
config set active-defrag-threshold-lower 50
通过 Redis 自动内存碎片清理机制可能会对 Redis 的性能产生影响,我们可以通过下面两个参数来减少对 Redis 性能的影响:
# 内存碎片清理所占用 CPU 时间的比例不低于 20%
config set active-defrag-cycle-min 20
# 内存碎片清理所占用 CPU 时间的比例不高于 50%
config set active-defrag-cycle-max 50