#作者:朱雷
文章目录
一、系统参数优化建议
1.1系统参数优化
1.1.1 关闭系统内存大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled # 加入开机启动脚本中
cat /sys/kernel/mm/transparent_hugepage/enabled
应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长。
对于 Redis 这种对性能和延迟极其敏感的数据库来说,我们希望 Redis 在每次申请内存时,耗时尽量短,所以不建议在 Redis 机器上开启这个机制。
1.1.2 降低系统swap设置
根据机器内存资源设置一个合理的值,一般大内存机器建议设置一个较低的值,避免内存页面频繁的swap到磁盘
echo "vm.swappiness=10" >> /etc/sysctl.conf
sysctl -w vm. swappiness=10
在Linux中,并非要等所有物理内存都耗尽才会使用到swap,参数swppiness会决定操作系统使用swap的倾向程度。swappiness的取值范围是0~100,swappiness的值越大,说明操作系统可能使用swap的概率越高,swappiness值越低,表示操作系统更加倾向于使用物理内存。
1.1.3 合理设置文件句柄数
ulimit -n 65536
cat /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
1.1.4 合理设置内核参数 somaxconn 大小
somaxconn是 Linux 内核中的一个参数,用于设置每个套接字(socket)监听队列的最大长度。它主要影响 TCP 协议的连接请求队列(backlog queue)。在调用listen函数开始监听 TCP 连接时,应用程序可以指定一个backlog参数,这个值会与somaxconn进行比较,实际的等待队列长度取其中较小的值。
- 队列过小:在高并发场景下,过小的队列可能导致大量连接被拒绝。
- 队列过大:过大的队列会占用更多内核资源,可能影响系统整体性能。
应用程序backlog参数取值根据实际业务访问量进行测试验证设置
sysctl -w net.core.somaxconn=65535
echo "net.core.somaxconn = 65535" >> /etc/sysctl.conf
1.1.5 合理设置 net.ipv4.tcp_max_syn_backlog 大小
sysctl -w net.ipv4.tcp_max_syn_backlog = 8192
echo "net.ipv4.tcp_max_syn_backlog=8192" >> /etc/sysctl.conf
在Linux系统中,net.ipv4.tcp_max_syn_backlog是一个重要的内核参数,用于设置系统允许的最大SYN队列长度。SYN队列是指等待客户端完成3次握手过程的连接请求队列。
增加net.ipv4.tcp_max_syn_backlog的值可以承载更多的等待连接,从而减少连接被拒绝的可能性。
推荐值:根据系统的实际负载和性能要求进行合理配置。过高的值可能会消耗过多的系统资源,而过低的值则可能导致连接请求被拒绝。。但是,不建议将这个值设置得过大,浪费过多的系统资源。
1.1.6 配置端口范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"
echo"net.ipv4.ip_local_port_range = 1024 65535" >> /etc/sysctl.conf
1.1.7 配置参数vm.overcommit_memory
sysctl -w vm.overcommit_memory=1
echo " vm.overcommit_memory = 1" >> /etc/sysctl.conf
在 Redis 中,此vm.overcommit_memory设置对于通过 fork 系统调用使用后台保存时管理内存分配至关重要。如果设置为零,则如果没有足够的可用 RAM 来复制所有父内存页面,则 fork 可能会失败。设置vm.overcommit_memory为 1 可让 Linux 更乐观地分配内存,这有利于 Redis 确保即使可用内存有限也能顺利进行后台保存过程。要配置此项,可以将其添加vm.overcommit_memory = 1到/etc/sysctl.conf文件中,然后运行sysctl -w vm.overcommit_memory=1或重新启动系统以应用更改。
1.1.8 合理设置参数vm.max_map_count
sysctl -w vm.max_map_count=65530
echo "vm.max_map_count = 65530" >> /etc/sysctl.conf
vm.max_map_count是Linux内核参数,控制进程能拥有的内存映射区域(Memory-Mapped Regions)最大数量。这个参数对于需要大量内存映射的应用很重要,比如Elasticsearch、MongoDB或者Redis。特别是当用户使用Redis的持久化功能时,如果子进程在AOF重写或RDB保存时遇到无法分配内存的问题,可能需要调整这个值。
默认值:通常为 65530,如果超过默认限制,会导致错误:
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
7369:M 22 Feb 2022 01:02:09.012 # Guru Meditation: Redis aborting for OUT OF MEMORY. Allocating 122916 bytes! #server.c:5968
- 不要过度调高:过高的值可能导致内存碎片或系统不稳定。
- 容器环境:若在容器内运行相关应用,需确保宿主机的 vm.max_map_count 已调整。
- 监控内存映射:使用 pmap 或 /proc/[pid]/maps 观察进程的内存映射使用情况。
二、 使用建议参考
2.1 测试 Redis 服务器响应延迟
执行以下命令,就可以测试出该实例 60 秒内的最大响应延迟:
redis-cli -h 127.0.0.1 -p 6379 --intrinsic-latency 60
可以使用以下命令,查看一段时间内 Redis 的最小、最大、平均访问延迟:
redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1
以上两条命令不要随便在生产实例上使用
2.2 避免使用复杂度过高的命令
通过查看慢日志,可以知道在那些时间点,执行了哪些比较耗时的命令。
SLOWLOG get 20
如果应用程序执行的 Redis 命令有以下特点,那么有可能会导致操作延迟变大:
经常使用 O(N) 以上复杂度的命令,例如 LRANGE、SORT、SUNION、ZUNIONSTORE 聚合类命令等等
使用 O(N) 复杂度的命令,但 N 的值非常大
第一种情况导致变慢的原因在于,Redis 在操作内存数据时,时间复杂度过高,要花费更多的 CPU 资源。
第二种情况导致变慢的原因在于,Redis 一次需要返回给客户端的数据过多,更多时间花费在数据协议的组装和网络传输过程中。
如果你的应用程序操作 Redis 的 OPS 不是很大,但 Redis 实例的 CPU 使用率却很高,那么很有可能是使用了复杂度过高的命令导致的。
另外,Redis 是单线程处理客户端请求的,如果你经常使用以上命令,那么当 Redis 处理客户端请求时,若前面某个命令执行比较耗时,就会导致后面的请求发生排队,响应延迟也会变长。
可以使用以下方法优化业务:
- 尽量不使用 O(N) 以上复杂度过高的命令,对于数据的聚合操作,放在客户端做
- 执行 O(N) 命令,保证 N 尽量的小(推荐 N <= 200),每次获取尽量少的数据,让 Redis 可以及时处理返回
2.3 避免使用bigkey或hotkey
redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01 #扫描大key
redis-cli -h 127.0.0.1 -p 6379 --hotkeys -i 0.01 #扫描热key
以上命令需要分析redis问题时执行且在业务流量低峰时
对于 set hash list 这种类型的大key,使用不了redis cluster多slot分片的优势,集群数据分布粒度为Key级,集群还是将这些key 哈希到某一个节点分片,对于客户端来说跟使用单个节点实例没有区别。
Bigkey和hot产生原因:
未正确使用redis cluster、业务规划不足、无效数据的堆积、访问量突增等都会产生大Key与热Key,如:
- 大key
- 在不适用的场景下使用redis cluster,易造成Key的value过大,如使用String类型的Key存放大体积二进制文件型数据;
- 业务上线前规划设计不足,没有对Key中的成员进行合理的拆分,造成个别Key中的成员数量过多;
- 未定期清理无效数据,造成如HASH类型Key中的成员持续不断地增加;
- 使用LIST类型Key的业务消费侧发生代码故障,造成对应Key的成员只增不减。
- 热key
- 预期外的访问量陡增,如突然出现的爆款商品、访问量暴涨的热点新闻、直播间某主播搞活动带来的大量刷屏点赞、游戏中某区域发生多个工会之间的战斗涉及大量玩家等。
优化点:
业务应用避免使用 bigkey,如大的set、list、hash等,特别是高并发场景下
bigkey 在很多场景下会产生性能问题。如,bigkey 在分片集群模式下,对于数据的迁移也会有性能影响,以及数据过期、数据淘汰、透明大页,都会受到 bigkey 的影响对大Key进行拆分
例如将含有成千上万成员的一个 Key拆分为多个 Key,并确保每个Key的成员数量在合理范围。在集群架构中,拆分大Key能对数据分片间的内存平衡起到显著作用,同时也能分担单实例节点压力,降低请求延迟,提高整体吞吐量。避免使用KEYS命令对整个键空间进行扫描
使用异步UNLINK而不是同步来删除大键(bigkey)
对于hotkey 建议在客户端设置带过期时间的localcache,来缓存hotkey的结果,无需请求redis集群节点分片。注意如果热点Key的值发生改变,localcache缓存需过期后才会更新。
2.4 避免key的集中过期
检查你的业务代码,是否存在集中过期 key 的逻辑
优化点:
- 集中过期 key 增加一个随机过期时间,把集中过期的时间打散,降低 Redis 清理过期 key 的压力,比如,在过期时间点后的一段时间内增加随机时间
- 如果使用的 Redis 是 4.0 以上版本,可以开启 lazy-free 机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程
配置项:lazyfree-lazy-expire yes,根据业务需要进行配置
2.5 控制 Redis 实例的内存
注意点:
- 内存大小
保持在 8G – 10G 以下,执行 fork 的耗时与实例大小有关,实例越大耗时越久,容易发生异常、阻塞、执行命令时间增大等性能问题
2.6 合理配置数据持久化策略
在 slave 节点执行 RDB 备份,推荐在低峰期执行,而对于丢失数据不敏感的业务(如把 Redis 当做纯缓存使用),可以关闭 AOF 和 AOF rewrite
2.7 Redis 建议实例不要部署在虚拟机上
fork 的耗时也与系统有关,虚拟机比物理机耗时更久
2.8 物理服务器配置性能建议
硬件层面:
- 使用 SSD 磁盘,提高磁盘的 IO 能力
- 使用cpu主频高性能强的cpu型号
- 使用频率高带宽大的内存
- 建议集群中节点采用相同机器型号配置规格,避免某些节点产生性能瓶颈
Redis 机器最好专项专用,只用来部署 Redis 实例,不要部署其他应用程序,避免其它程序占用 CPU、内存、磁盘资源,导致分配给 Redis 的资源不足而受到影响。
2.9 底层网络建议
如果网络 IO 存在瓶颈,那么也会严重影响 Redis 的性能
建议集群实例在同一个内网vpc下,使用万兆网络及以上,延迟越低越好
避免redis实例流量过载,超过服务器网络负载能力
2.10 Hash Tags注意点
避免在Key名称中使用{}
若Key名称中包含{},例如{item}id1,则redis cluster仅会对{}中的内容进行Slot计算并选择数据分片节点。若存在{item}id1、{item}id2、{item}id3等大量Key,由于{}中的内容相同,上述Key均会被分配至同一数据分片节点,导致该数据分片节点的内存资源、性能消耗大幅升高。
2.11 高消耗命令建议
业务侧需减少或禁止使用高消耗命令
不同的命令具有不同的复杂度,高复杂度的命令会消耗大量性能资源,例如HGETALL命令的复杂度为O(n),该命令会随着您存储的Field越多,消耗越大。同时,简单的SET或GET命令也会在Value过大时,消耗大量数据分片节点的性能资源。
2.12 数据倾斜建议
在Redis集群架构实例中,若个别数据分片节点的内存使用率、CPU使用率或带宽使用率等性能指标远远高于其他数据分片节点,该集群可能已产生数据倾斜。
数据倾斜严重时,会导致实例在整体内存使用率不高的情况下,发生内存逐出(Key eviction)、内存溢出OOM(Out Of Memory)、实例响应时间上升等异常情况。
产生原因:
Redis集群架构实例作为一个分布式系统,整个数据库空间会被分为16384个槽(Slot),每个数据分片节点将存储与处理指定Slot的数据(Key),例如3分片集群实例,3个分片分别负责的Slot为:[0,5460]、[5461,10922]、[10923,16383]。当用户写入或更新数据时,客户端会通过CRC算法计算出Key所属的Slot,具体公式为Slot=CRC16(key)%16384,并将数据写入Slot所属的数据分片节点。通常情况下,各数据分片节点的Key数量是均匀分布的,同时内存使用率、CPU使用率等性能指标也是相近的。
但在使用数据库的过程中,可能会由于前期规划不足、bigkey、不规范的数据写入及突发的访问量(hotkey)等,造成内存倾斜、cpu使用倾斜、流量倾斜,最终引起数据倾斜。
2.13 业务客户端建议具备连接失败重连机制
由于受网络和运行环境的影响,redis可能会遇到暂时性的故障,例如瞬时的网络抖动、服务暂时不可用、服务繁忙导致超时等。通过设计自动重试机制可以大幅避免此类故障,保障操作的成功执行。如需确保请求的成功执行,请为客户端设计重试机制
2.14 客户端连接池参数设置建议
应该使用长连接操作 Redis,避免频繁的短连接。建议使用成熟的redis cluster客户端使用连接池的方式访问redis cluster。
合理设置maxTotal(最大连接数):
- 业务希望的Redis并发量;
- 客户端执行命令时间;
- Redis资源,例如nodes (如应用服务器/进程个数等) * maxTotal不能超过Redis的maxclients最大连接数
- 资源开销,例如虽然希望控制空闲连接,但又不希望因为连接池中频繁地释放和创建连接造成不必要的开销。
假设一次命令时间,即borrow|return resource加上执行命令 ( 含网络耗时)的平均耗时约为1ms,一个连接的QPS大约是1s/1ms = 1000,而业务期望的单个Redis的QPS是20000(业务总的QPS/Redis分片(这里指master)个数),那么理论上需要的资源池大小(即MaxTotal)是20000 / 1000 = 20。
但事实上这只是个理论值,除此之外还要预留一些资源,所以maxTotal可以比理论值大一些。这个值不是越大越好,一方面连接太多会占用客户端和服务端资源,另一方面对于Redis这种高QPS的服务器,如果出现bigkey的阻塞,即使设置再大的资源池也无济于事。
maxIdle与minIdle
maxIdle实际上才是业务需要的最大连接数,maxTotal 是为了给出余量,所以 maxIdle 不要设置得过小,否则会有new connect(新连接)开销,而minIdle是为了控制空闲资源检测。
连接池的最佳性能是maxTotal=maxIdle,这样就避免了连接池伸缩带来的性能干扰。如果业务存在突峰访问,建议设置这两个参数的值相等;如果并发量不大或者maxIdle设置过高,则会导致不必要的连接资源浪费。
您可以根据实际总QPS和调用Redis的客户端规模整体评估每个节点所使用的连接池大小。
使用监控获取合理值
在实际环境中,比较可靠的方法是通过监控来尝试获取参数的最佳值。可以考虑通过JMX等方式实现监控,从而找到合理值。
2.15 双写两个redis cluster一致性问题
如业务上允许存储的数据不一致,则可以采用双写,否则不建议业务端双写