当前位置: 首页 > 科技观察

去哪儿网MySQL日志分析实践,80%的数据丢失,为你挽回!

时间:2023-03-14 14:29:15 科技观察

1、后台日志记录了一个系统的行为,对于了解系统、诊断问题、辅助审计极为重要。通常,日志访问频率是最近一段时间内最高的。通过聚合日志、分析日志、汇总数据,帮助开发、运维、DBA了解业务的状态和行为。本次实践是分析MySQL的抓包日志。现有系统不足以支持新的需求,需要重构日志分析系统。现有的抓包系统将MySQL请求的所有SQL语句抓取并分析,写入Clickhouse。该数据可用于分析数据库的运行情况。可以查看当前实例、库、表有多少AppCode在访问,数据库资源是否可以回收,辅助审计。Sniffer抓包后将数据写入Kafka,Clickhouse直接消费Kafka队列中的数据并合并到Clickhouse中。Server程序根据日常级别定时读取Clickhouse中的数据,对数据进行分析汇总,解析请求IP对应的服务信息等,最后将结果写入MySQL。当前数据为日志数据,允许有一小部分丢失或重复。Kafka队列中的数据约为133w/s(单个队列最大可达33W),结果数据存储在MySQL中的大小约为4.6T。2.术语解释AppCode服务名称是一个服务的基本属性。Clickhouse是一个用于在线分析(OLAP)的柱状数据库。Kafka是一种高性能的分布式队列服务。Zookeeper是一种分布式开源协调服务。可扩展性当业务量增加时,服务可以以较低的成本满足增加的业务量的需求。聚合率将N条数据按照同一纬度聚合成M条数据,M必须小于等于N。聚合率=聚合后数据条数/聚合前数据条数。三、现状分析1、消费能力不足导致数据丢失。Clickhouse在容量不足、延迟高的Kafka队列中消费数据。而Kafka中的数据只保留3小时。根据对四个节点的Clickhouse消费的Kafka队列中数据的测试,大约有80%的数据丢失,导致数据不完整。2、服务信息计算不准确。Server是天级计算请求的服务信息。由于服务已经部署在云端,单个服务的IP变化非常频繁,导致服务器获取服务信息有延迟(最大延迟24小时),甚至很多计算结果是错误的。这使得在某些场景下分析的数据完全不可靠。3、聚合数据少目前只聚合了四个维度的请求数据,并且只有日级别的数据。粒度大,无法下钻查询具体信息,展示的维度和详细信息少。4.服务监控&可用性目前只有一个Server节点,缺乏可用性,没有详细的监控信息。4.需求针对当前服务的不足,设计一套服务需要满足当前日志计算性能和业务需求,并具有良好的可扩展性。五、目标1.高性能、高扩展、高可用服务需要具备以下特点:1)在资源有限的情况下保持高性能,最大化数据消耗和聚合能力,降低数据丢失概率,减少数据丢失存储延迟。2)当一个或多个高可用节点出现问题时,不会影响服务的整体可用性。3)高扩展性当未来业务日志量进一步增加时,只需增加部署节点或修改配置即可满足不断增长的需求,无需额外工作。2.减少计算请求业务信息的延迟。从请求IP到获取服务信息的时延降低到1分钟以内,确保99.9%的准确率。3、更多维度的汇总数据和下钻查询支持更多维度的汇总信息查询,部分场景支持下钻查询。4.监控提供多种监控数据,方便展示业务情况,方便根据监控定位问题。6.分析目前Clickhouse消费能力不足可以通过扩展节点和调参进一步解决,但是服务信息的计算Clickhouse做不到,而且OLAP类型的数据库对数据更新不是很友好,尤其是大-规模更新。如果要解决【减少计算和请求业务信息的延迟】的问题,有两种方案:如果采用第一种方案,会增加Sniffer端的不可控性,这与我们的初衷是一致的尽可能减少Sniffer端的资源消耗。违规,所以我们选择方案二。七。设计1.总体流程总体流程设计如下:2.模块划分所有模块的目标:高性能、高可用、高扩展。3.dubaiMeta1)说明由于基础服务无法提供IP-AppCode映射信息的高性能接口,需要自行实现该功能。连接基础服务的IP-AppCode映射信息提供的接口,实时从Kafka-OPS队列获取IP-AppCode映射变化的信息流,合并到当前数据,为IP提供接口-AppCode信息;2)设计因为dubaiMeta是提供基础信息的接口,访问量比较大,需要提供高性能、高可用、高扩展性。因此,dubaiMeta需要尽可能设计成无状态的服务。将所有IP-AppCode映射数据存储在MySQL中,映射到最新版本。所有IP-AppCode的更改历史也会存储在MySQL中。通过版本信息不断合并IP-AppCode变更信息到最新版本。目前IP-AppCode数据整体体量不大,可以在服务内部缓存,不需要使用其他缓存。这样既可以减少网络和缓存请求,减少请求时间消耗,又可以避免依赖缓存带来的性能和可用性问题。这样一来,dubaiMeta就有可能变成一个近乎无状态的服务。如果单个请求命中缓存失败,会请求基础服务的IP-AppCode接口,将变化的数据合并到缓存中。但是单个dubaiMeta节点如何在数据发生变化后与其他dubaiMeta节点通信,最终实现所有节点数据的一致性呢?解决方案有两种:尽可能保证单节点的高性能,检测单节点的数据一致性延迟,及时丢弃有问题的服务。这种方法可以最大限度地减少数据不一致带来的问题。因此,选择【变更通知】方式;3)启动进程时,服务先从Kafka-OPS不断订阅信息流,并将所有的变化信息合并到内部缓存,然后不断从Self-Kafka-OPS订阅变化信息流。将变化信息合并到内部缓存中,最后从MySQL中获取全量数据信息,将所有数据合并到缓存中,组合成全量数据。等到所有的数据合并完成,两个kafka队列都没有堆积,再提供服务。4)性能分析目前基础信息的数据量不大,可以在服务内部缓存,不需要使用其他缓存。不仅可以减少网络和访问缓存的时间消耗,大大减少请求的时间消耗,还可以避免依赖缓存带来的性能和可用性问题。单节点IP-AppCode映射变化会发起变化通知,异步通知其他节点,无需感知其他节点的存在,性能得到保证。5)可用性分析dubaiMeta目前将所有数据存储在本地缓存中,在MySQL中存储了一个复制和变更信息流。由于dubaiMeta几乎是无状态的,部署多个节点,通过负载均衡服务发送到不同的节点,可以保证整体服务的可用性。MySQL通过平台现有的HA保证可用性;6)可扩展性分析由于dubaiMeta几乎是无状态的,可以水平扩展而不影响其他节点,只需要在负载均衡器中添加节点即可。4.SnifferServer1)表示从Kafka-Sniffer队列中获取日志数据,补充日志中的各种业务信息,聚合数据。最后将聚合结果分批写入Clickhouse。由于队列中的数据量很大,不可能将所有的数据全量存储,需要将数据按照一定的维度进行聚合。通过降低聚合比(聚合后的数据数/聚合前的数据数),可以降低整体的数据量,在保证性能的前提下尽可能缩短存储时间。由于Clickhouse是OLAP分析的列式数据库,建议分批写入,以提高数据库的写入性能(每次不少于1000条记录)。2)针对单个队列设计,可以使用多个SnifferServer使用同一个consumerGroup来消费数据,保证消费者的性能和可用性,同时SnifferServer也具有一定的扩展性。SnifferServer是一种内存消耗和cpu消耗类型的服务。为了减少gc和内存消耗,通过重用对象来保证单个服务的内部对象数量不会有太大的波动,可以大大减少gc和内存占用,提高性能。根据功能,将SnifferServer拆分为三个模块:consumer模块、aggregator模块和writer模块。消费者模块消费队列中的数据,并将数据传递给聚合器模块;聚合器模块填充业务信息并聚合数据,并将聚合后的数据传递给写入器模块;writer模块将数据批量写入Clickhouse。①consumer模块功能从Kafka-Sniffer队列中获取数据,将数据合并后分批传输给aggregator模块。重点分析单个Kafka队列,提高消费速度的方法如下:经测试,在当前场景配置下,单个SnifferServer消费队列速度可达20W/s+。通过consumer的调优,完全可以达到33w/s(两台SnifferServer)的消费速度,完全满足业务的需求。②聚合器模块的功能接受来自消费者的数据,获取IP-AppCode映射信息和业务信息,并将这些信息添加到日志数据中。在一定数据量或超时后,按指定维度聚合该批数据。最后,将聚合结果传输到writer模块进行分析。如果每条数据都需要通过接口获取IP-AppCode映射和业务信息,聚合效率会大大降低,所以IP-AppCode和业务信息需要缓存在本地。聚合器模块还需要订阅Kafka-OPS和Self-Kafka-OPS变化,并将IP-AppCode变化信息合并到本地缓存。通过增加聚合器线程数来增加并发数,可以有效提高聚合效率。但是,更多的线程会不同程度地提高聚合率(与业务行为相关)。通过增加单批次的数量,可以降低聚集率。这两个指标需要根据需要进行测试和调整;经测试:同一个队列数据,单个SnifferServer的聚合率在10%-20%左右,两个SnifferServer的平均聚合率在15%-30%左右,三个SnifferServer的平均聚合率在30左右%,所以在相同配置的情况下,增加SnifferServer会增加聚合率,存储端会增加数据量。③writer模块函数从aggregator模块接收数据,缓存到内存中。当缓存数据超过N条或缓存时间超过M秒时,将缓存数据批量写入Clickhouse;每个batch在写入数据前尽量缓存,以提高写入效率,减少写入次数,预设单batch为1w(最小,可配置)条数据;3)分析每个模块都可以设置缓存大小和并行线程数,通过设置缓存大小和并行线程数可以提高效率。但是需要注意的是,缓存越大,并行线程越多,占用的资源就越多。而如果程序意外终止,会??丢失更多的数据,需要酌情考虑各个参数的配置。需要额外注意各个模块的启动和关闭顺序。①模块启动顺序先启动writer模块,初始化writer模块的缓存和线程,然后启动aggregator模块,初始化aggregator的缓存和线程,最后启动consumer模块,初始化聚合器的线程和缓存消费者模块。②模块关闭顺序为了减少数据丢失,正常情况下关闭服务时,需要关闭模块,按照以下顺序关闭所有Kafka连接;将consumer模块中的缓存数据全部发送给aggregator模块后,关闭所有consumer模块的线程;立即聚合聚合器中的缓存数据(所有日志数据完成业务信息,聚合完成)。所有聚合数据发送到writer模块后,关闭aggregator模块的所有线程;writer模块中的数据批量写入Clickhouse,最后关闭writer模块的所有线程。4)扩展性分析由于SnifferServer是一个面向计算的服务,从Kafka到SnifferServer再到Clickhouse,需要大量的数据传输,需要更好的cpu和网卡才能充分发挥SnifferServer的性能;①SnifferServer内部可扩展性每个模块都可以通过设置线程数和缓存大小来提高性能;②SnifferServer级别的可扩展性受限于单个队列在Kafka中的partition数量。最多运行相同分区数的SnifferServer。当分区数超过分区数时,节点将不再参与队列消费;③Kafka的Partition单个Kafka集群的分区理论上是没有限制的,但是受到服务器资源、Zookeeper、运维方便性等方面的限制,需要设置一个上限;④整个服务可以规划为不同的IDC,向不同的Kafka写日志数据,所以日志数据会被分区;5)可用性分析SnifferServer通过consumerGroup消费Kafka队列中的数据。对于通过冗余SnifferServer的单个队列,即使单个SnifferServer挂掉,也不会影响整体服务。5.SnifferAnalyze1)每日级别定时从Clickhouse获取聚合数据,分析、汇总、存储分析结果,并提供接口和页面展示结果的说明。2)设计分析总结的结果大致可以分为两类数据。第一类是初步聚合的粗粒度数据,数据量比较大。数据一旦产生,将不会被修改,只会被大规模删除。一般用于溯源、分析、下钻查询,使用频率不是很高。第二种是汇总报表数据。会有一些数据的大规模修改,并发查询比较多。与第一类数据相比,整体数据量较小,一般显示结果和接口查询较多;Clickhouse在大规模存储上单表查询和写入效率更高,压缩效率更高,但不擅长做并发查询和频繁修改数据,官网建议每秒查询100次以上。相比之下,MySQL适合存储小容量数据,对数据进行增删改查,擅长高并发查询。考虑到存储成本的节省和各存储的特点,第一类数据适合存储在Clickhouse,第二类数据适合存储在MySQL。为原始日志数据设置一个合适的过期时间,超过指定时间的数据将被删除。与原始数据相比,汇总数据大大减少,可以保留更长的过期时间。依托Clickhouse的分析能力,对于第一类数据,直接将Clickhouse中的数据分析写入Clickhouse,避免中间dump。第二类数据需要从Clickhouse获取,分析、计算、汇总,然后写入MySQL。每个任务在执行前都需要获取锁,只有成功获取锁的任务才能执行该任务。SnifferAnalyze多个节点在执行任务前会通过抢占方式获取任务锁,只有成功获取锁的节点才会执行任务,保证单个节点挂掉不影响分析任务的执行。但是,如果任务在执行过程中终止,目前需要人工干预。3)可扩展性分析目前,分析型任务节点的可扩展性有限。4)可用性分析由于分析类任务的扩展能力有限,只能保证节点间的高可用性。而一旦任务执行中断,就需要人为干预。6.Clickhouse1)讲解Clickhouse的架构图。例如,Clickhouse有两个分片(A和B),每个分片有两个副本([A1,A2],[B1,B2])。集群与三节点Zookeeper通信。①通过Zookeeper同步Clickhouse集群的Replication引擎的副本,Zookeeper存储数据块的元数据信息。FragmentA有A1和A2两个节点,A1和A2共同监控Zookeeper执行目录下节点的数据。A1节点写入数据块后,A1更改Zookeeper上的元数据信息。A2收到Zookeeper的元数据变化后,通知A1拉取指定数据块,A1将指定数据块发送给A2,完成数据传输。同样,A2也可以写入数据,通知Zookeeper更改数据,并向A1传输数据。数据同步是异步的,传输数据块时会去重。关于地牢的更多信息,请参考官网说明。②分片分片由Distribute引擎实现。分发引擎不存储任何数据。Clickhouse有一个Distribute引擎,支持按照指定的规则(random,hash,range,custom)将数据分发到各个shard。当数据写入到分布式引擎时,本地写入完成后立即返回数据。Clickhouse会按照规则周期性的向其他节点分发数据。③通过Distribute引擎查询查询时,无论分片规则是什么,Distribute都会将SQL分发到所有分片进行查询,汇总当前节点上的数据,最后返回给客户端。如果查询Replication引擎的表,则只查询本地数据。2)可用性分析Clickhouse的Replication引擎和Zookeeper实现了数据副本,保证了每个shard有一份可以down。Distribute引擎可以保证在所有的Clickhouse节点上都可以读写。但是通过数据同步是异步的。如果节点写入成功,在数据传输完成之前宕机,则无法恢复,未同步的数据块将丢失。3)可扩展性分析由于Distribute查询会将SQL分发到所有分片,无论分片的数据状态如何,都可以直接将新节点添加到Clickhouse集群中,无需担心旧数据迁移。但是需要考虑数据倾斜的问题。通过增加新增节点的权重,可以将更多新写入的数据发送到新增的分片。在向Distribute引擎写入数据时,Distribute引擎会先写入本地,然后依次转发给其他节点,这可能会导致本地网络流量增加,服务合并数据块时额外的cpu和IO使用问题。这个问题可以通过只写入本地表而不写入分发表来解决。但是需要通过一定的策略(比如轮询)写入不同的Clickhouse节点,解决数据倾斜的问题。7、监控1)说明目前的监控只是用来查看各个节点的状态,显示性能问题,不提供高层功能。监控报警接入公司现有监控系统。八、效果1、SnifferServer所有SnifferServer节点的平均内存占用约为12G,平均最大gc约为21ms,75thpercentile的平均gc约为0.6ms。两个节点SnifferServer的CPU(32核)峰值利用率约为70%,网卡峰值约为上行330Mb/s,下行400Mb/s。单个队列的峰值数据生成速度在30w/s左右,平均累计在20k左右,consumer速度比producer快。聚合器平均每秒聚合1.2次,数据聚合率约40%,写入器每秒写入约6次,业务信息和appCode的命中率接近100%。2、页面展示显示某业务的平均响应时间和查询次数。显示慢查询的平均响应时间和查询次数。9.总结SnifferServer完成业务信息与队列中的日志数据,聚合数据,并将结果写入Clickhouse,SnifferAnalyze周期性分析数据,并将结果写入Clickhouse和MySQL。整个系统具有高性能、高可用性、高扩展性等优点,内存和CPU使用率比较稳定。日志分析系统呈现的结果可以帮助开发者更好地理解业务行为,发现潜在问题,评估数据库方面的业务风险。结合慢查询风险指标体系,可以更准确地评估慢查询风险。