前言

这次是慢查询的故事,一些情况下很难避免,我们屏蔽来源,只讨论量。从最开始只要简单处理能过滤报警(几百),坏个盘也就是这么量。到小 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次

dumps

单条1W次

loads

单次1K条

dumps

单次1K条

loads

json

754

771393

0.1193

0.2423

0.0095

0.0402

pickle

975

318576

1.8495

1.6626

0.1121

0.0681

marshal

791

495263

0.0505

0.0606

0.0026

0.0039

ujson

698

712394

0.0451

0.0597

0.0048

0.0105

msgpack

629

640259

1.5178

2.6460

0.1622

0.4593

marshal 是不安全的,不通用的,不同版本也不兼容的。不过这速度,又是内网环境,我最终义无反顾选择了它。

插入一个无效的优化手段,乍一看我是认为有效的。来个简单常见,就是消息记录,然后按 A、B分类,参考代码如下:

def cls_message(handler, raws):"""对日志分类,然后简单格式化下"""logger = logging.getLogger(handler)slow_dict = {}pending_dict = {}for message in raws:key = make_key(message)if message.get('flag') == 'Pending':pending_dict[key] = formatter(message)logger.debug("save client Pending message <%s> success!" % key)elif message.get('flag') == 'Slow':slow_dict[key] = formatter(message)logger.debug("got client slow message <%s>" % key)else:logger.warning("Message flag %s not define" % message.get('flag'))

这个坑在多客户端就已经出现了,因为多进程写日志,采用文件锁。多客户端同时写,大家应该能猜到,效果就看表格吧。

场景(2000条) 耗时
不打日志 0.002063274383544922
单线程打日志 0.12387228012084961
多线程打日志(4) 0.4494140148162842
多进程打日志(4) 36.29296827316284

偷懒点,可以直接关闭细节日志。深入思考也可以看出,单线程性能是牛逼的,不过为了写日志,上下文切换,性能直接降低近百倍。采用多线程,性能在降几倍,说明这种场景下,线程切换开销大于收益。而多进程受限于原先使用的文件锁,不太适合比较。

分布式实时报警(几万)

当各类优化到极致,细节日志都关掉,这样单实例的瓶颈已经出现了,很难在大幅度提升了。而基于文件的跨进程通信也是一个很慢的效率,CPU 密集型的 多线程也不会有效果,多进程创建销毁开销也很大,基本不太适用这种场景。因此基于 socket 的并行内存实时计算,可以预见是一个简单有效的方法。抽象下基础服务,我们大致需要如下:

  1. Master 服务,接收 Client 信息,然后转发给指定 Consumer 服务
  2. Consumer服务,只接收指定来源信息,消息独立而有序,避免跨进程内存共享
  3. LogServer服务,日志统一接收处理,提高效率,也方便排查问题。
  4. Monitor服务,对符合条件消息,进行报警

服务拆分好,接着选 RPC 了,可以直接 Socket 封装,也可以使用已有的。当前 LogServer 采用Socket 通信,因为 logging 自带客户端,只需要实现服务端就可以了。

场景(2000条) 耗时
单线程写 socket 日志 0.11522245407104492

其他的采用 Zeromq,这个性能与Socket一样,不过使用更方便,为了支持多客户端同时请求,才用了 ROUTER - DEALER 作为通信框架,参考了 locust_rpc 实现,简单易用。

关于消息拆分粒度,我也做了个尝试,就是Master 接收到消息后,在按保序前提下分为 M 块,然后发送个 M 个Consumer,结果速度反而更慢了。我总结了下消耗:

虽然 Consumer 变快了,但是路径更长了,速度反而更慢了

进入内存计算后,关于如何不丢失日志,我其实困惑了一段时间。

方案一、预写日志。Consumer 收到消息,先写日志,在处理。这样又有新问题,比如网络不好,Client 未收到指定码触发重发,那么如何实现日志回滚?第二就是运行很长一段时间,一旦重启,这加载速度也太慢了。

方案二、快照。这个好理解,缺点不及时?相对于预写日志,每条都写。然而真是这样吗?

再回到方案一的问题,是不是负负得正,一的问题不就是二的答案吗!

因为最近一直优化细节,反而忘记了整体,在最初设计中,为了保证数据不丢失,我们设计的原子操作是每次请求,而不是每条日志!跳出每次日志的来看,直接对每次请求执行快照,不就可以保证数据不丢失了。而覆盖写单次效率很不错,参考数据如下:

类型

次数

单次大小

虚拟机(三次)

覆盖写

1000

1024

0.12、0.12、0.13

覆盖写

1000

1024 * 4

0.12、0.11、0.13

覆盖写

1000

1024 * 10

0.14、0.13、0.16

覆盖写

1000

1024 * 1000

2.1、2.2、1.8

Consumer 初始化的时候加载快照,之后每次消息过来,都在内存中计算。计算完毕,触发一次快照覆盖,然后返回 Master,失败返回异常码,则重发再来。

支持更大并发

考虑到在 Master 端进行数据在分割并没有效果,那么把 Client 包含进来,当做一个整体看待。正常按行读日志,效率也够了。毕竟写日志的上限在那里摆着,不用过度优化。不过日志格式转化是个CPU密集型任务,是可以通过 RPC 改为并行计算,顺带也实现了数据分片,简单的实现更高并发。

优化总结:在数据流向路径不变的情况,优化原先模块或者拆分为并行计算,效果会很明显;反之为了并行而增大路径,额外增加的序列化成本,以及并行开销可能导致速度更慢。

参考文献:

zmq_rpc:  https://github.com/locustio/locust/tree/master/locust/rpc

Python进阶 | 实时计算 | 如何从几百到几千再到几万吞吐量相关推荐

  1. 阿里巴巴双11数据大屏背后的实时计算处理

    作者:藏六.黄晓锋.同杰 1.双11数据大屏的实时计算架构 1.1 背景 2016年的双11我们的实时数据直播大屏有三大战场,它们分别是面向媒体的数据大屏.面向商家端的数据大屏.面向阿里巴巴内部业务运 ...

  2. 【双11背后的技术】双11数据大屏背后的实时计算处理

    选自<不一样的技术创新--阿里巴巴2016双11背后的技术>,全书目录:https://yq.aliyun.com/articles/68637 本文作者:藏六  黄晓锋  同杰 1. 双 ...

  3. 第六章 大数据,6.1 双11数据大屏背后的实时计算处理(作者:藏六 黄晓锋 同杰)...

    6.1 双11数据大屏背后的实时计算处理 1. 双11数据大屏的实时计算架构 1.1 背景 2016年的双11我们的实时数据直播大屏有三大战场,它们分别是面向媒体的数据大屏.面向商家端的数据大屏.面向 ...

  4. 运维学python哪部分_初入运维的小伙伴,别再问需不需要学Python了

    这篇文章来自百度知道(你可以搜索"运维需不需要学Python"),或许有些观点不一定正确,但运维学Python这事毫无疑问是主流了,不信可以到各大招聘网站,看看但凡招聘运维有编程技 ...

  5. 运维学python用不上_运维朋友们,别再问需不需要学 Python 了!

    运维人员需不需要学开发?需不需要学 Python?PythonN 和 Shell 有什么区别?天天问这种好水的问题,我实在受不了,决定帮大家扫扫盲. 现阶段,掌握一门开发语言已经成为高级运维工程师的必 ...

  6. 基于python快速简便地实时计算金融技术指标

    从简单应用到平台框架应用,不同场景下,基于python快速简便地实时计算金融技术指标的方法,总结如下(鄙人之前走了不少弯路,以下五种场景实现及避坑方法,分别介绍给各位朋友,请借鉴): 一.tradin ...

  7. 日均百亿级日志处理:微博基于Flink的实时计算平台建设

    来自:DBAplus社群 作者介绍 吕永卫,微博广告资深数据开发工程师,实时数据项目组负责人. 黄鹏,微博广告实时数据开发工程师,负责法拉第实验平台数据开发.实时数据关联平台.实时算法特征数据计算.实 ...

  8. python进阶练习题:IRR计算 - 盈利能力的评价【难度:2级】--景越Python编程实例训练营,不同难度Python习题,适合自学Python的新手进阶

    python进阶练习题:IRR计算 - 盈利能力的评价[难度:2级]: <跨度风格="字体重量:粗体;颜色:金"> IRR </跨度>或内部收益率,是基于所 ...

  9. python进阶——AI视觉实现口罩检测实时语音报警系统

    大家好,我是csdn的博主:lqj_本人 这是我的个人博客主页:lqj_本人的博客_CSDN博客-微信小程序,前端,vue领域博主lqj_本人擅长微信小程序,前端,vue,等方面的知识https:// ...

最新文章

  1. 141. Linked List Cycle
  2. 畅捷通t+标准版操作手册_外网IP访问畅捷通T+教程
  3. UiAutomator喷射事件的源代码分析
  4. JSP自定义标签(2)
  5. 在WPF中集成OpenTK
  6. java spark 项目_spark应用程序如何在Java项目中运行
  7. C语言C++编程软件推荐及下载方式
  8. RapidScada免费开源Scada组态软件系列教程1-初识RapidScada
  9. Echarts中使用china.js
  10. vivado基本知识
  11. 金碟打印凭证或多联纸格式时十几张后错位
  12. python 使用 pyodbc 访问 Sql Server
  13. 2020年ICPC辽宁省赛- 最长回文串(Java)
  14. AI可以自动生成绘画吗?分享几款AI绘画软件
  15. JavaScript异步编程(1)- ECMAScript 6的Promise对象
  16. LMDI 理论推导详解【从理论到Python-MATLAB实现(理论)】
  17. excel解析html代码,使用excel vba解析HTML的错误
  18. 小白也能学会的调色教程,你学会了吗?
  19. 应用全屏启动页适配小米9(水滴屏)
  20. 计算机企业照片墙设计效果图,怎么用自己的照片做电脑墙纸

热门文章

  1. 后台系统-医院设置信息接口开发
  2. Linux 程序编译过程
  3. oracle vsm5,傲里拔尊--宝马M5 VS.奔驰CLS55AMG
  4. 现代信息检索1----课程介绍
  5. MySQL快速下载地址
  6. KKB:mybatis的分页:pagehelper
  7. 什么叫诚实_什么是诚实?
  8. 手把手教你制作Excel抽奖器
  9. calendar库:Python日历的处理与生成
  10. Python Day1 基础语法和变量【零基础】