mongodb每秒写入量_Mongodb读取数据缓慢问题-Spark&Mongodb

问题

在我现在工作的集群环境上,读取基础表(30.6MB,47106条数据),耗时10秒,在另一个组(叫B组)吧,我的叫A组吧,的集群环境上耗时1毫秒。从mongodb的操作来看,其操作数超过了14000次/s。

问题分析

从后台的IO上来看,两个环境的sdb磁盘使用率都达到了100%。更仔细的话,发现两个环境中的资源写入量不同,我的组写入量在10MB/s~30MB/s,而B组环境写入量在500KB/s~1MB/s。简单的sdb磁盘使用率并不能代表mongodb的性能。所以要从从监控来看。

做一些测试所有统计值是在业务组将其最大流量的业务去掉之后的统计值;

数据库数据量从739GB删除到208GB后的统计。

我的组

B组

从平均网络传输来看,网络输入和输出均是我的环境的量更大。同时网络输出量比网络输入量大。而B组环境是因为业务不多,数据更多是流处理的写入数据,用于查询数据的批处理业务较少。因此,从网络传输量角度分析发现,我组存在大批量的文件传输。这些传输主要是表现在mongodb与磁盘交互的操作(查询、插入、更新、删除)。

Granfana监控能够监控当前mongodb的所有操作数量(A组)。

B组

我组环境是B组环境的4倍。其中主要的操作是插入操作。这也解释了我组为什么IO比B组高的原因。但该操作数不能反映实际的操作,例如对于批插入操作,虽然只有1次操作,但是实际上mongodb进行多次操作。

将mongodb的各个分片的操作数进行统计,进行分析。统计方法如下:

比如现在我进入了mongos容器中发现,(以前容器生命周期是由K8S维护)

kubectl exec -it -n a-service mongodb-cdf487ds bash

执行统计(在mongodb的容器内)

mongostat --port 26000 --discover

可以看到统计数据。

对该数据进行监控大约半小时的数据,进行统计其各个类型的操作数量。另外根据压测结果表明当前A组环境的总操作数达到2万/s时,其磁盘IO达到100%,在数据统计中为了避免最大值和最小值相差太悬殊而影响统计效果;因此对数据进行过滤,即过滤掉将1秒内的操作数大于两万的值。

插入数

查看shard1,shard2和shard3分片分片的主分片和副本分片情况。shard1和shard3的插入量明显比shard2大,表示数据存在倾斜。同时从数据库的量级来看也存在数据倾斜状况:

数据倾斜原因如下

(1)理想状态

采用预分片的形式对插入的数据进行负载均衡到各个分片中。例如已知设备ID有9个,那么根据该字段,将设备均匀分布到各个分片中,具有以下特点:

同一个批次的数据,能够分布式地(或同时)插入到不同的分片中,能够达到快速插入的目的;

同一个设备服务器尽可能地在一个分片中,便于在查询时,能够一次性地从单个分片拉取该设备的大量数据,减少网络传输以及磁盘IO。

当插入数据完成后,无重新负载的过程,减少了数据转移的负载。

(2)实际状态

因为时间是不停变化的,没有范围限制,因此数据的插入会大量地插入到某一个分片中,也就是A组中的shard1分片。其次,插入数据到主分片后,数据仍然要进行重新负载均衡,也就是在后台完成数据交换。

那么实际插入量= 主分片插入量+副本分片同步量+数据重新均衡量。

插入量总数

从插入数量的总数来看,其平均值为3570次,其中大于10000次的有254个时间点,在[5000,10000]区间的有398个时间点,在[0,5000]的有1568个时间点。

还有一些其他类型的操作,在此不列举。但是得到的结论如下

平均值统计如下:

查询:41.1/s

更新:31.2/s

删除:245.1/s

这三种操作的数据量相比于插入操作来说小很多,因此不做深入分析。将所有的操作合并计算法总操作的平均值为4201次/s。考虑到副本的同步,总操作数的平均值为2倍,即8401次/s,说明写入量实际上已经较大了。而经过性能压力测试发现:该插入数已经将达到mongodb的插入性能瓶颈点9938次/s.

还有从网络传输来看。网络输入量的平均值为2316KB/s,网络输出量的平均值为3862KB/s。

业务分析预分片问题

当前无一个良好的方案,指定用于预分片的字段。既无法做到以下几点:

数据均匀分布到各个shard

按照某个字段进行范围查询时能够从单个shard取数据

因此,设计出良好的预分片字段是有困难的

2. 瞬时写入量问题

从目前业务分析,我负责的业务模块的Spark流处理作业从采集数据写入到mongodb的表,其中各个kafka主题统计如下

从统计表中看出,总数据量大约在1670KB左右吧,会有误差。

但是从sdb盘磁盘监控以及mongodb网络传输监控来看,不够匹配的是,sdb磁盘中的写入量在10MB/s。那么这个更多的数据量写入无法评估。mongodb没有统计实际操作量的能力。但从副本集的机制来看,副本是需要进行同步备份,那么流处理的总写入量约为3600KB/s。该写入量和sdb磁盘总写入量仍然有6400KB/s的数据量差距在。该数据量包括两种可能:spark批处理的操作(大批量数据同时写入)。

mongodb在重新负载均衡时的数据迁移(数据倾斜)。

关于数据倾斜,在预分片中已经进行了讨论,因此不展开。主要讨论的是spark批处理的操作问题。这里仍然用最大流量的业务说明。和同事聊了一下,A组的业务,当数据量多时,可能无法在10分钟内执行完成。这是由于在同一个批处理中操作较为复杂。从业务流程上的暂时未找到可优化点。

所以主要关注Mongodb。

问题总结

性能瓶颈在同等容器配置(4核10G+mongos3核)、带有自动平衡机制情况下,其性能瓶颈值为14803次总操作/s,9940次插入操作/s。通过测试的最佳性能为46015次操作/s,但是同时也牺牲了较大的硬件资源(6核18G+mongos3核、带有自动平衡机制)。另外,如果做出良好的预分片,那么插入性能能够提高为原来的2倍。

从我负责的kafka来看,当前流数据的最高写入数量为3921条/s,该值需要进行副本写入,因此已经达到mongodb的插入性能瓶颈。

解决方案

1. 找到合理的预分片字段

Ø 目标:减少数据自动平衡的代价。

Ø 理论:尽可能找到一个具有确定值域的字段,且该字段的每个值都对应着相近数据量。

Ø 行动项:当前业务大部分数据都是时间序列数据,需要根据时间回溯,很难找到。

2. 采用中间存储

Ø 目标:减少中间结果的读写,大幅度降低mongodb的性能压力。

Ø 理论:数据的特点是业务读取数据都是按照5分钟、10分钟等范围读取数据块,如果使用能够顺序读写的文件块,理论上能够提高磁盘利用率,将随机读写转化为顺序读写。但是存在1分钟业务,这将会导致大量的小文件产生,分布式文件系统的缺点是对小文件的支持不太好,需要更多资源消耗。

Ø 行动项:可以对该方案进行试验并尝试观察至少一周。对于小文件问题,可以定时合并以及老化。

3.存储分离

将中间结果和最终结果分离到不同的存储系统中,不会互相干扰,但是要再讨论讨论。

4. 数据量降级

现在每天表都很大,有的大表既是中间表也是结果表,同时,有的业务为10分钟定时任务,但采集了过去24小时的数据,对于大表的查询范围较大,导致计算性能下降。web的查询返回了全量数据。

怎么解决呢?

表的老化时间调小,对于中间表,设定老化时间为1天至2天。对于结果表,设定老化时间为7天。缩减查询范围,将查询范围缩减到和定时任务相同的时间间隔。 降低web返回数据量,使用分页技术,每次仅查询该页内容。

5. 使用缓存

考虑大量使用Redis缓存机制。

记录一些常用命令

docker run -it --rm 镜像名 bash

运行spark jar 任务

spark-submit --class 应用类名 --master local[20](本地或者集群模式) --name 业务类名 --driver-memory=10g --conf spark.ui.port=45005 --conf spark.default.parallelism=48 --conf spark.streaming.concurrentJobs=10 jar包位置

使用如下命令来监控mongodb的各个分片的操作数。

mongostat --port 26000 –discover

Spark & Mongodb转化量测试一些总结

很多时候数据的插入过程是突发的。例如总共4万条数据,默认采用kafka的分区数,那么spark的分区数则是4个。那么4万条数据会几乎平均分配到这4个分区中。对于单机版的Spark来说,就是4个线程同时写入Mongodb,那么对应于插入操作的统计图中的突发流。每个线程的写入都会造成一次突发流。同时每次插入都伴随着更新操作和删除操作。删除操作主要是自动平衡机制在发挥作用;更新操作则是在更新索引。

Flink & Mongodb转化量测试一些总结

在测试时候,实际上到后期的数据出现突发现象是由于kafka的数据形式导致的。通过监控每个kafka的消息而得到的日志信息,发现存在一段秒时间内没有kafka数据到达。虽然Flink和Spark最后都转变成突发流量,但是性质不同。Spark是 主动堆积,而Flink是由于kafka没有数据了,无法持续写入。只要kafka一直有数据,那么Flink就会呈现出均匀的写入趋势。

为了比较spark是否受到kafka的影响,测试了一秒窗口的spark-streaming性能,只看写入效果。

同时监控Spark每秒从kafka收到的数据量,该收集方法是“dStream.count().print()”,不属于shuffle,因此不影响性能。

该图中前期存在消费不规律,这是因为当前kafka消费组中存在资源堆积。而在多次窗口消耗掉kafka堆积的数据后,则明显的呈现出规律性。大致判断,目前这个kafka业务主题是每分钟发送一次的,不是每秒,这个从上图中能够看到每次数据的波动正好是1分钟。这个和从Flink得到的规律是一致的。同时表明从Granfana监控中看到的kafka的每分钟平均值存在误导性,实际上指的这一分钟的最大值。

为此考虑到对spark从kafka接收的数据量降低,修改变量为

“spark.streaming.kafka.maxRatePerPartition=10”,表示每秒spark的分区从kafka最多拉取10条数据,本测试中kafka共四个分区,而Spark使用了kafka的分区,也就是最多拉取40条数据。

由于kafka的数据是100条压缩为10条,所以看到这个插入量最大值在4000条,也就是Spark从kafka拉取了40条/秒。下面看Spark的拉取数量。

因此得出结论:Spark流处理的波峰是可以控制的,且时间窗口可以压缩到秒级别。

修改Spark的流处理参数“spark.streaming.kafka.maxRatePerPartition=1”和“spark.streaming.receiver.maxRate=1”,发现Spark仍然是每分区收集1条消息,如下所示:

该图中可以看出写入量在400左右波动,最高值有达到800,这个是mongodb的延迟引起的。实际上,400条是数据量较少的。下图显示Spark从kafka获取的数据情况。

可以发现Spark收集数据稳定在4条左右。

你可能感兴趣的:(mongodb每秒写入量)