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

如何在SQLServer中每天处理4.3亿条记录?

时间:2023-03-22 10:11:45 科技观察

项目背景这是某数据中心的一个项目。工程难度大得离谱。这个项目真的让我觉得商场就像战场,而我只是其中之一。战术太多了。高水平的较量太多,内幕也太多。对于这个项目的具体情况,有时间我会写相关的博文。本项目需要进行环境监测。我们暂且将被监控的设备称为采集设备,采集设备的属性称为监控指标。项目要求:系统支持不少于10万个监控指标,每个监控指标的数据更新不超过20秒,存储延时不超过120秒。那么,我们通过简单的计算就可以得出一个理想状态——要存储的数据是:每分钟30w,每小时1800w,也就是每天4.32亿条。实际上,数据量会比这大5%左右。(其实大部分都是信息垃圾,可以通过数据压缩处理,但是别人想惹你怎么办)以上就是项目需要的指标。我想很多在大数据处理方面有丰富经验的同学都会尝试一下。靠鼻子,仅此而已?嗯,大数据处理我也看了很多,但是没接触过。其他人清楚什么是分布式,什么是读写分离的时候,好像真的很容易解决。然而,问题并没有那么简单。正如我上面所说,这是一个非常糟糕的项目,是典型的行业恶性竞争项目。没有更多的服务器了,但这台服务器不仅配备了数据库和集中采集器(即数据分析、报警、存储程序),还支持30万点的北向接口(SNMP)。在程序没有优化之前,CPU占用了80%的时间。%多于。因为项目需要使用双机热备份,为了省事,减少不必要的麻烦,我们把相关服务放在一起,这样可以充分利用HA(外购HA系统)的特性。系统数据的准确性极度异常。要求从底层采集系统到顶层监控系统,一条数据不能有差异。我们的系统架构如下。可以看出数据库压力很大,尤其是LevelA节点:硬件配置如下:CPU:Intel?至强?处理器E5-2609(4核,2.40GHz,10MB,6.4GT/s)内存:4GB(2x2GB)DDR3RDIMM内存,1333MHz,ECC硬盘:500GB7200RPM3.5''SATA3硬盘,Raid5。数据库版本使用SQLServer2012标准版,HP提供的正版软件,缺少企业版的很多NB功能。写入瓶颈遇到的第一个绊脚石是我们发现在现有的程序下,SQLServer根本无法处理如此大量的数据。具体情况如何?一般我们的存储结构为了存储大量的历史数据,会进行物理分表。否则,每天数百万条记录一年将是数亿条记录。所以,我们原来的表结构是这样的:CREATETABLE[dbo].[His20140822]([No][bigint]IDENTITY(1,1)NOTNULL,[Dtime][datetime]NOTNULL,[MgrObjId][varchar](36)NOTNULL,[Id][varchar](50)NOTNULL,[Value][varchar](50)NOTNULL,CONSTRAINT[PK_His20140822]PRIMARYKEYCLUSTERED([No]ASC)WITH(PAD_INDEX=OFF,STATISTICS_NORECOMPUTE=OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS=ON,ALLOW_PAGE_LOCKS=ON)ON[PRIMARY])ON[PRIMARY]No作为唯一标识,采集设备Id(Guid),监控指标Id(varchar(50)),记录时间,记录值。并以采集设备Id和监控指标Id作为索引进行快速查找。批量写入当时是使用BulKCopy完成的,没错,就是这样。它声称可以在几秒钟内写入数百万条记录。.seInternalTransaction){BulkCopyTimeout=300,NotifyAfter=dt.Rows.Count,BatchSize=batchSize,DestinationTableName=desTable}){foreach(DataColumncolumnindt.Columns)sbc.ColumnMappings.Add(column.ColumnName,column.ColumnName);sbc.WriteToServer(dt);}返回t.Rows.Count;}怎么了?上面的架构对于每天4000万条数据是可以的。但是在后台调整到上述配置时,集中监控程序内存溢出。据分析,接收到的数据过多,存储在内存中,但没有及时写入数据库,最终导致生成的数据大于消耗的数据,导致内存溢出,程序无法运行.瓶颈究竟在哪里?是因为RAID磁盘有问题吗?是不是数据结构问题?是硬件问题吗?是SQLServer版本的问题吗?没有分区表有问题吗?还是程序问题?当时时间只有一个星期,如果一个星期没搞定,项目监理就会让我们走人。于是,出现了连续工作48小时的壮举,出现了一个到处打电话求救的捕鸡人……不过这时候需要的是冷静,再冷静一下……SQLServer版?硬件?现在不太可能被取代。RAID磁盘阵列,不应该。这到底是怎么回事,我实在是淡定不下来。现场紧张的气氛你可能不理解。其实过了这么久,我已经很难再回到那种状态了。但是可以说,也许我们现在有各种各样的方法,或者我们有更多的局外人的思考,但是当一个项目压迫着你,你快要放弃的时候,你当时的想法和考量是被现场制约的环境因素。可能会出现重大偏差。它可能会让你快速思考,也可能会停止思考。有的同事甚至在这种高压环境下犯了更多的低级错误,思维已经完全乱了,效率也降低了……36小时不闭眼,还是只在工地上(有泥巴)下雨天到处,干了就抹灰)眯两三个小时,然后继续连续工作一周!或者继续!很多人给出了很多思路,但是好像有用,又好像没用。等等,为什么是“好像能用,又好像不行”?我隐约似乎抓住了一丝方向,那是什么?顺便验证一下,我们现在是在现场环境中运行,之前没有问题,但不代表现在的压力下没有问题。在一个大系统中分析这么一个小功能,影响太大了,应该分解一下。对,就是“单元测试”,就是对单个方法的测试。我们需要验证每个功能。每个独立的步骤在哪里花费时间?逐步测试和验证系统瓶颈修改BulkCopy参数首先我想到的是XiukarBulkCopy的各种参数,BulkCopyTimeout和BatchSize,不断测试和调整,结果总是在一定范围内波动,这实际上没有效果。它可能会影响一些CPU计数,但它远未达到我的预期。写入速度仍然在5秒内从1w到2w波动,远远不能满足20秒内写入20w条记录的要求。采集器存储对,上面的结构,每个指标的每个值都有一个记录,是不是太浪费了?那么用采集设备+采集时间作为记录是否可行呢?问题是,如何解决不同采集设备属性不同的问题?这时候同事大显身手,监控指标+监控值可以用XML格式存储。哇,可以这样吗?对于查询,可以使用forXML的形式。于是就有了这个结构:No,MgrObjId,Dtime,XMLData结果校验,比上面的稍微好一点,但也不是太明显。数据表分区???那时,我还没有学会这项技能。看了网上的文章,感觉挺复杂的。我没有太多时间,所以我不敢尝试。停止其他程序,我知道这肯定不行,因为软硬件架构暂时不能修改。但我希望验证这些因素是否受到影响。结果发现提示确实很明显,但还是不符合要求。是SQLServer的瓶颈吗?不会吧,难道这就是SQLServer的瓶颈?网上查了相关资料,可能是IO的瓶颈,尼玛,我还能怎么办,要升级服务器,是不是要更换数据库,但是项目会提供吗?等等,好像还有一个东西,索引,对索引!索引的存在会影响插入和更新。删除索引。是的,去掉索引后查询会变慢,但我必须先验证去掉索引是否会加快写入速度。如果果断去掉MgrObjId和Id字段的索引。跑完之后,奇迹发生了。每次写入10w条记录,可以在7~9秒内完成写入,满足系统要求。如何解决查询?一张表一天需要4亿多条记录,没有索引是无法查询的。该怎么办!?我又想到了我们的老办法,物理分表。是的,我们以前是按天划分的,所以现在我们是按小时划分的。那么24张表,每张表只需要存储大约1800万条记录。然后在一个小时或几个小时内查询某个属性的历史记录。结果:慢!慢的!!慢的!!!如果不建立索引,查询超过1000万条记录简直是不可想象的。我还可以做些什么?继续分表,我想到了,我们也可以继续按照底层采集器分表,因为不同的采集器采集设备不一样,那我们查询历史曲线的时候,只能查历史曲线单个指标的,那么这个可以分布在不同的表中。结果通过收集10个嵌入式嵌入表,按24小时分表,每天生成240张表(历史表名类似这样:His_001_2014112615),最终一天写入4亿多条记录,支持简单查询这个问题解决它!!!解决了以上查询优化中的问题,这个项目的难度就解决了一半,项目主管也不好意思上门找茬了。我不知道这是一种什么样的战术安排。时间一长,快到年底了,问题又来了,就是把你拖死,让你年底不能接其他项目。这次的需求是这样的:因为上面是模拟10万个监控指标,现在真正上线,不过只有5万台左右的设备。那么这显然不符合招标文件的要求,不能接受。那么该怎么办?这些聪明人想,既然监控指标减半了,那么我们也可以把时间减半,是不是实现了:也就是说,按照现在5w的装备,那么你需要在10s之内入库。不说了,按照你的逻辑,如果我们只有500个监控指标,那不是0.1秒就得存吗?难道不考虑一下那些被监控设备的感受吗?但是,如果有人想和你一起玩,你能做什么?接受提议。结果时间缩到10秒后,问题来了。仔细分析上面的逻辑可以知道,分表是按照collector划分的。现在收藏者的数量减少了,但数量增加了。发生了什么?可以支持写作。但是每张表的记录接近400w,有些采集设备的监控指标很多,接近600w,怎么破?于是相关技术人员开会商讨相关措施。如何在没有索引的情况下优化查询?有同事指出,where子句的顺序会影响查询结果,因为可以滑动选择一部分数据,然后继续按下一个条件筛选。听起来很有道理,但SQLServer查询分析器不会自动优化吗?原谅我是新手,只是感觉而已。感觉应该和VS编译器一样,应该是自动优化的。具体还是要用事实说话:同事修改客户端后,测试反馈有很大的提升。我看了看代码:真的有这么大的影响吗?等等,你是不是忘了清除缓存,造成了假象?于是让同事执行下面的语句获取更多信息:--优化前,DBCCFREEPROCCACHEDBCCDROPCLEANBUFFERSSETSTATISTICSIOONselectDtime,Valuefromdbo.his20140825WHEREDtime>=''ANDDtime<=''ANDMgrObjId=''ANDId=''SETSTATISTICSIOFF--优化后,DBCCFREEPROCCACHEDBBCCDROPCLEONBUFFValuefromdbo.his20140825WHEREMgrObjId=''ANDId=''ANDDtime>=''ANDDtime<=''SETSTATISTICSIOFF结果如下:是不是优化前比较好?仔细查看IO数据,发现预读是一样的,也就是说我们要查询的数据记录都是一致的,物理读和表扫描也是一致的。逻辑读取略有不同,应该是缓存条目数导致的。也就是说,在没有索引的情况下,where子句的条件顺序对查询结果的优化没有明显的影响。那么,唯一的办法就是通过索引。建立索引的尝试建立索引不是一件简单的事情,需要一些基础知识。过程中走了很多弯路,终于把索引建立起来了。下面的实验是基于对以下总记录数的验证:单字段索引的思路主要是受我建立的数据结构的影响。我记忆中的数据结构是:Dictionary。我以为应该先建立MgrObjId的索引,再建立Id的索引。SQLServer查询时,速度会更快。