1 数据倾斜
1.1 定义
对于集群系统,一般缓存是分布式的,即不同节点负责一定范围的缓存数据。我们把缓存数据分散度不够,导致大量的缓存数据集中到了一台或者几台服务节点上,称为数据倾斜。一般来说数据倾斜是由于负载均衡实施的效果不好引起的。
1.2 危害
如果发生了数据倾斜,那么保存了大量数据,或者是保存了热点数据的实例的处理压力就会增大,速度变慢,甚至还可能会引起这个实例的内存资源耗尽,从而崩溃。这是我们在应用切片集群时要避免的。
1.3 分类
-
数据量倾斜(bigkey导致倾斜)
某个实例上正好保存了 bigkey。bigkey 的 value 值很大(String 类型),或者是 bigkey 保存了大量集合元素(集合类型),会导致这个实例的数据量增加,内存资源消耗也相应增加。
-
数据访问倾斜(读取倾斜-热key问题)
虽然每个集群实例上的数据量相差不大,但是某个实例上的数据是热点数据,被访问得非常频繁
在 Redis(五):常见面试题目详解 中写过关于bigkey和hotkey的生产问题,但是只治标不治本,数据倾斜还是需要监控起来,这样在生成环境遇到问题时,才不至于措手不及。
2 BigKey
2.1 定义
通常以Key的大小和Key中成员的数量来综合判定,例如:
Key本身的数据量过大:一个String类型的Key,它的值为5 MB。
Key中的成员数过多:一个ZSET类型的Key,它的成员数量为10,000个。
Key中成员的数据量过大:一个Hash类型的Key,它的成员数量虽然只有1,000个但这些成员的Value(值)总大小为100 MB。
2.2 产生原因
在不适用的场景下使用Redis,易造成Key的value过大,如使用String类型的Key存放大体积二进制文件型数据;
业务上线前规划设计不足,没有对Key中的成员进行合理的拆分,造成个别Key中的成员数量过多;
未定期清理无效数据,造成如HASH类型Key中的成员持续不断地增加;
使用LIST类型Key的业务消费侧发生代码故障,造成对应Key的成员只增不减。
bigkey的产生主要是由于程序的设计不当所造成的,如以下几种常见的业务场景
社交类:粉丝列表,如果某些明星或者大v不精心设计下,必是bigkey。
统计类:例如按天存储某项功能或者网站的用户集合,除非没几个人用,否则必是bigkey。
缓存类:将数据从数据库load出来序列化放到redis里,这个方式经常常用,但有两个地方需要注意:第一,是不是有必要把所有字段都缓存;第二,有没有相关关联的数据。
由此可见,在程序设计中,我们要对数据量的增长和边界有一个基本性的评估,做好技术选型和技术架构。
2.3 危害
-
1.内存空间不均匀
在集群模式中,由于bigkey的存在,会造成主机节点的内存不均匀,这样会不利于集群对内存的统一管理,存在丢失数据的隐患。
-
2.超时阻塞
由于redis单线程的特性,操作bigkey通常比较耗时,也就意味着阻塞redis可能性越大,这样会造成客户端阻塞或者引起故障切换。慢查询通常就会有它们的身影。
-
3.网络拥塞
bigkey也就意味着每次获取要产生的网络流量较大。假设一个bigkey为1MB,客户端每秒访问量为1000,那么每秒产生1000MB的流量,对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾。
-
4.阻塞删除
有个bigkey,对它设置了过期时间,当它过期后会被删除,如果使用Redis 4.0之前的版本,过期key是同步删除,就会存在阻塞redis的可能性,而且这个过期删除不会从慢查询发现(因为这个删除不是客户端产生的,是内部循环事件)。4.0之后的版本会采用异步删除。
2.4 优化方案
2.4.1 拆分Bigkey
优化Bigkey的原则就是string减少字符串长度,list、hash、set、zset等减少元素数量。当我们知道哪些key是Bigkey时,可以把单个key拆分成多个key,比如以下拆分方式可以参考。
big list:list1、list2、...listN
big hash:可以做二次的hash,例如hash%100
按照日期拆分多个:key20220310、key20220311、key202203212
如果bigkey不可避免,也要思考一下要不要每次把所有元素都取出来(例如有时候仅仅需要hmget,而不是hgetall),删除也是一样,尽量使用优雅的方式来处理。
2.4.2 选择适合的数据类型。
例如:实体类型(要合理控制和使用数据结构,但也要注意节省内存和性能之间的平衡)
反例:
set user:1:name tom
set user:1:age 19
set user:1:favor football </pre>
正例:
hmset user:1 name tom age 19 favor football
2.4.3 控制key的生命周期,redis不是垃圾桶
建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期)。
2.5 监控并预警
Bigkey首先需要重源头治理,防止Bigkey的产生;其次是需要能够及时的发现,发现后及时处理。分析Bigkey的方法不少,这里介绍两种比较常用的方法,也是Daas平台分析Bigkey使用的两种方式,分别是Bigkeys命令分析法、RDB文件分析法。
2.5.1 scan命令分析
Redis4.0及以上版本提供了--Bigkeys命令,可以分析出实例中每种数据结构的top 1的Bigkey,同时给出了每种数据类型的键值个数以及平均大小。执行--Bigkeys命令时候需要注意以下几点:
bigkeys 是以 scan 延迟计算的方式扫描所有 key,因此执行过程中不会阻塞 redis,但实例存在大量的 keys 时,命令执行的时间会很长,这种情况建议在 slave 上扫描。
建议在节点本机执行,这样可以减少网络开销。
如果没有从节点,可以使用--i参数,例如(--i 0.1 代表100毫秒执行一次)。
--Bigkeys只能计算每种数据结构的top1,如果有些数据结构有比较多的Bigkey,是查找不出来的。
string 类型统计的是 value 的字节数;另外 4 种复杂结构的类型统计的是元素个数,不能直观的看出 value 占用字节数(元素个数少,不一定 value 不大;元素个数多,也不一定 value 就大),所以 --bigkeys 对分析 string 类型的大 key 是有用的,而复杂结构的类型还需要一些第三方工具。
redis-cli -h 127.0.0.1 -p 6379 -a "password" --bigkeys
2.5.2 RDB文件分析
借助开源的工具,比如rdb-tools,分析Redis实例的RDB文件,找出其中的Bigkey,这种方式需要生成RDB文件,需要注意以下几点:
建议在slave节点执行,因为生成RDB文件会影响节点性能。
需要生成RDB文件,会影响节点性能,虽然在slave节点执行,但是也是有可能造成主从中断,进而影响到master节点。
Rdbtools 是 python写的 一个第三方开源工具,用来解析 Redis 快照文件。除了解析 rdb 文件,还提供了统计单个 key 大小的工具。
1、安装
git clone https://github.com/sripathikrishnan/redis-rdb-tools
cd redis-rdb-tools sudo && python setup.py install
2、使用
从 dump.rdb 快照文件统计, 将所有 > 10kb 的 key 输出到一个 csv 文件
rdb dump.rdb -c memory --bytes 10240 -f live_redis.csv
2.5.3 使用 memory 命令查看 key 的大小(仅支持 Redis 4.0 以后的版本)
redis-cli -h 127.0.0.1 -p 6379 -a password
MEMORY USAGE keyname1
(integer) 157481
MEMORY USAGE keyname2
(integer) 312583
3 HotKey
3.1 定义
通常以其接收到的Key被请求频率来判定,例如:
QPS集中在特定的Key:Redis实例的总QPS(每秒查询率)为10,000,而其中一个Key的每秒访问量达到了7,000。
带宽使用率集中在特定的Key:对一个拥有上千个成员且总大小为1 MB的HASH Key每秒发送大量的HGETALL操作请求。
CPU使用时间占比集中在特定的Key:对一个拥有数万个成员的Key(ZSET类型)每秒发送大量的ZRANGE操作请求。
也就是说虽然每个集群实例上的数据量相差不大,但是某个实例上的数据是热点数据,被访问得非常频繁
3.2 产生原因
3.3 危害
占用大量的CPU资源,影响其他请求并导致整体性能降低。
集群架构下,产生访问倾斜,即某个数据分片被大量访问,而其他数据分片处于空闲状态,可能引起该数据分片的连接数被耗尽,新的连接建立请求被拒绝等问题。
在抢购或秒杀场景下,可能因商品对应库存Key的请求量过大,超出Redis处理能力造成超卖。
热Key的请求压力数量超出Redis的承受能力易造成缓存击穿,即大量请求将被直接指向后端的存储层,导致存储访问量激增甚至宕机,从而影响其他业务。
3.4 优化方案
3.4.1 备份热key
可以把热点数据复制多份,在每一个数据副本的 key 中增加一个随机后缀,让它和其它副本数据不会被映射到同一个 Slot 中。
这里相当于把一份数据复制到其他实例上,这样在访问的时候也增加随机前缀,将对一个实例的访问压力,均摊到其他实例上
3.4.2 本地缓存
如果我们可以预测到hotkey发生,比如秒杀这类,那我们就可以在活动上线前提前把缓存数据 写入到本地内存。
3.4.3 本地缓存+动态计算自动发现热点缓存
处理突发的hotkey就只能通过 动态计算然后缓存进行热点缓存
3.5 监控并预警
3.5.1 基于流式计算技术的缓存热点?动发现
框架如下图,具体介绍见 Redis(五):常见面试题目详解
3.5.2 京东开源框架JD-hotkey
JD-hotkey框架如下图,git地址 https://gitee.com/jd-platform-opensource/hotkey
流程介绍:
客户端通过引用hotkey的client包,在启动的时候上报自己的信息给worker,同时和worker之间建立长连接。定时拉取配置中心上面的规则信息和worker集群信息。
客户端调用hotkey的ishot()的方法来首先匹配规则,然后统计是不是热key。
通过定时任务把热key数据上传到worker节点。
worker集群在收取到所有关于这个key的数据以后(因为通过hash来决定key 上传到哪个worker的,所以同一个key只会在同一个worker节点上),在和定义的规则进行匹配后判断是不是热key,如果是则推送给客户端,完成本地缓存。
1)etcd集群 etcd作为一个高性能的配置中心,可以以极小的资源占用,提供高效的监听订阅服务。主要用于存放规则配置,各worker的ip地址,以及探测出的热key、手工添加的热key等。
2)client端jar包 就是在服务中添加的引用jar,引入后,就可以以便捷的方式去判断某key是否热key。同时,该jar完成了key上报、监听etcd里的rule变化、worker信息变化、热key变化,对热key进行本地caffeine缓存等。
3) worker端集群 worker端是一个独立部署的Java程序,启动后会连接etcd,并定期上报自己的ip信息,供client端获取地址并进行长连接。之后,主要就是对各个client发来的待测key进行累加计算,当达到etcd里设定的rule阈值后,将热key推送到各个client。
4) dashboard控制台 控制台是一个带可视化界面的Java程序,也是连接到etcd,之后在控制台设置各个APP的key规则,譬如2秒20次算热。然后当worker探测出来热key后,会将key发往etcd,dashboard也会监听热key信息,进行入库保存记录。同时,dashboard也可以手工添加、删除热key,供各个client端监听。
下面几篇文章是作者写的,对于框架理解很有意义。
京东开源热key探测(JD-hotkey)中间件单机qps 2万提升至35万实录
京东毫秒级热key探测框架设计与实践,已实战于618大促
从性能上来说,明显的可以看出开源框架JD-hotkey很优秀,只是第一个方案可以自定义开发,开源框架JD-hotkey就只能使用了。
补充
etcd简介
etcd 这个名字由两部分组成: etc 和 d ,即 UNIX/Linux操作系统的“/etc” 目录和分布式( distributed )首字母的“d ” 。我们都知道,/etc目录一般用于存 储 UNIX/Linux 操作系统的配置信息 因此etc和d合起来就是一个分布式的/etc 目录。etcd 的寓意是为大规模分布式系统存储配置信息。
etcd 的官方定义是一个 Go 语言编写的分布式、高可用 的用于分布式系统重要数据存储的 一致性键值存储 系统,常用于提供可靠的分布式键值( key-value )存储、配置共享和服务发现等 功能。 etcd 具有容错能力,对于n个节点的集群,可以在n-1/2 个节点宕机(其他节点正常) 的情况下仍继续工作。
etcd应用场景
etcd 的定位是通用的一致性 key/value 存储,但也有 服务发现和共享配置 的功能 。 因此,典型的 etcd 应用场景包括但不限于分布式 数据库、服务注册与发现 、 分布式锁 、 分布式消息、队列 、 分布式系统选主等 。 etcd 的定位是通用的一致性 key/value 存储,同时也面向服务注册与发现的应用 场景 。
etcd vs Zookeeper
Zoo Keeper 的主要优势是其具有成熟、健壮以及丰 富 的特性,然而,它也 有自己的缺点,具体如下:
复杂 。 ZooKeeper 的部署维护比较复杂;Paxos 强一致性算法也复杂难懂;使用也比较复杂,需要安装客户端,官方 只提供了 Java 和 C 两种语言的接口
Java 编写,Java 本身就偏向重型应用依赖较多,对资源的占用也比较高
发展缓慢 ,Apache 基金会庞大的结构和松散的管理导致项目发 展缓慢
与 ZooKeeper 相比,ETCD更简单,安 装、部署和使用更加容易,并且 etcd 的某些功能是 ZooKeeper 所没有的,比如:
etcd 更加稳定可靠,它的唯一目标就是把分布式一致性 KV 存储做到极 致,所以它更注重稳定性和扩展性
服务发现,etcd 使用的是节点租约( Lease ),并且支持Group (多 key );而 ZooKeeper 使用的是临时节点,临时节点存在很多问题
etcd 支持稳定的 watch ,而不是 ZooKeeper 一样简单的单次触发式watch,很多调度系统需要得到完整节点历史记录,etcd 可以存储数十万个历史变更
etcd 支持 MVCC (多版本并发控制),因为有协同系统需要无锁操作
etcd 支持更大的数据规模 , 支持存储百万到千万级别的 key
etcd 的性能更好 。 在 一 个由 3 台 8 核节点组成的云 服务器上, etcd d 版本可以做到每秒数万次的写操作和数十万次的读操作
数据持久化。etcd默认数据一更新就进行持久化。
最后,etcd作为一个年轻的项目,正在高速迭代和开发中,这既是一个优点,也是一个缺点。优点在于它的未来具有无限的可能性,缺点是版本的迭代导致其使用的可靠性无法保证,无法得到大项目长时间使用的检验。然而,目前CoreOS、Kubernetes和Cloudfoundry等知名项目均在生产环境中使用了etcd,所以总的来说,etcd值得你去尝试。
JD-hotkey 为什么用etcd,而不是zookeeper
etcd里面具备一个过期删除的功能,你可以设置一个key几秒过期,etcd会自动删除它,删除时还会给所有监听的client回调,这个功能在框架里是在用的,别的配置中心没有这个功能。
etcd的性能和稳定性、低负载等各项指标非常优异,完全满足我们的需求。而zk在很多暴涨流量前和高负载下,并不是那么稳定,性能也差的远。