Redis_09_Redis优化

Redis优化

内存优化

内存消耗分为:程序内存、对象内存、缓冲内存、内存碎片

1)对象内存占内存消耗中最多;

2)优化内存主要操作对象内存和内存碎片;


(1)程序内存:运行Redis进程所需内存;

1)空的Redis进程所消耗内存可忽略不计;


(2)对象内存:Redis中每个键值对所占用内存

1)键和值都分别占用内存,应避免过长的键或值;

2)键类型均为string,可忽略其在对象内存中消耗内存;

3)值需根据不同的类型,尽可能调整其底层数据结构的实现;


(3)缓冲内存:Redis中使用各缓冲占用内存

1)缓冲内存分为:客户端缓冲区、复制积压缓冲区、AOF缓冲区

2)输入/输出缓冲区在流量较大下容易失控(造成内存不稳定),需重点监控;


(4)内存碎片:操作Redis键值对使部分内存不可再用

1)Redis默认内存分配器:jemalloc

2)Redis支持的内存分配器:glibctcmalloc

3)尽量避免频繁更新键和删除过期键,会增加内存碎片率(导致内存不可用)

4)可通过数据对齐(避免碎片)和重启Redis(回收碎片)将内存碎片率降低


回收

内存回收分为:删除过期键值对、内存溢出控制策略

1)Redis中所有设置过期时间的键均保存在字典中

2)主节点的内存回收操作会同步至从节点;

//频繁执行内存回收的成本较高(应maxmemory > used_memory时才执行)


(1)删除过期键值对: 通过删除过期键值对实现内存的回收

1)删除过期键值对分为:惰性删除、定时任务删除

2)惰性删除:当客户端读取过期键时,才删除该键并返回空

3)定时任务删除:Redis定时任务根据扫描所有键并根据比列删除

//惰性删除可有效节省CPU


定时任务删除过期键流程:

1)随机检查每个数据库中20个键值对,并删除过期键;

2)若过期键比列超过1/4,则继续检查(直至比列低于1/4或超时);

3)若因超时停止,Redis将执行快模式回收过期间(默认慢模式,25毫秒);

//快模式的超时时间为1毫秒(2秒内能只可执行1次)


如:内存回收流程:
在这里插入图片描述




(2)内存溢出控制策略:当内存超过参数maxmemory指定值时,触发溢出策略

1)由参数maxmemory-policy指定内存溢出控制策略;

2)常用内存控制策略如下:

内存控制策略说明
noeviction (默认)拒绝所有写入操作并返回错误信息至客户端 (只响应读操作)
volatile-lru根据LRU算法删除设置过期时间的键,直到满足内存 (若无键可删除,则回退到noeviction策略)
allkeys-lru根据LRU算法删除键,直到满足内存 (可删除任意键)
allkeys-random随机删除所有键,直到满足内存
volatile-random随机删除过期键,直到满足内存
volatile-ttl根据字段TTL,删除将要过期的键 (若无键可删除,则回退到noeviction策略)

优化

Redis中所有值的底层结构均为redisObject结构体

1)每个redisObject结构体至少占用16字节;

2)redisObject结构体中常用字段如下:

字段含义
type值的类型 (type命令本质)
encoding值的底层数据结构
lru值最后一次被访问时间
refcout值被引用的次数
*ptr值是整数,代表值数据 值是其他类型,代表指向数据的执行

内存优化的两个方向:

(1)采用压缩数据结构作为值的底层实现(如:ziplist、quicklist和intset等)

1)Redis只支持压缩编码格式向非压缩编码格式转化(重启可实现逆转换);

2)当使用压缩编码格式时不建议频繁增删操作,非常消耗CPU;


(2)降低Redis的外层键数量(使用多层数据结构的类型存储类似数据)

1)建议使用哈希表实现(底层应为ziplist,否则可能消耗更多的内存);

2)ziplist长度需控制在1000以内(读取长列表非常消耗CPU);

3)仅适用于存储小对象;


ziplist

ziplist:采用线性连续的内存结构存储数据(节省内存)

1)增删操作设计内存重新分配和释放(相关操作较多时,不建议使用该类型);

2)读写操作设计复杂的指针移动(最坏时间复杂度为O(n²))

3)长度和每个值大小建议不超过1000个和512字节;


ziplist结构体中常用字段如下:

字段含义
zlbytes记录整个压缩列表所占字节长度 (int32类型,4字节)
zltail记录距离尾节点的偏移量 (int32类型,4字节)
zllen记录压缩列表节点数量 (int16类型,2字节)
entry记录具体节点信息
zlend记录列表结尾 (占用1字节)

//可模拟双向链表结构(以O(1)时间复杂度出入队列)


entry存储具体节点信息如下:

信息含义
prev_entry_bytes_length前一个节点所占空间 (快速定位上一个节点,实现反向迭代)
encoding标识当前节点编码和长度 (格式:字符串/整数)
contents存储节点代表的值
intset

intset:存储有序且不重复的整数集(自动排序和去重)

1)以O(n)时间复杂度实现值的插入

2)以O(log(n))的时间复杂度实现值的排序、去重和查询

3)当存储数据超出原有内存时,intset会申请新内存空间并将拷贝数据;


intset结构体中常用字段如下:

字段含义
encoding整数数据的类型 (int16、int32和int64)
length集合中值的个数
contents整数数组 (默认从小到大存储)

缓存优化

缓存优化:实现Redis在应用中充当缓存层的实时性、高效性和高可用性

1)实际应用中将Redis做为底层存储层(数据库)的缓存层;

2)缓存优化以外还有缓存预热和缓存降级(较简单);


如:实际应用中Redis做为缓存层
在这里插入图片描述


缓存更新

缓存更新:保证缓存中的数据的实时性

1)缓存更新分为:LRU/LFU/FIFO算法剔除、超时剔除、主动更新

2)通过数据实时性和维护成本选择缓存更新策略;


(1)LRU/LFU/FIFO算法剔除:根据算法策略删除特定键

1)适用场景:Redis使用内存易超出预分配值;

2)实时性:最差(无法人工干预键的保留/删除);

3)维护成本:低(配置参数即可)


(2)超时剔除:根据过期时间,定时定量删除键

1)适用场景:能够容忍一定时间内的数据不一致

2)实时性:差(缓存数据与真实数据存在窗口期)

3)维护成本:中(创建键时指定过期时间)


(3)主动更新:通过开发程序监控真实数据进行实时更新

1)适用场景:应用需数据

2)实时性:强(真实数据更新后立即更新缓存数据)

3)维护成本:高(需通过开发程序)

缓存更新实时性维护成本
LRU/LFU/FIFO算法剔除最差
超时剔除
主动更新

//实际应用中建议结合使用超时剔除和主动更新(提高容灾性)


缓存穿透

缓存穿透:数据请求在缓存层和存储层中均未命中,请求均转到存储层

1)缓存穿透导致后端存储层负载过大(可能宕机);

2)默认存储层查不到数据返回空时,不会在缓存层进行备份;


优化方案:

1)当存储层返回空时,缓存层进行备份(并设置较短的过期时间);

2)采用布隆过滤器,将过滤器认为不存在的数据请求全部拒绝;


布隆过滤器(Bloom Filter):将所有数据的hash_tag存储在Bitmap中用于过滤

1)布隆过滤器需部署在缓存层前;

2)Bitmap中值只能记录1bit信息;

优化方案使用场景维护成本
缓存空对象1)数据命中率低 2)数据实时性高1)代码维护简单 2)需较多的缓存空间
布隆过滤器1)数据命中率低 2)数据实时性低1)代码维护复杂 2)占用较少的缓存空间

缓存雪崩

缓存雪崩(Stampeding Herd):缓存层宕机后,数据请求均转到存储层

1)缓存雪崩导致后端存储层负载过大(可能宕机);


优化方案:

1)使用多节点保证存储层的高可用性(哨兵和集群);

2)隔离每个应用组件使之互不影响(容器或线程池实现);

3)缓存数据的过期时间分散设计,防止同一时间大量数据过期;


缓存击穿/热点Key

缓存击穿/热点Key:特定键在一段时间内被多个客户端频繁请求

1)热点Key在缓存失效时,会有大量线程请求存储层以重建缓存;

2)缓存击穿:请求数据仅在存储层中,大量线程请求存储层以重建缓存;


优化方案:

1)互斥锁:限定仅由获得锁的线程可重建缓存;

2)热点Key永不过期:使之永久存储于缓存层中(单独线程更新);

//互斥锁中其他线程会一直尝试获取锁,直到获取数据为止

优化方案优点缺点
互斥锁1)实现简单; 2)实时性高;1)增加代码复杂度; 2)存在程序死锁风险; 3)存在线程池阻塞风险;
永不过期1)完全杜绝热点Key1)实时性低; 2)占用较多内存;

配置优化

配置优化:通过配置Linux系统提升Redis性能

1)Redis是CPU密集型服务,不建议和其他多核CPU密集型服务部署在一起

2)若Redis开启持久化或参数赋值,则不建议绑定CPU(子进程消耗过大)

3)建议所有运行Redis节点的Linux运行NTP服务(确保时间一致性)

4)关闭swap或降低其优先级(同时保证系统有20~30%闲置内存)

//不和其他CPU密集型服务部署在一起是为避免CPU过度竞争


内存分配

(1)使Redis进程可获得足够内存运行

1)overcommit技术:Linux对大部分内存请求均允许,但不立刻分配

2)通过设置“vm.overcommit_memory”保证fork等操作的顺利执行

3)overcommit_memory不同值的含义如下:

含义
0先检查是否有足够可用内存再分配
1允许超量使用内存(直到用完为止)
2使用内存不能超过swap + overcommit_ratio

优化流程:

echo "vm.overcommit_memory=1" \>\> /etc/sysctl.conf

sysctl vm.overcommit_memory=1

(2)使Redis不轻易使用swap

1)swappiness技术:根据参数值指定各进程使用swap的概率

2)通过设置“/proc/sys/vm/swappiness”使Redis的swap优先级最低;

3)swappiness特殊值的含义如下(范围0~100):

含义
0宁愿被OOM,也不用swap
1宁愿用swap也不被OOM
60默认值
100优先使用swap

优化流程:

echo 0 \> /proc/sys/vm/swappiness

echo vm.swappiness=0 \>\> /etc/sysctl.conf

(3)避免创建子进程时过度使用内存

1)Transparent Huge Pages(THP):创建进程时分配大内存页(2MB)

2)写时复制期间赋值内存页单位从4KB变为2MB(导致大量写操作慢查询)

3)开启THP会增快fork子进程的速度,但会导致过度内存消耗


优化流程(关闭THP):

echo never > /sys/kernel/mm/transparent_hugepage/enabled

echo "echo never > /sys/kernel/mm/transparent_hugepage/enabled" >> /etc/rc.local

(4)避免Redis进程被关闭

1)OOM Killer:当内存不足时,系统选择性关闭用户进程

2)通过配置“/proc/进程ID/oom_adj”使Redis进程被关闭的优先级最低;

//其值范围为“-17 ~ -15”


优化流程(Shell脚本):

#!/bin/bash

for redis_pid in \$(pgrep -f "redis-server")
do
	echo -17 \> /proc/%{redis_pid}/oom_adj
done

连接限制

(1)配置最多可连接用户数(确保客户端正常连接)

1)文件句柄:Linux默认限制进程最多可持有1024个文件句柄

2)客户端连接后需占有一个文件句柄,且服务端默认占有32个文件句柄

3)通过“ulimit”实现最多连接用户数和参数maxclients一致;

//若限定持有文件句柄和参数maxclients不一致时,以前者为准


优化流程:

ulimit -Sn 10032

(2)保证TCP正常连接

1)TCP backlog:Linux通过 backlog队列存储端口的TCP连接(默认值128)

2)Redis的backlog队列默认长度为511(两者不一致,以系统为准);

3)通过配置“/proc/sys/net/core/somaxconn”保证可连接数;


优化流程:

echo 511 \> /proc/sys/net/core/somaxconn
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值