本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。
本文章的讨论集中在数据库领域。
热点直到现在依旧是一个“热点”问题,在不同的基础系统中对其的要求有不同的约束,问题的解决范式基本上分为三个步骤:
其实不同系统发展的不同阶段对于热点的需求也不尽相同。以目前经历过的系统来看,发展初期诉求其实非常简单,无论解决与否,我首先需要知道现在出现了热点,线上经常会出现一个秒级别的热点,导致访问超过了系统预设的SLO,进而导致SLA保证受损,发现问题甚至是靠后来的流水日志。
对于一个分布式系统来说其实全局热点的汇聚有几种方法,以目前存算分离的系统来说,基本上计算节点的部分日志都会放入ELK体系管理,依托于Elasticsearch强大的查询能力,我们其实很好获取目前的热点信息。但是有一个问题,以实践经验来说ELK的收费也是真心不便宜,对系统的总体成本提出了不小的挑战,以流水日志的量级基本上不会选择全量的放入Elasticsearch,有一个简单的方案,上报Elasticsearch的时间粒度增加,对这一段时间筛选以后再执行上报,成本是下来了,但是不能做到10秒级别的热点识别了,这需要系统有优秀的过载处理能力。
另外一个路数就是存储节点执行热点识别,以一段时间为基本单位汇聚热点,上报prometheus,这是一个切实可行的方法,尤其是我们给prometheus remote wirte一个低成本的存储系统时成本优势更为明显,prometheus本身的查询能力也非常出众,基本上可以解决这个问题,(突然想到一个经典问题,公有云厂商自己的云原生数据库和三方厂商的云原生数据库有什么区别),甚至于这一部分的能力可以依托于自己的产品去完成,成本基本上就只有裸机器了。
目前各大公有云厂商/三方厂商对于热点有这么几个能力:
| 公有云厂商产品 | 热点方案 |
|---|---|
| 阿里云-Tair | 1. 实时热key统计 2. 离线全量key分析 3. 通过Proxy Query Cache优化热点Key问题 |
| 腾讯云-Redis | 1. 5/10/15秒热点分析 2. 历史热key分析 |
| 华为云-DCS | 每日热key分析 |
| AWS ElastiCache | 全量指标检测 |
| Azure Cache for Redis | 全量指标检测 |
| google memorydb | 全量指标检测 |
| Redis Lab | 全量指标检测 |
| TiDB | 1. Hot Write 面板,可以追踪表/索引级别的热点 2. 基于热点识别(Load Base Split)的分裂 |
| Lindorm | 1. 分片热点 2. 基于热点识别(CpuUsageBasedBalaner)的分裂 |
可以看到内存数据库(Pmem,DRAM)和基于HDD,SSD的数据库对于热点的处理是完全不同的,除了阿里实现了Proxy Query Cache优化热点问题以外其他内存数据库厂商无一例外选择点到为止,只提供监控,而不提供解决。而TiDB,Lindorm等其他大容量数据库均选择热点发现以及分裂来处理热点。
当然分裂策略展开来讲也是一个点,主要还是两个流程:
这里以Lindorm的公开资料来看,开始时选择StochasticLoadBalancer,这个Balancer和它的名字一样,异常复杂。它会根据读请求数,写请求数,文件数,本地化率等一系列条件做为输入,通过用户配置的权重因子,计算出一个集群均衡指数,然后再根据这个指数来进行Partition的负载均衡,但是因为运维的复杂性后来没有采用。
后来选择以每台服务器的CPU消耗均衡状态,即CpuUsageBasedBalaner,这从某种角度来讲是一种非常粗糙的方式,因为CPU只是众多维度的一个,切我认为不是最重要的一个,partition/db级别的容量,负载,分片的集中程度,请求数,范围操作的时延等等都非常重要。
至于切点,玩来玩去大家都一样,无外乎是判断请求是单点还是范围,然后基于容量,负载或者自定义执行切分。
站在大容量数据库的角度来讲,其实阿里Proxy Query Cache的方案是可以参考的,无论是计算节点缓存还是DAX都是可行的路(这里我所指的是切实可行的用缓存解决问题,而不是使用奇巧的数据结构在单机解决问题),但是细节实现并不简单,且需要存储节点本身的热点识别能力。
前面提到了监控,日志,离线处理来识别热点,实际上外挂流量分析工具也是一个出路,比如流量透明代理[15][14],eBPF[16]都是很好的方法,且不失为一个很好的方法。
从门槛来说,我认为前者更适合普罗大众,因为难的东西已经有人封装好,要做的就是写个go/python程序罢了,值得一提的事目前混沌工程工具中任意的http请求故障注入就是用tproxy透明代理做的。至于后者不是调下bpftrace那么简单的事情,整体流程大概是这样:把bpf程序挂在TC或者XDP,然后根据目标端口筛选网络数据包,用bpf解析redis请求,然后分析热key,完全独立于引擎本身,我以前写过一个用ebpf加速内存数据库的项目,其中在TC中解析了简单的redis请求[17],有兴趣的同学可以参考下。
单机热点识别其实是一个偏学术的问题,即Elephant Flows的识别,相关的研究已经很多了。阿里云缓存作为国内面临热点问题最严峻的团队,必定是有两把刷子的,我们看看国内标杆是如何解决问题的:


热点识别一个最简单的方案当然就是min-heap,但是其插入时间复杂度是极高的,我们显然希望做到热点识别本身需要O(1),不对实际操作造成任何影响,内存占用也小那就更棒了!这里也有几个问题需要注意:
这都是实现细节的问题,但是这其实凸显出另外一个问题,我们到底是想要怎样的热点机制?是仅仅单点识别?还是要做处理?如何处理,只是分裂吗?
仅仅是做热点识别上引擎级别的热点识别机制我个人认为是很得不偿失的,本来监控和日志能解决的事情,何必呢?如果只是为了分裂,那也没有太大的必要做引擎级别的热点识别,我们基于全局状态发现此parition需要分裂时再去决定哪里分也不迟(当然常态化的分裂合并也可,因为实时根据负载和容量决定可能会影响线上请求)。
所以答案已经呼之欲出了,我们只有在需要实时“处理”热点时一定需要引擎级别的热点识别,阿里的hotring[12]就是个很好的例子(虽然不太实用),包括目前阿里云上的热点识别,显然是给Proxy Query Cache铺路的,再看国外一众厂商和华为云,只提供监控,不做处理,当然也没必要去做引擎热点识别了,从这个角度看,腾讯云显得有点突兀。
这些思考是宝贵的,于我而言这指出了DrangonflyDB/X-stor未来热点处理的发展方向。
这篇文章本来想连带着阐述一些过载相关的思考,显然过载和热点是两码事,过载本身的处理也是系统的兜底方案之一,但是今天给自己只留下了三个半小时写文章时间,就到这里吧,以后有机会再谈,抛下三个本来想写的问题:
参考: