文章目录
一、Linux 配置优化
内存分配控制
- Redis 设置合理的 maxmemory,保证机器有 20% ~ 30% 的闲置内存。
- 集中化管理 AOF 重写和 RDB 的 bgsave。
- 设置 vm.overcommit_memory=1,防止极端情况下会造成 fork 失败。
OOM killer
- OOM killer 会在可用内存不足时选择性地杀掉用户进程。
- 对于 Redis 所在的服务器来说,可以
将所有 Redis 的 oom_adj 设置为最低值或者稍小的值
,降低被 OOM killer 杀掉的概率。
使用 NTP
- NTP(Network Time Protocol,网络时间协议)是一种
保证不同机器时钟一致性
的服务。一般公司里都会有 NTP 服务用来提供标准时间服务,从而达到纠正时钟的效果。
二、flushall / flushdb 误操作
缓存与存储
- 被误操作 flush 后,根据当前 Redis 是缓存还是存储使用策略有所不同:
借助 AOF 机制恢复
- Redis 执行了 flush 操作后,AOF 持久化文件会受到什么影响呢,如下:
- 也就是说,虽然 Redis 中的数据被清掉了,但是
AOF 文件还保存着 flush 操作之前完整的数据
,这对恢复数据是很有帮助的。
RDB 有什么变化
-
1)如果
没有开启 RDB 的自动策略
,也就是配置文件中没有类似如下配置save 900 1 save 300 10 save 60 10000
-
那么除非手动执行过 save、bgsave 或者发生了主从的全量复制,否则 RDB 文件也会保存 flush 操作之前的数据,可以作为恢复数据的数据源。注意问题如下:
-
2)如果
开启了 RDB 的自动策略
,由于 flush 涉及键值数量较多,RDB 文件会被清除意味着使用 RDB 恢复基本无望。
三、安全的 Redis
Redis 密码机制
伪装危险命令
- Redis 中包含了很多“危险”命令,一旦生产上错误使用或者误操作,后果不堪设想,例如:
- Redis 提供了
rename-command
配置解决了这个问题,例如添加如下配置:rename-command flushall abcabcabc
- 那么再执行 flushall 的话,会收到 Redis 不认识 flushall 的错误提示,说明成功的对 flushall 进行了伪装。
- 而如果执行 abcabcabc ,那么就可以实现 flushall 功能了。
防火墙
- 可以使用防火墙限制输入和输出的 IP 或者 IP 范围、端口或者端口范围。
定期备份数据
- 定期备份数据能够在一定程度挽回一些损失,定期备份持久化数据是一个比较好的习惯。
不使用默认端口
使用非 root 用户启动
四、处理 bigkey
bigkey 是指 key 对应的 value 所占的内存空间比较大
,例如一个字符串类型的 value 可以最大存到 512MB,一个列表类型的 value 最多可以存储 2^32 - 1 个元素。如果按照数据结构来细分的话,一般分为两种:- 字符串类型:体现在
单个 value 值很大
,一般认为超过 10 KB 就是 bigkey,但这个值和具体的 OPS 相关。 - 非字符串类型:哈希、列表、集合、有序集合,体现在
元素个数过多
。
- 字符串类型:体现在
bigkey 的危害
- 内存空间不均匀:例如在 Redis 集群中,bigkey 会造成节点的内存空间使用不均匀。
- 超时阻塞:由于 Redis 单线程的特性,操作 bigkey 比较耗时,也就意味着阻塞 Redis 可能性增大。
- 网络阻塞:每次获取 bigkey 产生的网络流量较大。
如何发现
redis-cli --bigkeys
可以命令统计 bigkey 的分布。但是生产环境中,开发和运维人员更希望自己可以定义 bigkey 的大小而且更希望找到真正的 bigkey 都有哪些
,这样才可以去定位、解决、优化问题。- 判断一个 key 是否为 bigkey,只需要
执行 debug object key 查看 serializedlength 属性
即可,它表示 key 对应的 value 序列化之后的字节数。 - 在实际的生产环境中发现 bigkey 的两种方式如下:
- 被动收集:许多开发人员确实可能对 bigkey 不了解或重视程度不够,但是这种 bigkey 一旦大量访问,很可能就会带来命令慢查询和网卡跑满问题,开发人员
通过对异常的分析通常能找到异常原因可能是 bigkey
,这种方式并不推荐,但是在实际生产环境中却大量存在,建议修改 Redis 客户端,当抛出异常时打印出所操作的 key,方便排查 bigkey 问题。 - 主动检测:
scan + debug object
,如果怀疑存在bigkey,可以使用scan命令渐进的扫描出所有的key,分别计算每个 key 的 serializedlength,找到对应 bigkey 进行相应的处理和报警,这种方式是比较推荐的方式。
- 被动收集:许多开发人员确实可能对 bigkey 不了解或重视程度不够,但是这种 bigkey 一旦大量访问,很可能就会带来命令慢查询和网卡跑满问题,开发人员
如何删除
- 首先,无论是什么数据结构,del 命令都能将其删除。但是经过上面的分析你一定不会这么做,
删除 bigkey 通常来说会阻塞 Redis 服务
。 - 这个时候就需要 scan 命令的若干类似命令拿出来:
sscan、hscan、zscan
。 - string
- 对于 string 类型使用 del 命令一般不会产生阻塞
- hash、list、set、sorted set
public void delBigHash(String bigKey) { // 游标 Jedis jedis = new Jedis("127.0.0.1", 6379); String cursor = "0"; while (true) { ScanResult<Map.Entry<String, String>> scanResult = jedis.hscan(bigKey, cursor, new ScanParams().count(100)); // 每次扫描后获取新的游标 cursor = scanResult.getStringCursor(); // 获取扫描结果 List<Map.Entry<String, String>> list = scanResult.getResult(); if (list == null || list.size() == 0) { continue; } String[] fields = getFieldsFrom(list); // 删除多个field jedis.hdel(bigkey, fields); // 游标为0时停止 if (cursor.equals("0")) { break; } } //最终删除key jedis.del(bigKey); } /** * 获取 field 数组 * @param list * @return */ private String[] getFieldsFrom(List<Map.Entry<String, String>> list) { List<String> fields = new ArrayList<>(); for (Map.Entry<String, String> entry : list) { fields.add(entry.getKey()); } return fields.toArray(new String[fields.size()]); }
- Redis 4.0 新增了非常实用的
惰性删除 lazy free 特性
,从根本上解决了 bigkey(主要指定元素较多集合类型key)删除的风险
五、寻找热点 key
热门新闻事件或商品通常会给系统带来巨大的流量,对存储这类信息的 Redis 来说却是一个巨大的挑战。以 Redis Cluster 为例,它会造成整体流量的不均衡,个别节点出现 OPS 过大的情况,极端情况下热点 key 甚至会超过 Redis 本身能够承受的 OPS,因此寻找热点 key 对于开发和运维人员非常重要。下面从以下几个方面分析热点 key。
统计热点 key
- 客户端
- 代理端
- Redis 服务端
- 机器
解决热点 key 问题
拆分复杂数据结构
:如果当前 key 的类型是一个二级数据结构,例如哈希类型。如果该哈希元素个数较多,可以考虑将当前 hash 进行拆分,这样该热点 key 可以拆分为若干个新的 key 分布到不同 Redis 节点上,从而减轻压力。迁移热点key
:以 Redis Cluster 为例,可以将热点 key 所在的 slot 单独迁移到一个新的 Redis 节点上,但此操作会增加运维成本。本地缓存加通知机制
:可以将热点 key 放在业务端的本地缓存中,因为是在业务端的本地内存中,处理能力要高出 Redis 数十倍,但当数据更新时,此种模式会造成各个业务端和 Redis 数据不一致,通常会使用发布订阅机制来解决类似问题。
来源:《Redis 开发与运维》第 12 章 开发运维的“陷阱”
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。