共计 3318 个字符,预计需要花费 9 分钟才能阅读完成。
一、键值设计
1、key 名设计
三大建议
- 可读性和可管理性
以业务名(或数据库名)为前缀(防止 key 冲突),用冒号分隔,比如业务名: 表名:id
- 简洁性
保证语义的前提下,控制 key 的长度,当 key 较多时,内存占用也不容忽视 (embstr 3-39)
- 不包含特殊字符
反例:包含换行 等
2、value 设计
- 拒绝 bigkey
- 选择合适的数据结构
- 过期设计
string 类型控制在 10kb 之内
hash、list、set、zset 元素个数不超过 5000
反例 一个包含几百万个元素的 list、hash 等,一个巨大的 json 字符串
bigkey 的危害
- 网络阻塞
- 慢查询
- 节点数据不均衡
- 反序列消耗
Redis 客户端本身不负责序列化
应用频繁序列化和反序列化 bigkey:本地缓存或者 Redis 缓存
bigkey 发现
- 应用异常
- redis-cli –bigkeys
- scan + debug object
- 主动报警
- 内核热点 key 问题优化
3、bigkey 问题
bigkey 的删除
- 阻塞:注意隐性删除(rename、过期等)
- lazy delete(unlink 命令)
bigkey 的预防
- 优化数据结构:例如二级拆分
- 物理隔离或者万兆网卡:不是治标方案
- 命令优化:例如 hgetall->hget、hscan
- 报警和定期优化
键值生命周期
- 周期数据需要设置过期时间,object idle time 可以找垃圾 key-value
- 过期时间不宜集中:缓存穿透和雪崩问题
命令优化
- O(N)以上的命令关注 N 的数量
- 禁用命令 keys flush flushdb 等
- 合理使用 select redis 的多数据库较弱,使用数字进行区分 很多客户端支持较差
- Redis 事物功能较弱,不建议过多使用
- Redis 集群版本在使用 Lua 上有特殊要求
- 必要情况下使用 monitor 命令时,要注意不要长时间使用
客户端优化
避免多个应用使用一个 Redis 实例
不相干的业务拆分,公共数据做服务化
使用连接池
最大连接池的设置
maxIdle 接近 maxTotal 即可
业务希望 Redis 达到的并发量
客户端执行命令时间
Redis 资源
资源开销
例子:一次命令时间的平均耗时为 1ms,一个连接的 QPS 大约是 1000
业务期望的 QPS 是 50000
理论的 maxTotal = 50000/1000 = 50 个
二、内存优化
1、内存消耗
- 内存使用统计
- 内存消耗划分
- 子进程内存消耗
内存划分:自身内存(800k 左右),对象内存,缓存内存,Lua 内存,内存碎片
内存消耗:自身内存,缓冲内存 (客户端缓冲器,复制缓冲区,AOF 缓冲区),对象内存(key,value)
客户端缓冲区
- 普通客户端
输入缓冲区,最大 1GB,超过后会被强制断开,不可动态设置
输出缓冲区配置 client-output-buffer-limit <class> <hard limiy> <soft limit> <soft seconds>
class 客户端类型,分为三种 普通客户端 slave 从节点客户端 pubsub 发布订阅客户端
hard limit 如果客户端使用的输出缓冲区大于 hard limit 客户端会被立即关闭
soft limit 和 soft seconds 如果客户端使用的输出缓冲区超过了 soft limit 并且持续了 soft seconds 客户端会被立即关闭
默认 client-output-buffer-limit normal 0 0 0
默认 没有限制客户端缓冲
注意 防止大的命令或者 monitor
- slave 客户端
默认 client-output-buffer-limit slave 256mb 64mb 60
阻塞 主从延迟较高,或者从节点过多
注意 主从网络,从节点不要超过 2 个
- pubsub 客户端
默认 client-output-buffer-limit pubsub 32mb 8mb 60
阻塞 生产大于消费
注意 根据实际场景适当调试
缓冲内存
复制缓冲区:此部分内存独享,考虑部分复制,默认是 1MB,可以设置更大
AOF 缓冲区:AOF 重写器键,AOF 的缓冲区,没有容量限制
对象内存
key:不要过长,量大不容忽视(redis3:embstr 39 字节)
value:ziplist,intset 等优化方式
内存碎片
必然存在 jemalloc
优化方式:
避免频繁更新操作:append setrange 等
安全重启 例如 redis sentinel 和 redis-cluster 等
子进程内存消耗
必然存在 fork(bgsave 和 bgrewriteaof)
优化方式:
去掉 THP 特性
观察写入量
2、内存管理
- 设置内存上限
定义实例最大内存,便于管理机器内存,一般预留 30%
- 动态调整内存上限
config set maxmemory 6GB
config rewrite
- 内存回收策略
删除过期键值
惰性删除:访问 key->expired dict -> del key
定时删除:每秒运行 10 次,采样删除
内存溢出
超过 maxmemory 后触发响应策略,由 maxmemory-policy 控制
3、内存优化
内存分布
自身内存,缓冲内存,对象内存
合理选择数据结构
独立统计:集合,MitMaps,HyperLogLog
客户端内存优化
客户端缓冲区
输入缓冲区:最大 1G
一次内存暴增 可能原因:
- 批量写入
- 主从不一致
- 客户端溢出
处理和预防:
找到对应的业务方直接干掉
运维层面:线上 Redis 禁用 monitor,适度限制缓冲区大小
开发层面:理解 monitor 的原理,也可以短暂寻找热点 key,使用 CacheCloud 可以直接监控到
4、Redis 运维
Linux 内核优化
overcommit 含义:
0 表示内核将检查是否由足够的可用内存,如果有足够的可用内存,内存申请通过,否则内存申请不通过,并把错误返回给应用进程
1 表示内核运行超量使用内存知道用完为止
2 表示内核坚决不过两的使用内存,即系统整个内存地址空间不超过 swap+50% 的 RAM 值,50% 是 overmit_ratio 默认值,此参数同样支持修改
获取
cat /proc/sys/vm/overcommit_memory
设置
echo “vm.overcommit_memory=1” >> /etc/sysctl.conf
sysctl vm.overcommit_memory=1
最佳实践:
- Redis 设置合理的 maxmory,保证机器有 20%-30% 的闲置内存
- 集中化管理 AOF 重写的 RDB 的 bgsave
swappiness 最佳实践
- vm.swappiness=1
物理内存充足的时候,使 Redis 足够快
物理内存不足的时候,避免 Redis 死掉
- THP
作用:加速 fork
建议:禁用,可能产生更大的内存消耗
设置方法:echo never > /sys/kernel/mm/transparent_hugepage/enabled
- OOM killer
作用:内存使用超出,操作系统按照规则 kill 掉某些进程
配置方法:/proc/{progress_id}/oom_adj 越小,被杀掉的概率越小
运维经验:不要过度依赖此特性,应该合理管理内存
安全的 Redis
被攻击 Redis 的特征:
- redis 所在的机器有外网 IP
- Redis 以默认端口 6379 为启动端口,并且是对外网开放的
- Redis 是以 root 用户启动的
- Redis 没有设置密码
- Redis 的 bind 设置为 0.0.0.0 或者 ””
攻击方式:
- flushall
- set crakit id_rsa.pub
- config set dir
- config set dbfilename
- save
安全七法则
- 设置密码:
服务端配置:requirepass 和 masterauth
客户端连接:auth 命令和 - a 参数
相关建议
密码要足够复杂,防止暴力破解
masterauth 不要忘记
auth 还是通过铭文传输
- 伪装危险命令
服务端配置:rename-command 为空或者随机字符串
客户端连接:不可用或者使用指定随机字符串
相关建议
不支持 config set 动态设置
RDB 和 AOF 如果包含了 rename-command 之前的命令,将无法使用
config 命令本身是再 Redis 内核会使用到,不建议使用
- bind
服务端配置:bind 限制的是网卡,并不是 IP
相关建议
bind 不支持 config set
bind 127.0.0.1 需要谨慎
- 防火墙:杀手锏
- 定期备份
- 不使用默认端口,防止被若攻击杀掉
- 使用非 root 用户启动
热点 key
- 客户端
- 代理端
- 服务端
- 机器搜集