共计 4764 个字符,预计需要花费 12 分钟才能阅读完成。
本篇文章将以 redis 的 bigkey 为主题进行技术开展,通过从意识 redis 的高性能,bigkey 的危害、存在起因、4 种解决方案,到模仿实战演练的介绍形式,来跟大家一起意识、探讨和学习 redis。
先认识一下 redis 和 bigkey 吧
redis——互联网的宠儿
redis 作为一款优良的工业级内存型数据库,自诞生后便逐步成为互联网的宠儿,撑持起了互联网丰富多彩的性能和微小的 QPS(每秒查问率),并和 nginx 一样成为高性能的代名词,比方微博上的每一条热搜背地都有着 redis 默默守候。从某种意义上来说,redis 的使用量也代表着一家互联网公司的流量。
在冯诺伊曼计算体系中,内存是一种重要的存在。计算和存储之间存在着肯定的辩证关系,通过计算能够缩小存储,通过存储能够缩小一定量的耗费。因而,缓存的思维也成为零碎性能缩小计算量的一种重要优化伎俩。
咱们看两张比照图。
这张是 cpu、内存、磁盘的性能比照。内存的读写性能是磁盘的近一千倍,redis 作为内存型存储介质,对系统性能晋升具备革命性的变动,经济基础决定上层建筑,所以经济效益永远是第一位的。
再看下各种存储的价格。
咱们简略评估剖析下:内存折算大概 30 元 1G,磁盘大概 0.5 元 1G,相比性能的差异,经济效益的投入产出比还是很高的。
redis 的存储数据结构:redis 是一种 key/value 构造的存储型数据库,外部组织模式应用了 hashMap 的这种数据存取索引构造,数据的读取工夫复杂度为 O(1)。hashMap 的 key 为 string 类型,valuek 能够为 string、list、hashMap、set 和 zset 五种根本数据结构。这些数据都存储在内存中,具备高效的读写性能,常被用来做缓存解决,进步零碎的性能。
底层存储构造如下:
撑持起 redis 高性能的另一个设计实现就是单线程的工作解决设计。
在面对一个微小工作量时,为了尽快实现工作工作,通常都会抉择加人,把工作拆解成多份并行处理。这就是一个简略的多线程并发解决的思维,多线程能够进步工作解决的吞吐量。
那么为什么 redis 却反其道而行之,应用单线程的形式?这里的单线程是指 Redis 的网络 IO 和键值对读写是由一个线程来实现的,这也是 Redis 对外提供键值存储服务的次要流程。
先来看下,如果应用多线程,在解决网络 IO 时会存在哪些问题?线程是 cpu 调度的根本单位,cpu 通过时钟中断,进行工夫片的切换,对外体现出并行处理的成果,工作切换必然耗费资源。当开启很多的线程,到一定量后,这些切换就会达到性能的瓶颈。
还有一个就是多线程下的资源共享问题,这也是并发编程的外围问题。并发环境下存在资源竞争,就须要对共享资源的临界区进行加锁解决,把并发解决转化为串行化解决,redis 的数据结构中,底层应用的 hashMap 索引数据结构,在多线程解决状况下,必然呈现资源抢夺的问题,这即又变成一个串行同步化的处理过程。因而,咱们在此否定了多线程。
那么来看看单线程在这种场景下有什么益处
申请端和服务端通过 tcp 三次握手后会建设网络连接,申请数据从网卡被写入到操作系统的内核缓冲区中,用户程序执行 read 操作,会将数据从内核空间写入到用户程序执行的变量中,如果内核空间还未收到数据,这里就会产生阻塞期待。
这种解决形式咱们显然是不可能承受的,为了解决这个技术问题,一种被叫做 IO 多路复用的技术被发明进去,在 linux 下一共有三种实现,select、poll、epoll,,简略来说,该机制容许内核中同时存在多个监听套接字和已连贯套接字,再实现读写就绪,告诉用户态来执行。具体这里不做开展,有趣味的敌人能够网上搜下。
nodejs、nginx 这些网关层的技术,都是单线程的设计,在解决网络 IO 上,单线程要比多线程优异。然而单线程也有其致命的弱点,一旦解决中的某个申请工作解决过长,就会阻塞后边的申请。这也是后文要开展的 bigkey 危害的次要起因。就像单行道上,某辆车呈现了故障,就会呈现堵车景象一个情理。
一、什么是 bigkey?
从上图的 redis 底层数据存储构造中,能够看到 value 具备多种数据结构的实现,因而,value 大小在字符串类型,体现为字符串的长度;value 为复合类型,则体现为元素的个数。
bigkey 就是 redis key/value 体系中的大 value 问题。依据数据类型的划分,bigkey 体现在两点:
- 存储数据为 string 类型, value 值长度过大;
- value 为复合类型,蕴含元素个数过多。
在 redis 中,一个字符串最大 512MB,一个二级数据结构(例如 hash、list、set、zset)能够存储大概 40 亿个 (2^32-1) 个元素,这是一个理论值,理论应用时,咱们能够通过运维给到的数据来综合掂量限度数,个别 string 类型管制在 10KB 以内,复合类型 hash、list,、set, 和 zset 元素个数不超过 5000 个。
二、bigkey 有什么危害?产生起因?
看到这里,咱们对 bigkey 曾经有一个初步的理解。接下来,咱们针对 bigkey 的危害和产生起因一一进行介绍。
1、bigkey 的四大危害
俗话说“一颗老鼠屎坏掉一锅汤”,对于 redis 而言,bigkey 就像是老鼠屎的存在。其危险性次要体现为以下四个方面:
1. 内存空间不平均
在集群模式中,因为 bigkey 的存在,会造成主机节点的内存不平均,这样会不利于集群对内存的对立治理,存在失落数据的隐患。
2. 超时阻塞
因为 redis 单线程的个性,操作 bigkey 通常比拟耗时,也就意味着阻塞 redis 可能性越大,这样会造成客户端阻塞或者引起故障切换。慢查问通常就会有它们的身影。
3. 网络拥塞
bigkey 也就意味着每次获取要产生的网络流量较大。假如一个 bigkey 为 1MB,客户端每秒访问量为 1000,那么每秒产生 1000MB 的流量,对于一般的千兆网卡(依照字节算是 128MB/s) 的服务器来说几乎是灭顶之灾。
4. 阻塞删除
有个 bigkey,对它设置了过期工夫,当它过期后会被删除,如果应用 Redis 4.0 之前的版本,过期 key 是异步删除,就会存在阻塞 redis 的可能性,而且这个过期删除不会从慢查问发现(因为这个删除不是客户端产生的,是外部循环事件)。
2、bigkey 怎么产生的?
bigkey 的产生次要是因为程序的设计不当所造成的,如以下几种常见的业务场景
- 社交类:粉丝列表,如果某些明星或者大 v 不精心设计下,必是 bigkey。
- 统计类:例如按天存储某项性能或者网站的用户汇合,除非没几个人用,否则必是 bigkey。
- 缓存类:将数据从数据库 load 进去序列化放到 redis 里,这个形式常常罕用,但有两个中央须要留神:第一,是不是有必要把所有字段都缓存;第二,有没有相干关联的数据。
由此可见,在程序设计中,咱们要对数据量的增长和边界有一个根本性的评估,做好技术选型和技术架构。
三、4 种排查发现 bigkey 的解决方案
先看一个思考题:
明天年初,石家庄陆续暴发新冠疫情,对于一个有着 1000 万多人口的中大型城市,疫情防控面临着微小的压力,怎么高效的发现病毒感染人群和接触人群,成为疫情防控取胜的要害。政府做了以下几方面工作,简略概括为 4 点:
- 禁止人员流通,居家隔离;
- 制订危险等级;
- 网格化治理;
- 核算检测。
依据流行病医学特点,呈现症状必须被动上报。这种是被动上报,因为新冠疫情具备肯定的潜伏期,很多无症状患者,因而就须要通过核算检测的机制,被动地发现,这其实就体现了一种计算机解决扫描的思维。
发现、解决 bigkey 的思维和疫情防控的做法有些类似,惯例做法也有四种。
1、redis 客户端工具
redis-cli 提供了 –bigkeys 来查找 bigkey,例如上面就是一次执行后果。
从上图能够看出这种形式给出了每种数据结构的 top 1 bigkey,同时给出了每种数据类型的键值个数以及均匀大小。但如果咱们须要更多的 bigkey,这种形式就无奈做到。它的外部是通过 scan 的形式进行的,有肯定的性能开销,为了不影响业务,可把该工作放到从节点上进行执行。
2、debug object
redis 提供了一个 debug object key 的命令。假如有一个“找出 redis 中大于 10KB 以上的 key” 需要,为了失去该后果数据,就须要先扫描出所有的 key,而后通过循环调用 debug object key 失去所有 key 的字节大小。
因为 debug object key 的执行效率很慢,会存在阻塞 redis 线程的可能。因而该种计划,对业务也会存在肯定的伤害,在应用时,可将该执行程序运行到从节点之上。
3、RDB 文件扫描
咱们晓得,redis 有一种长久化的计划叫做 RDB 长久化,它是 redis 内存存储数据的一个磁盘化快照,通过 RDB 工具对 RDB 文件进行扫描,能够查找出存在的 bigkey。
在抉择这种计划时,首先须要做 RDB 文件长久化。RDB 长久化是一种内存快照的模式,依照肯定的频次进行快照落盘,这种计划是一种理想化的抉择,不会影响 redis 主机的运行,但在对数据可靠性要求很高的场景,不会抉择 RDB 长久化计划,也因而它不具备广泛适用性。
4、DataFlux bigkey 的扫描设计思维
后面几种计划,要么由客户端发现,要么须要进行全量数据扫描,扫描是很耗费计算资源的一种行为。类比疫情,就好比是某千万人口的城市呈现病例后,不分等级地对全员进行核酸检测,这岂但耗费微小的物力、财力、人力,效率还非常低下,跟须要和工夫赛跑的疫情防控南辕北辙。
上文咱们也剖析了 redis bigkey 产生的起因,很多都是业务的设计不合理和评估有余所导致的。因而 DataFlux 产品在设计中,就让 datakit 的 redis 采集器应用了一种自主配置潜在 bigkey 进行扫描发现的计划,反对固定 key 值和 key pattern。在 key pattern 中,通过 scan pattern 失去肯定范畴的 key,再通过 length 函数对每种类型的 key(”HLEN””LLEN””SCARD””ZCARD””PFCOUNT””STRLEN”)取值,失去对应的 key 的 length,上报 DataFlux 平台进行监控存储。
这种做法的益处就两点:
一、因为是针对指标 key 失去 length,而 redis 中各种数据类型的 length 值获取都是 O(1)工夫复杂度。因而执行效率很高;
二、采集到的后果数据上报到 DataFlux 存储平台,在该平台下,能够对指标数据做各种图表展现,监控告警。
接下来,咱们通过对该计划进行简略的业务场景模仿来开展阐明。
四、实战演练,等的就是这刻!
在一个业务零碎中,应用 redis 的 string 类型存储用户的认证 token, 应用 list 数据类型做异步音讯队列。
业务剖析:存储 token 的 key 数据是一个定长数据,不会有数据量的变动,不会造成 bigkey。再来看音讯队列,如果生产端呈现故障,音讯生产端在这一时刻涌入了大量数据,这时应用音讯队列的 redis key 就会成为一个潜在的 bigkey,因而,咱们须要对该 key 进行监控。
咱们假如音讯队列的键名为 queue。
咱们按官网教程,装置 datakit 工具。
官网教程:《如何装置 DataKit》
https://help.dataflux.cn/doc/…
装置好后,进入 DataKit 装置目录下的 conf.d/db 目录,复制 redis.conf.sample 并命名为 redis.conf。做下图配置:
模仿初始队列 push 10 个 value
数据上报到 DataFlux 平台
再 push 一定量的数据
最终可在 DataFlux 后台上看到如下采集后果。
在 DataFlux 平台上,通过指标可对监控的 key 进行图表展现、监控报警以及可视化展现等等,最大幅度出现数据价值。