数据处理是一项细活,每步相关问题都值得梳理清楚
日志处理流: #
日志格式->写日志文件->读日志文件->数据队列->流处理入库->离线分析->报表API查询->可视化
日志格式:
- 自定义数据格式,比如field之间分隔,如果以| 竖线分隔,那么就要做好每一个filed值的encode和decode处理,否则很可能出现数据错乱导致下一步解析问题;
- 使用json结构,简单,容易扩展,但是空间利用率有点浪费; 像在golang,最好不要使用自带的标准json 库,效率不高
写日志文件:
- 对于高并发程序,写日志做好缓存批量写入而不是一次请求写一次文件,要不IO压力大,请求服务响应也有变慢
读日志文件: - 使用filebeat持续监控日志文件读取数据推向下一个数据点; filebeat把日志读取的偏移位置与日志文件inode绑定,即使日志产生切割,但是inode不会变化,多个日志文件的内容不会出现读取跳过的问题,但要注意 给filebeate设置日志文件时,使用正则去指定 日志整体(比含有某命名前缀的 ailog* 或者某个目录,而不是单个日志文件名,如1.log)
数据队列:
- 通常后端服务会起多个服务实例做负载均衡,如果日志从文件读取后立刻写入数据库,比如MySQL,数据库的写入压力就起来,会出现超时数据无法写入现象;使用数据队列比如kafka来缓和数据IO压力; 消息队列的选择有得讲究,生产者-消费者,pull 还是push, 是否保证发收顺序,消息单播还是广播等要根据业务场景选择;
流处理入库
- storm
- 在前面有消息队列抗压的前提下,入库程序或者脚本可以根据有条不紊的执行
离线分析
- 基于原生数据进行聚合,清洗,训练,这是人工智能平台不断持续演进升级的后劲
报表API查询
- 查询慢需要解决,druid,甚至出现基于机器学习的查询过程,LRU
初始公司,人手少,开始阶段忙于写业务,就 make it work, make it right and make it fast三点来看,都做到了但是不健壮,一方面与建设者的经验,眼光和能力有关,另一个方面,基础架构的复杂与业务的体量大小这中间的trade-off,衡量不好,后续改造成本负担不小;
过程记录: #
数据流量大的日志在入库时 势必会对数据库产生比大压力,虽然目前日志是先存放到文件中,再从文件解析后入库,存在可能因为日志解析后入库导致的日志文件消耗过慢,从而由日志文件切割产生日志数据遗漏问题,解决方法是可以在数据库前加一个消息队列如kafka来缓和压力:一方面可以加快 日志解析速度,另一方面可以减缓数据库的入库压力。
日志收集:
使用filebeat持续监控日志文件读取数据推向下一个数据点;
另个一个是数据的聚合问题:
机器人对话服务中每一通对话由多个QA组成, 每一条日志保存了一组QA,目前一条日志 作为一条row存放于MySQL表chat中(MyISAM存储引擎); web界面显示是以一通对话组合显示,所以要把 每一通对话的多个QA聚合起来;想了以下方法:
a. 使用即聚合返回 一个是给前端提供的api 中用group by 查询; 优点是简单,不折腾,缺点: 数据量大后,比如100万条后,查询明显感觉很慢
b. 离线聚合 把chat表中的数据按sessionID聚合后 存放于merge_chat表中;优点: 能不小程序解决a.中查询慢的问题;折腾的地方是, 每一通对话聚合结束的标志不明确,解决方法是:
- 根据业务场景,一般一通会话的时长不会超过3分或者5分,那么就可以每5分对一通对话进行聚合处理; 另外一个是 有时对话中会有产生一个end,这个可以作为对话结束的标志,检查对话中如果产生end就立马就对内存中的已经聚合的对话数据进行刷库持久化处理
- 另个对于老的数据,因为已经是确认是对话已经完成的情况下,直接从chat表中拿数据聚合后入库merge_chat表即可; 为了确保数据的正确性,查询老数据时加上时间限制,距离现在超过一定时长(比如5分,一个小时) 才会把数据取出聚合
这两个结合一起使用达到聚合对话数据的目的 - chat表中已经聚合的数据要作物理删除或者逻辑删除(把标志字段值设为已删除)
c. 优先从聚合表中 但优先从merge_chat表中取数据,merge_chat表中没数据时,从chat表中拿所需要的数据进行聚合后,存入聚合表中;考虑到分页情况, chat表只保留一条session数据并且有标志显示数据已经聚合; 优点: 用到时才聚合,省cpu; 缺点: 要处理好 两张表的数据一致性关系。 这个算是a和b的折中方法
d. 日志存放ES或者MongoDB 用非关系型(文档)数据库,日志这种结构不定的数据,应对随时可能要增加一些指标项的情况, 修改起来要不方便不少;