热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

学习在SQLServer中处理千万单位记录

sql教程栏目介绍如何处理千万单位的记录。

sql教程栏目介绍如何处理千万单位的记录。

采用的是SQLServer2012标准版,HP提供的正版软件,缺少很多企业版的NB功能。

推荐一下自己的linuxC/C++交流群:973961276!整理了一些个人觉得比较好的学习书籍、视频资料以及大厂面经视频共享在群文件里面,有需要的小伙伴可以自行添加哦!~

写入瓶颈

首先遇到的第一个拦路虎就是,我们发现现有的程序下,SQLServer根本处理不了这么多的数据量,具体情况是怎样的呢?

我们的存储结构

一般为了存储大量的历史数据,我们都会进行一个物理的分表,否则每天上百万条的记录,一年下来就是几亿条。因此,原来我们的表结构是这样的:

CREATE TABLE [dbo].[His20140822](
    [No] [bigint] IDENTITY(1,1) NOT NULL,
    [Dtime] [datetime] NOT NULL,
    [MgrObjId] [varchar](36) NOT NULL,
    [Id] [varchar](50) NOT NULL,
    [Value] [varchar](50) NOT NULL,
 CONSTRAINT [PK_His20140822] PRIMARY KEY CLUSTERED (
    [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,没错,就是它,号称写入百万条记录都是秒级的

    public static int BatchInert(string connectionString, string desTable, DataTable dt, int batchSize = 500)
    {
        using (var sbc = new SqlBulkCopy(connectionString, SqlBulkCopyOptions.UseInternalTransaction)
        {
            BulkCopyTimeout = 300,
            NotifyAfter = dt.Rows.Count,
            BatchSize = batchSize,
            DestinatiOnTableName= desTable        })
        {
            foreach (DataColumn column in dt.Columns)
                sbc.ColumnMappings.Add(column.ColumnName, column.ColumnName);
            sbc.WriteToServer(dt);
        }

        return dt.Rows.Count;
    }

存在什么问题?

上面的架构,在每天4千万的数据都是OK的。但是,调整为上述背景下的配置时,集中监控程序就内存溢出了,分析得知,接收的太多数据,放在了内存中,但是没有来得及写入到数据库中,最终导致了生成的数据大于消费的数据,导致内存溢出,程序无法工作。

瓶颈到底在哪里?

是因为RAID磁盘的问题?是数据结构的问题?是硬件的问题?是SQLServer版本的问题?是没有分区表的问题?还是程序的问题?

当时时间只有一个星期,一个星期搞不好,项目监管就要我们滚蛋了,于是,有了连续工作48小时的壮举,有了到处打电话求人的抓鸡……

但是,这个时候需要的是冷静,再冷静……SQLServer版本?硬件?目前都不大可能换的。RAID磁盘阵列,应该不是。那么到底是什么,真TM的冷静不下来。

大家可能体会不到现场那种紧张的气氛,其实过了这么久,我自己也都很难再回到那种情境。但是可以这么说,或许我们现在有了各种方法,或者处于局外人我们有更多思考,但是当一个项目压迫你快到放弃的时候,你那时的想法、考虑在现场环境因素的制约下,都可能出现重大的偏差。有可能让你快速的思考,也有可能思维停滞。有些同事在这种高压的环境下,甚至出现了更多的低级错误,思维已经完全乱了,效率更低了……36小时没有合眼,或者只在工地上(下雨天到处都是泥巴,干了的话到时都是泥灰)眯两三个小时,然后继续干,连续这么一个星期!或者还要继续!

很多人给了很多想法,但是好像有用,又好像没用。等等,为什么是“好像有用,又好像没用”?我隐隐约约中,好像抓住了一丝方向,到底是什么?对了,验证,我们现在是跑在现场环境下,之前没有问题,不代表现在的压力下没有问题,要在一个大型系统中分析这么个小功能,影响太大了,我们应该分解它。是的,是“单元测试”,就是单个方法的测试,我们需要验证每个函数,每个独立的步骤到底耗时在哪里?

逐步测试验证系统瓶颈

修改BulkCopy的参数
首先,我想到的是,修噶BulkCopy的各项参数,BulkCopyTimeoutBatchSize,不断的测试调整,结果总是在某个范围波动,实际并没有影响。或许会影响一些CPU计数,但是远远没有达到我的期望,写入的速度还是在5秒1w~2w波动,远远达不到要求20秒内要写20w的记录。

按采集设备存储
是的,上述结构按每个指标每个值为一条记录,是不是太多的浪费?那么按采集设备+采集时间作为一条记录是否可行?问题是,怎么解决不同采集设备属性不一样的问题?这时,一个同事发挥才能了,监控指标+监控值可以按XML格式存储。哇,还能这样?查询呢,可以用for XML这种形式。

于是有了这种结构:No、MgrObjId、Dtime、XMLData

结果验证,比上面的稍微好点,但是不是太明显。

数据表分区???
那个时候还没有学会这个技能,看了下网上的文章,好像挺复杂的,时间不多了,不敢尝试。

停止其他程序
我知道这个肯定是不行的,因为软件、硬件的架构暂时没法修改。但是我希望验证是不是这些因素影响的。结果发现,提示确实明显,但是还是没有达到要求。

难道是SQLServer的瓶颈?
没辙了,难道这就是SQLServer的瓶颈?上网查了下相关的资料,可能是IO的瓶颈,尼玛,还能怎么办,要升级服务器,要更换数据库了吗,但是,项目方给吗?

等等,好像还有个东西,索引,对索引!索引的存在会影响插入、更新

去掉索引

是的,去掉索引之后查询肯定慢,但是我必须先验证去掉索引是否会加快写入。如果果断把MgrObjId和Id两个字段的索引去掉。

运行,奇迹出现了,每次写入10w条记录,在7~9秒内完全可以写入,这样就达到了系统的要求。

查询怎么解决?

一个表一天要4亿多的记录,这是不可能查询的,在没有索引的情况下。怎么办!?我又想到了我们的老办法,物理分表。是的,原来我们按天分表,那么我们现在按小时分表。那么24个表,每个表只需存储1800w条记录左右。

然后查询,一个属性在一个小时或者几个小时的历史记录。结果是:慢!慢!!慢!!!去掉索引的情况下查询1000多万的记录根本是不可想象的。还能怎么办?

继续分表,我想到了,我们还可以按底层的采集器继续分表,因为采集设备在不同的采集器中是不同的,那么我们查询历史曲线时,只有查单个指标的历史曲线,那么这样就可以分散在不同的表中了。

说干就干,结果,通过按10个采集嵌入式并按24小时分表,每天生成240张表(历史表名类似这样:His_001_2014112615),终于把一天写入4亿多条记录并支持简单的查询这个问题给解决掉了!!!

查询优化

在上述问题解决之后,这个项目的难点已经解决了一半,项目监管也不好意思过来找茬,不知道是出于什么样的战术安排吧。

过了很长一段时间,到现在快年底了,问题又来了,就是要拖死你让你在年底不能验收其他项目。

这次要求是这样的:因为上述是模拟10w个监控指标,而现在实际上线了,却只有5w个左右的设备。那么这个明显是不能达到标书要求的,不能验收。那么怎么办呢?这些聪明的人就想,既然监控指标减半,那么我们把时间也减半,不就达到了吗:就是说按现在5w的设备,那你要10s之内入库存储。我勒个去啊,按你这个逻辑,我们如果只有500个监控指标,岂不是要在0.1秒内入库?你不考虑下那些受监控设备的感想吗?

但是别人要玩你,你能怎么办?接招呗。结果把时间降到10秒之后,问题来了,大家仔细分析上面逻辑可以知道,分表是按采集器分的,现在采集器减少,但是数量增加了,发生什么事情呢,写入可以支持,但是,每张表的记录接近了400w,有些采集设备监控指标多的,要接近600w,怎么破?

于是技术相关人员开会讨论相关的举措。

在不加索引的情况下怎么优化查询?

有同事提出了,where子句的顺序,会影响查询的结果,因为按你刷选之后的结果再处理,可以先刷选出一部分数据,然后继续进行下一个条件的过滤。听起来好像很有道理,但是SQLServer查询分析器不会自动优化吗?原谅我是个小白,我也是感觉而已,感觉应该跟VS的编译器一样,应该会自动优化吧。

具体怎样,还是要用事实来说话:

结果同事修改了客户端之后,测试反馈,有较大的改善。我查看了代码:

可以看到,这里完全使用了索引,没有额外的消耗。而实际执行的结果,1秒都不到,竟然不用一秒就在1100w的记录中把结果筛选了出来!!帅呆了!!

怎么应用索引?

既然写入完成了、读取完成了,怎么结合呢?我们可以把一个小时之前的数据建立索引,当前一个小时的数据就不建立索引。也就是,不要再创建表的时候建立索引!!

还能怎么优化

可以尝试读写分离,写两个库,一个是实时库,一个是只读库。一个小时内的数据查询实时库,一个小时之前的数据查询只读库;只读库定时存储,然后建立索引;超过一个星期的数据,进行分析处理再存储。这样,无论查询什么时间段的数据,都能够正确处理了——一个小时之内的查询实时库,一个小时到一个星期内的查询只读库,一个星期之前的查询报表库。

如果不需要物理分表,则在只读库中,定时重建索引即可。

总结

如何在SQLServer中处理亿万级别的数据(历史数据),可以按以下方面进行:

  • 去掉表的所有索引
  • 用SqlBulkCopy进行插入
  • 分表或者分区,减少每个表的数据总量
  • 在某个表完全写完之后再建立索引
  • 正确的指定索引字段
  • 把需要用到的字段放到包含索引中(在返回的索引中就包含了一切)
  • 查询的时候只返回所需的字段

以上就是学习在SQLServer中处理千万单位记录的详细内容,更多请关注其它相关文章!


推荐阅读
  • 本文探讨了Web开发与游戏开发之间的主要区别,旨在帮助开发者更好地理解两种开发领域的特性和需求。文章基于作者的实际经验和网络资料整理而成。 ... [详细]
  • 在Linux系统上构建Web服务器的详细步骤
    本文详细介绍了如何在Linux系统上搭建Web服务器的过程,包括安装Apache、PHP和MySQL等关键组件,以及遇到的一些常见问题及其解决方案。 ... [详细]
  • 本文介绍如何配置SecureCRT以正确显示Linux终端的颜色,并解决中文显示问题。通过简单的步骤设置,可以显著提升使用体验。 ... [详细]
  • 如何从python读取sql[mysql基础教程]
    从python读取sql的方法:1、利用python内置的open函数读入sql文件;2、利用第三方库pymysql中的connect函数连接mysql服务器;3、利用第三方库pa ... [详细]
  • 俗话说得好,“工欲善其事,必先利其器”。这句话不仅强调了工具的重要性,也提醒我们在任何项目开始前,准备合适的工具至关重要。本文将介绍几款C语言编程中常用的工具,帮助初学者更好地选择适合自己学习和工作的编程环境。 ... [详细]
  • MySQL 'Too Many Connections' 错误处理及优化方案
    本文详细介绍了如何诊断和解决MySQL数据库中出现的‘Too Many Connections’错误,包括查看当前连接状态、调整配置文件以及优化应用代码等方法。 ... [详细]
  • 本文将详细介绍如何安装和使用 CactiEZ 的中文版本,帮助那些对英文界面不太熟悉的用户轻松掌握这一强大的网络监控工具。 ... [详细]
  • 请看|间隔时间_Postgresql 主从复制 ... [详细]
  • 本文详细探讨了在服务器上运行的PostgreSQL数据库出现'内存不足'错误的具体情况,并提供了一系列有效的解决策略。通过本文,读者将能够更好地理解这一常见问题及其背后的原理。 ... [详细]
  • 本文详细介绍了JSP(Java Server Pages)的九大内置对象及其功能,探讨了JSP与Servlet之间的关系及差异,并提供了实际编码示例。此外,还讨论了网页开发中常见的编码转换问题以及JSP的两种页面跳转方式。 ... [详细]
  • RedHat 系统下配置国内 YUM 源以替代官方收费源的方法
    本文详细介绍如何在 RedHat Linux 中安装并配置 YUM 包管理器,并通过使用国内镜像源来解决因未购买官方服务而导致的更新源限制问题。 ... [详细]
  • Eclipse 中 JSP 开发环境配置指南
    本文详细介绍了如何在 Eclipse 集成开发环境中配置 JSP 运行环境,包括必要的软件下载、Tomcat 服务器的配置以及常见问题的解决方法。 ... [详细]
  • NFS(Network File System)即网络文件系统,是一种分布式文件系统协议,主要用于Unix和类Unix系统之间的文件共享。本文详细介绍NFS的配置文件/etc/exports和相关服务配置,帮助读者理解如何在Linux环境中配置NFS客户端。 ... [详细]
  • 理解文档对象模型(DOM)
    本文介绍了文档对象模型(DOM)的基本概念,包括其作为HTML文档的节点树结构,以及如何通过JavaScript操作DOM来实现网页的动态交互。 ... [详细]
  • 三菱PLC SLMP协议报文详解
    本文详细解析了三菱PLC中使用的SLMP协议报文结构,包括其工作原理、通信流程及报文格式,旨在帮助工程师和技术人员更好地理解和运用这一协议。 ... [详细]
author-avatar
UP7家族--婵婵_172
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有