前言
这次是慢查询的故事,一些情况下很难避免,我们屏蔽来源,只讨论量。从最开始只要简单处理能过滤报警(几百),坏个盘也就是这么量。到小 Region灰度,单Client 峰值8K,再到大集群单 Client 峰值几万,程序一路翻车。
对于报警而言,我们更关注各个来源每秒可以处理的量,总体意义不大,数据来源主要为双机压测结果(非Server吞吐量,且包含网络请求耗时)。
一些环境下是不存也不方便引入其他中间件的,一个Centos7 自带的 Python 2.7.5 一路黑到底。
项目不同阶段
日常需求阶段(几百)
慢查询有一定格式,我们简单抽象为 A 和 B,其中含义如下:
A: 检测到一个查询还没结束,无法判定,因此记录到日志中
B: 检测到一个很耗时的查询,并且已结束,记录到日志中
因此常见组合方式为:
- AA...AB (多次受到慢查询存在告警,最后受到结束告警)
- B (刚好检测到一个耗时的查询,并且已结束)
- A...A (异常情况)
消息消费大致是B已完成,需要实时处理,A则需要抑制并判断阈值在报警,因此可以将服务分为 Server 和 Client:
Client 读取日志,记录 Offset,过滤无效日志,发送成功则刷新 Offset,否则暂停读取,重发。
Server则分为Master 和 Monitor,Master处理实时数据,其他的写入文件,完成就返回,剩下由有Monitor 消费,由此借助文件实现跨进程通信,以及消息实时落盘。(消息生成主键,因此重复写不影响,Monitor 读取时候直接去重即可。)
有很多成熟文件DB,简单的操作背后都是时延增大,本文是直接读写文件。因写文件而速成,也会因文件也受限。
多客户端冲突期
第一版有个很大的竞争点,就是所有消息公用一个文件作为跨进程通信与落盘。因此多客户端已接入,立马互相竞争文件锁,性能瞬间只剩几十,惨不忍睹。
提示:一个 Region 两台统计点,日志是随机打过去的,因此采用放在同一个文件进行比对。当然这是不合理的,当对同一个消息AB很短时间间隔内到达两个节点。再经过采集合并发送到报警服务器,基本无法保证有序。这个阶段已调整,单节点保序。
按 Client 分消息队列,就是分文件了,性能又恢复了。
小故障爆发期(几千,比如2-3千)
这个时候就是把以前偷的懒都给补上了,当然不是每个思路都有效,比较有效的有:
- 各类批量操作: 批量写消息、批量查数据库等
- 更换序列化,比如由 Json 改为 Marshal
- 优化时间格式处理, 自带的 strptime 性能很差,可以小用 panda
- cache,集群、节点等相关属性变化很低,适合放内存
替换实现是一个系统活,有一些测压对比统计,可以供大家参考,真正有提升在替换。
包 |
内置 |
单条序列化长度 |
1K列表序列化长度 |
单条1W次 dum |