随着企业上云趋势的日益热化, 作为产业核心组件的数据库, 已成为各大云计算公司增长最快的在线服务业务. 作为中国第一大云数据库厂商, 我们 RDS 团队致力于为用户提供稳定的云数据库服务. 从本质上看, RDS 是一个多租户 DBaaS 平台, 利用轻量级 KVM,Docker 镜像等资源隔离技术将用户所购买的数据库实例部署在物理机上, 按需分配资源并进行自动升降级, 实现一套完全自动化的智能运维管理.
云数据库对客户业务的稳定性至关重要, 因此快速发现云数据库性能出现异常, 及时定位异常原因是云数据库厂商的一个挑战. TcpRT 是阿里云数据库用来监控和诊断数据库服务质量的一个基础设施. TcpRT 从主机 TCP/IP 协议栈的壅塞控制采集 trace 数据, 计算数据库延迟和网络异常, 在后台流式计算平台进行大规模实时数据分析和聚合, 通过统计指标历史数据的柯西分布发现异常点, 并通过同一台主机, 交换机, proxy 下所有实例一致性趋势的比例来计算不同组件发生异常的概率.
到目前为止, TcpRT 以每秒采集 2 千万条原始 trace 数据, 每天后台处理百亿吞吐数据, 秒级检测异常的卓越性能在阿里云持续稳定运行三年.
本文贡献:
提出了一种新的对数据库服务质量进行采集的方法, 基于内核壅塞模块实现, 可以非侵入性, 低代价的采集基于停等协议的关系数据库的 per connection 的延迟, 带宽, 分析用户使用数据库的模型(短连接和长连接), 并且可以端到端的记录和量化基础网络服务质量对数据库服务质量的影响, 包括丢包率, 重传率.
我们开发了一套对采集的原始数据进行数据清洗, 过滤, 聚合, 分析的流式计算系统, 系统可以做到水平扩展, 容错性, 实时性, Exactly Once, 具有和其他大数据平台例如 EMR,MaxCompute 进行数据交换的能力.
我们提出了一个新的算法对 TcpRT 数据进行分析, 来发现数据库的服务质量有无异常, 并且对异常事件的根因进行定位.
问题
从网络架构上看, RDS 由控制层和数据链路层两个部分组成, 如上图所示. 其中, 控制层包括一系列管理模块, 比如资源管理器, HA 管理器, 迁移管理器等. 资源管理器负责将实例的不同副本分配在独立的物理主机上. 当实例发生故障时, HA 管理器会探测到主实例的故障, 并将服务连接切换到 Standby 节点上. 而当主机负载失衡时, 迁移管理器负责将数据库实例进行迁移, 保障用户服务质量.
数据链路层主要负责数据的分发和路由. 通常, 云用户通过 ECS 和 RDS 实现业务上云. 他们将业务部署在 ECS 上, 并通过 VPC 和 RDS 数据库实例进行交互. 数据包通过驻留在用户 VPC 网络中的 vSwitch 进行打包 / 解包并路由. 而 SLB 负责将这些数据包进行解析, 并将 DBaaS IP 映射到真实服务器上. 目前, SLB 同时支持 FNAT 和 DNAT 两种模式. 由于出流量数据不经过 SLB, DNAT 比 FNAT 呈现出更好的性能. 除此之外, 我们引入 NGLB 进行优化. 在 Proxy 中, 我们解析客户端 / 服务端协议, 并提取查询, 将读写进行分离, 同时支持横向分区和连接池的功能.
作为客户业务的核心组件, 保障云数据库 7/24 的稳定至关重要. 我们通过对用户承诺数据库 SLA 服务等级协议, 来保障用户的权益. 然而, 云数据库的服务质量受各种因素影响, 例如突发性连接断开, 数据库延迟发生抖动, 吞吐骤然下降等, 都有可能带来用户业务指标的下降. 因此, 快速发现云数据库性能异常, 及时定位根因是云数据库厂商的一个挑战.
可能造成云数据库的服务质量下降的原因很多, 例如网络上的异常, 比如 DB 主机的上联交换机 tor 发生丢包, 或者 load balaner 出现 TCP incast 问题; 或者用户侧的问题, 用户端网络异常或者丢包; 或者多租户机制, DB 主机内核缺陷, 硬件上 SSD 的硬件异常等等都会引起. 在出现问题时, 快速诊断定位解决问题是关键的问题.
传统数据库性能采集只需要采集 DBMS 内部处理 SQL 请求的延迟就够了, 但云数据库需要 end-end 数据. 因为对云上用户而言, 他看到的云数据库的延迟是终端的延迟. 真实的场景对 trace 工具提出这些需求, 因此很关键的是要采集 end-to-end 数据, 以及链路每段的延迟, 终端用户感受到的延迟是所有路径上每个节点处理延迟以及网络上所有延迟的总和, 不仅仅要采集 DB 上的延迟, 还要采集 proxy 上看到的延迟. 这需要在链路上引入 trace. 还要采集网络上的数据, 主机上其实可以看到的上行乱序和下行的重传, 这个信息对推测网络有无异常非常重要. 发送和接收会走不同的网络路径.
传统的 trace 手段需要入侵式地在业务代码中添加埋点. 第一, 我们不能在客户端埋点, 因为客户基本上都使用标准数据库客户端来访问, 没有嵌入打点代码, 我们也不能期望客户会修改自己的业务代码, 加入打点逻辑, 并将打点数据采集交给我们. 因此我们需要一种非侵入式的方法来获取 end-to-end 性能数据. 第二在服务器端埋点也有问题, 应用层无法感知真正的数据接收和发送时间. 应用层记录的时间是内核把数据交付给应用层和应用层把数据交付给内核的时间. 在系统负载很高, 应用层进程调度需要花费大量时间的场景下, 会导致请求的实际处理时间计算不准确; 应用层无法感知到网络链路质量. 在网络出现拥塞, 大量的重传报文, 导致数据发送和接收过程大大延长, 无法区分出是对端响应缓慢还是网络链路质量不佳.
在这种挑战下, TcpRT-- 阿里云数据库监控和诊断服务质量系统, 孕育而生.
TcpRT 从主机 TCP/IP 协议栈的拥塞控制采集 trace 数据, 用于监测数据库延迟和网络异常, 并利用先进的流技术, 在后台实时计算平台上进行大规模在线数据分析, 结合离线模型, 通过实时异常事件判定, 以及 RDS 网络关系图谱中的趋势一致性概率探测, 快速诊断出性能异常并定位原因.
架构
上图是 TcpRT 的概要架构图, 其中包含以下个组件:
内核模块 -- 用于采集指标 trace 数据, 包括查询延迟, Proxy 节点和 DB 节点的连接指标等
本地聚合器 -- 负责将内核模块采集的 trace 数据进行本地聚合处理, 推送至 Kafka 消息队列
流式 ETL-- 利用流技术, 在后台流式计算平台上将 Kafka 中的时序指标数据进行清洗, 多粒度聚合及在线分析, 利用冷热技术将数据分离
在线异常监测 -- 根据时序指标数据, 拟合异常模型, 通过实时异常事件判定, 以及 RDS 网络关系图谱中的趋势一致性概率探测, 快速诊断出性能异常并定位原因
TcpRT 内核模块
内核模块主要负责对网络数据的传输进行整个生命周期的监控. 在遵守停等协议的 TCP 通信机制下, 服务端处理每个请求的过程分成 3 个阶段, 接收阶段 (Receive), 处理阶段(Handle) 和响应阶段(Response). 如下图所示:
鉴于此, 我们需要计算如下时间:
上行时间 = T1 - T0
处理时间 = T2 - T1
下行时间 = T3 - T2
查询时间 = T3 - T0
RTT 时间 = T2 - T2'
现有的监控方案, 通常在业务层面埋点进行服务耗时监控, 但此方法既不能获得真正的数据接收和发送时间, 也无法感知到网络链路的质量, 更需要在业务代码中入侵式地添加埋点. 因此, 我们实现了一种通用, 低开销的内核模块进行 trace 监控以得到上述时间.
首先, 我们选择修改 Linux 内核的拥塞控制算法. 此算法可以感知内核发送报文的上下文, 并且支持热更新(只需增加一个驱动模块而无需更新线上内核).
此外, 拥塞控制算法提供了以下机制:
1. 每个 TCP 通信是独立的拥塞控制算法, 不存在资源竞争情况
2. 可以感知到收到的每个 ACK 报文的上下文
3. 在已经发送的报文都已经被 ACK 的情况下, 可以感知到当前发送的报文上下文
根据拥塞控制算法提供的事件回调, 我们可以获取到每个 TCP 连接下述事件:
1. 客户端发送给服务端 ACK 报文
2. 在所有已经发出去的 sequence 都被确认的情况下, 服务端发送报文
3. TCP 连接建立
4. TCP 连接断开
内核任何线程都可能调用到拥塞控制算法, 因此为了性能, 拥塞控制算法必须保证是没有数据争抢的. TcpRT 内核模块保证了以下四点:
1. TcpRT 所有的数据保存在每个 TCP 连接对象上, 所有的数据读写都没有跨 TCP 连接的共享;
2. TcpRT 在每个 CPU core 都有独立的写缓冲区, 防止了多个内核线程争抢写缓冲区加锁;
3. 为了避免内存开销, 又保证实时性, TcpRT 的写缓冲区会在 buffer 满或者时间到的情况下, 刷新写缓冲区到 debugfs, 供应用层采集端采集. 由于写 debugfs 的频率很低, debugfs 的锁争抢几乎不存在, 最大限度保证了性能;
4. TcpRT 回写的数据, 是 binary 格式的, 对比需要 format 的字符格式, 在实测场景可以提高 20% 的性能.
此外, 通过给 linux 内核添加 setsockopt 选项, 通知内核一个不需要应答的请求交互过程已经结束, 从而支持非停等协议.
针对 TcpRT 内核模块对用户数据库实例的性能影响, 我们基于 sysbench 模拟了 MySQL 400 个客户端连接进行压测, 结果如下图所示, TcpRT 内核模块对系统的负载影响不到 1%.
TcpRT 聚合器
TcpRT 内核模块, 利用 debugfs 和用户态通信. 每秒以千万的 trace 数据高速产出并吐入至 debugfs 中. 为了减轻后台在线分析任务的压力, 我们构建本地 TcpRT 聚合器, 实现本地 trace 秒级聚合, 并将聚合结果输出到 / dev/shm 中. Logagent 从中读取聚合数据, 发送至 Kafka, 并由后台 ETL 接手, 进行实时数据分析, 流程如下图所示:
在本地聚合器中, 聚合操作需要保证可交换且可结合, 我们采用均值, 最大值, 请求个数的三元组聚合方法来保证延迟时间类指标满足这一要求. 此外, 我们采用每秒同客户端出现的不同端口数对活跃连接数进行指标特征提取. 同时, 抽取请求数作为特征, 建立用户长短连接的使用模型, 进而对用户使用数据库实例的负载模式进行分析. 根据历史数据, 当前仍有众多用户采用短连接模式, 这对于诸如 MySQL 线程网络模型的 DB 是非常不友好的, 从而激发我们提供 Proxy 中间件将短连接转换为长连接服务.
为了最大化聚合效果, 我们在内存中维护最近 5s 的聚合结果, 将 5s 前的数据输出到 / dev/shm 的文件中. 且为了提高查询性能以及长时间段的聚合操作, 我们将三种粒度 1s, 5s, 1m 的聚合结果存入到库中.
TcpRT ETL
下图描绘了 ETL 任务的拓扑结构:
在线 ETL 任务主要包括四个子任务:
数据转换
数据关联与聚合
数据存储
以及延迟和乱序处理
其中, 延迟和乱序到达的处理是一个难点. TcpRT 输出的时序数据是以 SQL 执行结束时间为时间戳, 如何做到实时准确地对窗口内的时序数据进行聚合并将结果刷出是一个两难的问题. 我们采用了 "尽力聚合" 的方法, 即对窗口设定等待时间, 待时间结束后将实时聚合结果刷出, 若此后, 还有该窗口的时序数据到来, 则直接落入到数据库中. 如 TcpRT 聚合器中所述, 所有聚合数据具有可再结合特性, 这样, 我们可以对同时刻的聚合数据进行二次聚合.
在线异常监测
组件的异常往往伴随着相关指标的抖动. 比如主机发生 IO Hang 后, load,dirty,writeback, 某些 core 的 iowait, 被阻塞的线程数等指标会明显升高, 而各实例的 write,CPU 使用率会明显变低, 主机维度的 PT 指标会明显升高. 在部分情况下, 连接数会上升, 长连接请求数会下降, 流量会下降等. 在 Proxy 发生异常的时候, PT 可能会升高, CPU, 流量, 连接数均可能会下降.
传统方法下, 我们通过设定阈值进行指标抖动的检测. 然而阈值的设定强依赖于专家经验, 维护成本高, 且一刀切的设定常常会 "误伤" 健康指标. 比如, 有些数据库实例是专门用于 OLAP, 它们的 SQL 请求处理时间往往都是秒级的, 若采用常用的 SQL 请求处理时间作为异常判定阈值, 此类数据库实例就会触发报警.
鉴于此, 我们需要一种通用且自适应的智能模型来解决云数据库的异常监测.
为了避免人工设定阈值, 我们一开始尝试利用 control charts 来进行判断, 根据样本时间段的均值和标准差, 预测未来时间段的置信区间. 若实际值超出置信区间, 则判定为异常. 然而, 若样本本身为异常, 则此时间段的参数均不可信.
如上图, 左上图为 (ins1,*,db1) 的 upsize 指标时序图, 可以看到 (ins1,*,db1) 的 upsize 指标会有周期性的波动. 左下的两张图分别是历史时间窗口设定值为 30min 的 (ins1,*,db1) 的 upsize 指标 mean&median 时序图和 SD&MAD 时序图. 可以清楚看到, 当 upsize 指标发生波动后, mean 值和标准差 SD 值都会发生窗口时间长度的跳变, 但 median 和 MAD 却几乎不受指标波动的影响, 显得更平稳, 丝滑.
右上图为 (ins2,*,db2) 的 newConn 指标时序图, 可以看到在 03:40~04:00 期间, 指标发生异常, 出现大量极端值, 由于历史的时间窗口设定值为 30min, 所以可以从左下的图表中看到, 很长一段时间内样本的均值和标准差便会发生较大的波动, 而中位数和 MAD 指标却显得平滑, 对极端值并不敏感, 展出超强的鲁棒性. 由于依赖均值和标准差进行预测, control charts 具有不稳定性, 易造成误判. 这启发我们利用中位数和 MAD 替代均值和标准差作为预测模型参数.
如上图所示, 通过大量观察, 我们发现, 按(*,*,db) 和(*,*,proxy) 粒度聚合后的采样点集合近似正态分布. 但是, 正态分布依赖平均值和标准差作为参数, 如上文所述, 这两个参数波动大, 会造成严重误判. 鉴于柯西分布与正态分布相似, 且不依赖均值和标准差, 可以使用中位数和 MAD 这种稳定的统计指标来进行回归. 从上上图可以看到当历史时间窗口长度设定合适的时候, 中位数和 MAD 在面对指标波动和异常的情况下, 显得平滑且稳定, 这样回归出的模型具有更强的稳定性. 于是, 我们使用中位数和 MAD 作为参数的回归柯西分布来拟合异常诊断模型.
为了获取回归需要的样本集, 我们通过对过去一段时间 (比如: 最近一个小时) 的每一个时间点做采样, 得到一段历史窗口的数据点集合 S{x0,x1,x2,......}. 根据历史窗口数据集, 计算中位数 M 以及 MAD, 回归出这个数据集的柯西概率分布 D.
主机异常检测
RDS 主机上承载着众多实例, 各实例通常隶属于不同用户的不同业务. 在主机正常工作时, 由于实例相互独立, 并且对应的指标波动具有不确定性, 所有实例呈现出一致性升高或降低的概率非常小. 当主机发生异常时, 由于该主机上的所有实例共享同一资源, 某些关键指标会呈现出一致性趋势. 当一台主机发生了 IO Hang, 主机上大部分实例的 SQL 处理延迟都呈现出升高的趋势, 各实例的数据写入量呈现出下降的趋势. 这启发我们利用实例趋势一致性概率来判断主机的异常.
首先, 设定函数 H(curentVal, prevMideanVal,metricType)作为判断指标 metric1 是否异常的函数, 取值只能是 - 1,0,+1.currentVal 代表这一刻该指标的值, prevMideanVal 代表过去一段时间 (比如: 1 小时) 采样点集合的中位数, metricType 代表该指标的类型(比如: rt,rtt,qps, 流量等). 取值为 - 1 代表这个指标在跟过去一段时间指标做对比的时候呈现出明显下降的趋势, 0 代表这个指标没有明显波动,+1 代表这个指标与过去一段时间相比明显上升.
我们可以利用上文的算法来实现函数 H, 这样我们能算出 (ins,*,dstIp,metricType) 级别数据的函数 H 值, 然后在按 (ins,*,dstIp,metricType)->(*,*, dstIp,metricType) 做 map-reduce(agg 类型为 sum). 算出的值 s 反映了这个指标在这台主机上的突升, 突降的趋势, 用求和值 s 除以这个主机上的活跃实例数 t 得出突升 / 突降实例比例 r, 当 r 大于 0 是, 总体趋势上升, 当 r 小于 0 是, 总体趋势下降, 且绝对值越大概率越大.
那么我们如何利用 r 值来判断主机异常呢? 如果机器突然由正常变为异常时, 各实例由于都依赖这台机器的资源进行工作, 或多或少要受其影响, 因此相关指标发生突变的实例比例将会发生突变, 比如主机上 cpu 资源突然不足, 大部分实例都会受影响, 他们的处理时间会突升, 流量突降, 请求数突降, 因此可以通过判断 r 值是否突变来判断主机异常.
我们可以利用上文的算法来对 r 值的突变进行判断, 对过去一段时间的每一个时间点都做这样的计算, 我们可以得到一段历史窗口的突升 / 突降实例比例 r 行程的数据点集合 S{R0,R1,R2,...}. 根据历史窗口数据集, 我们算出数据集的中位数 M, 以及 MAD 值, 回归出这个数据集的柯西概率分布 D.
因为比例 r 的取值是 - 1~1, 而柯西概率分布 D 的自变量 x 范围是负无穷到正无穷. 我们需要对原来的比率 r 做转化让他的范围扩充到正负无穷. 通过定义的映射函数求出这一时刻下该指标的柯西概率分布的 CDF(), 如果 CDF()非常小, 比如小于 0.1%, 主机 hostA 的指标 metric1 呈现总体下降趋势, 或者非常大比如 99.8%, 主机 hostA 的指标 metric1 呈现总体上升趋势. 但是这样做判断, 只是判断出了 r 值的突升和突降, 为了减少误判, 我们还要判断出 r 值的突升和突降需要落到警戒范围. 因此需要一个必要条件: r 值的绝对值至少为 20%.
另外, 当 r 值足够高时, 无论 r 值是否突升还是突降, 都应该认为是异常, 因此还需要一个充分条件: r 值的绝对值大于 AlarmRatio(主机上的活跃实例 t), 那么认为是异常. 根据我们自己的实际情况, AlarmRatio(t)=0.8*Pow(0.987, t) + 0.2. 曲线如图, 当主机上的活跃实例比较少时, AlarmRatio 值比较高, 这样为了保证判断出异常的主机上异常的实例足够多, 这样才有较高的说服力, 而随着主机上活跃实例数变多, AlarmRatio 值会相应得变小最后收敛到 0.2, 这样为了不漏掉 r 比较少但是异常实例数足够多的情况.
当 r=-1 时, 这台机器 hostA 上所有的实例的指标 metric1 都出现了下降趋势, 当 r=1 时, hostA 上所有实例的指标 metric1 都出现了上升趋势. 除了独占实例, 一台主机上有数十甚至上百的实例, 他们分属不同的用户, 运行着不同的业务, 呈现出一致的趋势概率很小, 因此在正常的时候 r 值往往稳定在一个较低的范围内, 当 r 值很高的时候, 极有可能主机问题了.
但是这样的判断还不够, 因为还存在这两种情况: 1. 实例之间公用的某个组件出现了异常. 比如网络中间件引起的异常, 路由器的异常等等. 也会引起 r 值变高; 2. 有的主机上因为实例数比较少 (比如: 小于 3) 时, 单纯根据比例判断还分辨不出是否是主机的问题, 因为样本数太少, 不具有说服力. 针对这两种情况, 我们还用了多种方法来提高准确率, 比如:
1. 结合物理机的资源指标 (load,iowait,dirty,writeback 等) 的加权和来协助判断. 2. 如果存在上游节点(比如: proxy), 并且上游节点存在异常, 则优先报上游节点的异常. 因为上游节点往往还连接多个下游节点, 如果是某个单独的下游节点出现了异常, 一般上游节点是不会出现异常的, 因为上游节点上连接的下游节点很多, 这一个点并不会明显改变整体的指标趋势变化, 而某个节点的多个下游节点都出了问题, 他们的上游节点出问题的概率更大一些. 假设上游节点完全正常, 下游节点近似相互独立, 一个节点出问题的概率为 p,K 各节点同时出问题的概率就是 p^K, 所以问题的原因很可能是概率更大的上游节点.
Proxy 异常检测
我们的大部分实例在访问 db 前要经过 proxy 的转发, proxy 可以帮用户做短链接优化, 负载均衡, 连接审计, 注入检测等. proxy 是我们生产集群非常重要的组件, 一个 proxy 节点最多会有上千个实例的请求 (requests of thousands of instances) 经过. 也就是说, 如果一个 proxy 节点发生了故障, 将会影响到上千个实例, 这样的故障我们需要快速准确地发现并定位出来, 否则后果不敢想象.
我们一开始对 proxy 的 qps, 连接数等设定阈值的方式来进行报警, 但是由于不同分组的 proxy 业务量大小不一样, 因此我们需要对不同分组设定不同的阈值, 而且我们的业务增长迅速, proxy 会经常进行扩容, 这样阈值又要重新调整, 维护起来费时费力.
我们虽然可以利用上边所讲的判断 db 主机异常一样的算法来判断 proxy 异常, 但是由于 proxy 的处理时间包含了 db 本地的处理时间, 应用这种方式我们是无法评估出请求在单纯在 proxy 中停留的时间. 对于使用了 proxy 链路的用户, 由于在网络中多了 proxy 转发的代价, 所以 SQL 请求的延时会稍微比不用 proxy 链路的请求要慢. 因此为了不影响用户的体验, 我们希望 SQL 请求在 proxy 节点中停留的时间越短越好. 因此我们以 SQL 请求在 proxy 节点中停留和 proxy 与 db 之间的传输总时间 prT(proxy relay time)作为衡量 proxy 服务质量的重要指标. 如果实例 ins1 的请求在 proxy 节点的 prT>=ProxyRelayTimeLimit, 对于该请求, proxy 代价时间过长.
由于有现成的生产集群上 proxy 节点和 db 节点都安装了 tcprt 内核驱动, 我们打算 proxy 节点和 db 节点的 tcprt 数据来做类似的工作.
如图是 ins1,ins2 的 SQL 请求在 proxy 节点和 db 节点上的链路活动图. ins1 实例的一次请求时间 rt1= t0+ t1 + t2 + t3 + t4 + t5 + t6, 其中 t0 是 SQL 请求从 client 端到 proxy 端传输的时间, t1 是接收 client 端发的请求到向 db 端发送所需的时间, t2 是 proxy1 到 db1 的网络链路时间, t3 是 db1 的本地处理时间, t4 是 db1 到 proxy1 的网络链路时间, t5 是接受到 db1 端发的 SQL 应答到向 client 端发送的时间, t6 是应答从 proxy 端到 client 端的传输时间. 而 (ins1, *, proxy1) 的 tcprt 处理时间 proxyInTime1=t1+t2+t3+t4+t5, (ins1, proxy1, *)的 tcprt 处理时间 proxyOutTime1=t3, 通过计算 diff1 = proxyInTime1-proxyOutTime1 = t1+t2+t3+t5, 可以算出 SQL 请求在 proxy 节点中停留和 proxy 与 db 之间网络传输的总时间 prT.
由于网络延迟正常情况下在内部网络中比较稳定的, 如果 diff1 值变大了, 多出的部分, 往往是 proxy 节点贡献的, 因此我们可以大致通过 diff1 估计实例 ins1 在 proxy1 停留的的大致时间. 对于经过 proxy 的每个实例, 如果 prT>=ProxyRelayTimeLimit, 则认为 prT 过大. 我们算出 proxy1 上的各个实例的 prT 值, 得到 prT 值过长的实例数量占 proxy1 上活跃实例数的比例 r. 我们可以根据 r 的突升和范围来判断 proxy 及下游网络链路是否对用户形成影响. 为了判断 r 的突升, 首先, 利用上面判断 db 主机异常的方法, 回归出这个数据集的柯西概率分布 D.
因为比例 r 的取值是 0~1, 而柯西概率分布 D 的自变量 x 范围是负无穷到正无穷. 我们需要对原来的比率 r 做转化让他的范围扩充到正负无穷. 通过映射函数, 我们求出这一时刻下该指标的柯西概率分布的 CDF(x'), 由于 r 越小表明 proxy 越健康, 所以只有当 r>M 是才会进一步判断 proxy 是否异常, 如果 CDF(x')非常大比如: 大于 99.8%, 说明比率 r 突然明显上升, 需要引起注意. 为了减少误判, 我们还要判断出 r 值的突升和突降需要落到警戒范围. 因此需要一个必要条件: r 值的绝对值至少为 20%.
不过如果 r 本身就很大的话比如: 由于 proxy 升级到了一个有 bug 的版本上, 所有的实例从新版本上线后就一直慢, 由于数据集的中位数变成了 100%, 上面的方法就无法判断了. 我们还要再加个异常的充分条件, 那就是: 如果 r>MaxRatio(比如: 80%), 就判断为异常. 使用回归分布的方法适合当 r 发生巨变时来判断异常, 主要是为了找到 proxy 的急性病; 而后加的判断异常的充分条件适用于当 r 从一开始就不正常, 或者 r 缓慢变成不正常的情况, 是为了找到 proxy 的慢性病.
网络异常检测
为了容忍交换机单点故障, 每个节点 (代理节点和数据库节点) 会上联到一对 TOR 交换机上. TOR 中的高丢包率会导致大量 TCP 数据包重新传输, 并导致查询延迟变高, 失败连接增加, 从而导致用户数据库性能下降. 因此, 在短时间内, 识别网络故障并定位异常网络设备, 通过修复或者更换的方式去解决网络异常是至关重要的. 通过 TcpRT 采集的 TCP 连接上乱序数据包, 重传数据包, RTT 抖动和 RST 数据包的数量, 可用于网络故障排查.
如上图所示, 在分布式体系结构中, 每个节点相互通信, 比如, Proxy 节点到数据库节点的请求重定向. 我们绘制一个二分图来表示节点之间的关系, 顶点是 Proxy 节点和正在通信的数据库节点, 如果两个节点存在相互通信, 那么这两个节点存在一条链接边. 用虚线标记的链接表示在两个节点之间观察到大量网络异常事件(无序, 重传等), 否则我们使用实线代替.
根据主机到 TOR 交换机对的连接信息, 通过把主机节点替换成相应的 TOR 交换机对, 我们将上图 b 转换成上图 c. 直观上, 相连虚线数越多的顶点异常可能性越高. 因此, 我们定义公式 count^1.5/total 来衡量 TOR 交换机对发生异常的概率, 其中 count 表示虚线数, total 表示线 (虚 + 实) 数. count^1.5/total 值越大, 该 TOR 交换机对越有可能是异常.
小结
到目前为止, TcpRT 以每秒采集 2 千万条原始 trace 数据, 每天后台处理百亿吞吐数据, 秒级检测异常的卓越性能在阿里云持续稳定运行三年. 今年 TcpRT 的监控能力将包装成云产品开放给 RDS 客户, 给客户提供更好的数据库与应用诊断能力. 在技术上, 我们也在基于 TcpRT 开发更多的算法, 发掘更多的异常行为.
来源: https://yq.aliyun.com/articles/594517