详解 Redis 中 big keys 发现和解决
在使用 Redis 时,可能会出现请求响应慢、网络卡顿、数据丢失的情况。排查问题的时候,发现是 big keys 的问题。
什么是 big keys
在 Redis 中,一个字符串类型最大可以达到 512MB,其他非字符串类型的集合类型(list、set、hash、zset等)可以存储 40 亿个(2^32-1),但在实际业务场景中,并不需要这么大的内存。而且对于一个请求量大的互联网软件,对数据的大小要求更加的严格。如果达到如下标准,就可以认定是 big keys 了:
- String 类型的 key 对应的值超过 5 MB。
- list、set、hash、zset等集合类型,集合元素个数超过 2000。
以上对 big keys 的判断标准并不是唯一,只是一个大题的标准。在实际业务开发中,对 big keys 的判断是需要根据具体的使用场景做不同的判断。比如操作某个 key 导致请求响应时间变慢,那么这个 key 就可以判定成 big keys。
big keys 是如何产生的
一般来说,big keys 的产生都是由于程序的设计不当,或者对数据的规模没有一个大体的估算。比如:
- 统计类:例如统计某个网站的访问用户信息,网站的访问量越来越多,这个 key 的元素也会越来越大。变成了 big keys。
- 社交类:例如某个大V微博粉丝量很大,如果不做合理的设计,也是 big keys。
- 缓存类:一般缓存类的信息访问都比较频繁,是将从数据库查询出来的数据序列化到Redis缓存中,这里的缓存如果设计不当,或者为了方便,把所有的数据都存在一个 key 下,或者随着业务的扩大,对应的缓存也增多,也是形成 big keys。
以上几种类型都是在实际运维中遇到的。在开发中需要根据预估的数据大小来合理的设计缓存数据。
big keys 的危害
在系统中如果存在 big keys,会导致请求数据响应变慢、请求超时或者系统不稳定。
1、响应变慢、超时阻塞
Redis 是单线程工作的,同一时间只能处理一个请求,操作 big keys 时比较耗时,请求响应也变慢。其他请求也处于阻塞状态,导致请求超时。除了查询 big keys 比较耗时,删除 big keys 也会导致一样的问题。
2、网络拥塞
请求单个 big keys 产生的网络流量比较大,假设一个 big keys 为 1MB,客户端每秒访问量是 1000,那么每秒产生 1000MB 的流量,普通的千兆网卡承受不了这么大的流量。而且一般会在单机部署多个Redis实例,一个 big keys 可能也会影响其他实例。
3、内存分布不均
Redis 集群模式中,key根据不同的hash嘈分配到不同的节点上,当大部分的 big keys 分布在同一个节点,导致内存倾斜在同一个节点上,内存分布不均。在水平扩容时,需要以最大容量的节为准,浪费内存。
如何发现 big keys
Redis4.0 后提供了 --bigkeys命令,比如:
./redis-cli --bigkeys
获取每个数据类型最大的 big keys,同时给出每个类型键的个数和平均大小。因为 Redis 是单线程工作的,为了减少对线上请求的影响,执行--bigkeys命令需要注意一下几点:
- 最好在 slave 节点执行,因为
--bigkeys也是扫描数据,会造成其他线程阻塞。 - 使用
--i参数,降低扫描的执行速度,比如--i 0.1表示 100 毫秒执行一次。 - 只能统计每个数据类型最大的数据。

big keys 处理
异步删除 big keys
找到 big keys 之后,首先需要删除对应的big keys,但是使用 del 命令删除 big keys 是比较耗时的。Redis4.0 后可以使用 unlink 删除,和 del 命令相比,unlink 是非阻塞的异步删除。
非字符串的 big keys,使用 hscan、sscan、zscan 方式渐进式删除,同时要注意防止big keys 过期时间自动删除问题(例如一个 200 万的 zset 设置1小时过期,会触发del操作,造成阻塞)。
big key 拆分
字符串类型的数据是减少字符串的长度,将一个字符串拆成几个小的字符串。非字符串的是减少元素数量。这些都是讲一个 key 拆成多个 key,比如:
- 字符串类型的数据,根据数据的属性拆分。比如商品信息,根据的类别拆分 key。
- 非字符串类型的数据,根据数据的属性拆分,可以按照日期拆分,比如每天登录人的集合,按照日期拆分,key20220101、key20220102.
如果 big keys 无法避免,那获取数据尽量不要把所有的数据都取出来,就使用分段的方式取出数据。删除的方式也类似,分段删除数据。
总结
- big keys 会造成请求变慢、网络阻塞、数据丢失的问题。
- big keys 是字符串字节达到很大的数量(比如 5MB),非字符串类型元素类型达到 1000 个都可以判定成 big keys,具体还需要看具体的场景。
- big keys 的产生可能由于设计不合理或者对数据大小估算错误,导致数据偏大。
- 解决 big keys 先紧急使用异步删除 unlink 命令删除缓存。然后将单个 key 拆分成多个小 key。
- 如果无法避免 big keys,就使用分段查询的方式查询数据。
- 要从几个方面分析,
- big keys 会带来哪些问题。
- big keys 一般怎么产生的,线上如果产生了big key,线上先怎么紧急处理。
- 有哪些优化方案,各自有什么应用场景。
参考
详解 Redis 中 big keys 发现和解决的更多相关文章
- 详解Redis中两种持久化机制RDB和AOF(面试常问,工作常用)
redis是一个内存数据库,数据保存在内存中,但是我们都知道内存的数据变化是很快的,也容易发生丢失.幸好Redis还为我们提供了持久化的机制,分别是RDB(Redis DataBase)和AOF(Ap ...
- 详解Redis中两种持久化机制RDB和AOF
redis是一个内存数据库,数据保存在内存中,但是我们都知道内存的数据变化是很快的,也容易发生丢失.幸好Redis还为我们提供了持久化的机制,分别是RDB(Redis DataBase)和AOF(Ap ...
- 反射实现Model修改前后的内容对比 【API调用】腾讯云短信 Windows操作系统下Redis服务安装图文详解 Redis入门学习
反射实现Model修改前后的内容对比 在开发过程中,我们会遇到这样一个问题,编辑了一个对象之后,我们想要把这个对象修改了哪些内容保存下来,以便将来查看和追责. 首先我们要创建一个User类 1 p ...
- Redis学习——详解Redis配置文件(三)
一.Redis脚本简介 在我们介绍Redis的配置文件之前,我们先来说一下Redis安装完成后生成的几个可执行文件: redis-server .redis-cli .redis-benchmark ...
- (转载)详解Javascript中prototype属性(推荐)
在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不存在类(Class)的概念的,javascript中不 ...
- 详解Redis持久化(RDB和AOF)
详解Redis持久化(RDB和AOF) 什么是Redis持久化? Redis读写速度快.性能优越是因为它将所有数据存在了内存中,然而,当Redis进程退出或重启后,所有数据就会丢失.所以我们希望Red ...
- 【转】详解JavaScript中的this
ref:http://blog.jobbole.com/39305/ 来源:foocoder 详解JavaScript中的this JavaScript中的this总是让人迷惑,应该是js众所周知的坑 ...
- 详解javascript中的this对象
详解javascript中的this对象 前言 Javascript是一门基于对象的动态语言,也就是说,所有东西都是对象,一个很典型的例子就是函数也被视为普通的对象.Javascript可以通过一定的 ...
- 详解Vue中的computed和watch
作者:小土豆 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.cn/user/2436173500265335 1. 前言 作为一名Vue ...
- 详解JavaScript中的原型
前言 原型.原型链应该是被大多数前端er说烂的词,但是应该还有很多人不能完整的解释这两个内容,当然也包括我自己. 最早一篇原型链文章写于2019年07月,那个时候也是费了老大劲才理解到了七八成,到现在 ...
随机推荐
- 类和实例,super()函数
class Foo: def __init__(self, name): self.name = name def ord_func(self): """定义实例方法,至 ...
- Fluent插件管理
官方文档地址:https://docs.fluentd.org/deployment/plugin-management 命令fluent-gem是Fluentd用来安装插件的,它是gem命令的包装器 ...
- Spring boot定义多个配置文件并自由切换
在resource目录下定义三个配置文件 (properties文件已被我注销,配置文件建议用yml,如果properties文件与yml文件同时存在,SpringBoot会优选加载propertie ...
- C#-4 方法
一 何为方法 方法是一块具有名称的代码,是类的函数成员. 方法主要分为方法头和方法体. void Method() { 语句1: 语句2: } 二 类型推断和var关键字 var sum = 15; ...
- POJ3311 Hie with the Pie(状压DP,Tsp)
本题是经典的Tsp问题的变形,Tsp问题就是要求从起点出发经过每个节点一次再回到起点的距离最小值,本题的区别就是可以经过一个节点不止一次,那么先预处理出任意两点之间的最短距离就行了,因为再多走只会浪费 ...
- TensorFlow搭建模型方式总结
引言 TensorFlow提供了多种API,使得入门者和专家可以根据自己的需求选择不同的API搭建模型. 基于Keras Sequential API搭建模型 Sequential适用于线性堆叠的方式 ...
- Docker安装MongoDB并使用Navicat连接
MongoDB简介: MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案.是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库 ...
- Azure Kubernetes(AKS)部署及查看应用资源
简介 上一篇文章讲解了如何使用Azure DevOps持续部署应用到Azure Kubernetes上.但是部署是否成功?会不会遇到什么问题?项目运行中是否会出现问题?我们该怎么样查看这些问题,并且对 ...
- JSTL组件的下载链接地址
配置JSTL和下载jar包 JSTL的安装包 下载地址:http://tomcat.apache.org/download-taglibs.cgi 在下载页面找到JSTL的规范和实现的两个jar包,如 ...
- 二、redis介绍
二.redis介绍 2.1.定义 Redis(Remote Dictionary Server ,远程字典服务) 是一个使用ANSI C编写的开源.支持网络.基于内存.可选持久性的键值对存储数据库,是 ...