【问题标题】:Recommendations for database structure with huge dataset大数据集的数据库结构建议
【发布时间】:2010-12-22 09:45:14
【问题描述】:

在我看来这个问题没有准确的答案,因为需要过于复杂的分析和深入研究我们系统的细节。

我们有分布式传感器网络。信息收集在一个数据库中并进一步处理。

当前的数据库设计是每月对一个巨大的表进行分区。我们尝试将其保持在 10 亿条记录(通常是 600-8 亿条记录),因此填充率是每天 20-5000 万条记录。

数据库服务器目前是 MS SQL 2008 R2,但我们从 2005 年开始并在项目开发期间升级。

表本身包含 SensorId、MessageTypeId、ReceiveDate 和 Data 字段。当前的解决方案是将传感器数据保留在 Data 字段(二进制,16 字节固定长度)中,并部分解码其类型并将其存储在 messageTypeId 中。

我们有不同类型的传感器发送的消息类型(当前大约 200 个),并且可以根据需要进一步增加。

主要处理在应用服务器上完成,它按需获取记录(按类型、sensorId 和日期范围),对其进行解码并执行所需的处理。目前的速度对于这样的数据量来说已经足够了。

我们要求将我们系统的容量增加 10-20 倍,我们担心我们目前的解决方案能够做到这一点。

我们还有两个“优化”结构的想法,我想讨论一下。

1 Sensor的数据可以分为几种类型,为简单起见,我将使用2种主要的:(值)级别数据(具有值范围的模拟数据),状态数据(固定数量的值)

所以我们可以使用以下规则将我们的桌子重新设计成一堆小桌子:

  • 对于每个固定类型值(状态类型),使用 SensorId 和 ReceiveDate 创建它自己的表(因此我们避免存储类型和二进制 blob),所有依赖(扩展)状态都将存储在自己的表中,类似于外键,因此,如果我们有 State 的值 AB,以及它的依赖(或附加)状态 12,我们以表 StateA_1StateA_2StateB_1、@ 结尾987654329@。所以表名由它所代表的固定状态组成。

  • 对于我们创建单独表的每个模拟数据,它与第一种类型相似,但包含带有传感器值的附加字段;

优点:

  • 仅存储所需的数据量(目前我们的二进制 blob 数据包含最长值的空间)并减少了数据库大小;
  • 为了获取特定类型的数据,我们获取访问权限表,而不是按类型过滤;

缺点:

  • AFAIK,它违反了推荐的做法;
  • 需要框架开发来自动化表管理,因为手动维护它是 DBA 的地狱;
  • 表的数量可能相当大,因为需要完全覆盖可能的值;
  • 在引入新传感器数据或什至已定义状态的新状态值时,数据库架构会发生更改,因此可能需要复杂的更改;
  • 管理复杂容易出错;
  • 在这种表组织中插入值可能是 DB 引擎地狱?
  • 数据库结构不固定(不断更改);

可能所有缺点都超过了一些专业人士,但如果我们获得显着的性能提升和/或(不太受欢迎但也很有价值)存储空间,也许我们会遵循这种方式。

2 可能只是为每个传感器拆分表(大约 100 000 个表)或更好地根据传感器范围和/或移动到具有专用服务器的不同数据库,但我们希望尽可能避免硬件跨度。

3 保持原样。

4 切换到不同类型的 DBMS,例如面向列的 DBMS(HBase 和类似的)。

你怎么看?也许您可以推荐资源以供进一步阅读?

更新: 来自传感器的某些数据即使延迟一个月(通常延迟 1-2 周)也能到达的系统性质,有些始终在线,有些传感器具有板载内存并最终上线。每个传感器消息都有相关的事件引发日期和服务器接收日期,因此我们可以区分最近的数据和前一段时间收集的数据。处理包括一些统计计算,参数偏差检测等。我们构建了聚合报告以便快速查看,但是当我们从传感器更新旧数据(已经处理)中获取数据时,我们必须从头开始重建一些报告,因为它们取决于所有可用的不能使用数据和聚合值。所以我们通常会保留 3 个月的数据以便快速访问和其他存档。我们努力减少存储数据的需求,但决定我们需要这一切来保持结果的准确性。

更新2:

此处的表格包含主要数据。正如我在 cmets 中提到的,我们在“需要速度”期间删除了所有依赖项和约束,因此它仅用于存储。

CREATE TABLE [Messages](
    [id] [bigint] IDENTITY(1,1) NOT NULL,
    [sourceId] [int] NOT NULL,
    [messageDate] [datetime] NOT NULL,
    [serverDate] [datetime] NOT NULL,
    [messageTypeId] [smallint] NOT NULL,
    [data] [binary](16) NOT NULL
)

来自其中一台服务器的样本数据:

id      sourceId    messageDate          serverDate messageTypeId   data
1591363304  54  2010-11-20 04:45:36.813 2010-11-20 04:45:39.813 257 0x00000000000000D2ED6F42DDA2F24100

1588602646  195 2010-11-19 10:07:21.247 2010-11-19 10:08:05.993 258 0x02C4ADFB080000CFD6AC00FBFBFBFB4D

1588607651  195 2010-11-19 10:09:43.150 2010-11-19 10:09:43.150 258 0x02E4AD1B280000CCD2A9001B1B1B1B77

【问题讨论】:

  • 嗨尼克。你只讲输入数据和存储思路。理想情况下,形式遵循功能,因此 100 万美元的问题是:您对数据做了什么?您将获得更好的建议来实现您关心的目标,而不是与您的最终目标无关的存储想法。
  • 感谢您的回复,沃宾爵士。如果用三个词来描述:我们会处理它:) 我更新我的问题

标签: sql-server performance database-design data-warehouse


【解决方案1】:

只是提出一些想法,希望它们有用 - 它们是我正在考虑/思考/研究的一些事情。

分区 - 你提到表是按月分区的。是您自己手动分区,还是使用企业版中提供的分区功能?如果是手动的,请考虑使用内置的分区功能对数据进行更多分区,从而提高可扩展性/性能。 Kimberly Tripp 在 MSDN 上的这篇“Partitioned Tables and Indexes”文章很棒 - 里面有很多很棒的信息,我不会通过解释来做不公正的事情!值得考虑这一点,而不是手动为每个传感器创建 1 个表,这可能更难以维护/实施,因此增加了复杂性(简单 = 好)。当然,前提是您拥有企业版。

过滤索引 - 查看this MSDN article

当然还有硬件元素 - 不用说,带有大量 RAM/快速磁盘等的大型服务器将发挥作用。

【讨论】:

  • 感谢您的回复。我们使用企业版中提供的分区功能。谢谢你的链接,我猜不出我们如何使用过滤索引:(我们已经建立了聚集索引来提高插入率,并且非聚集索引支持我们通常做的每种类型的选择,或者你建议而不是使用拆分表通过使用谓词从二进制数据中提取字段来组织过滤索引,从而使用 DBMS 引擎和适当的索引过滤请求。它可以以一堆索引结束...
【解决方案2】:

一种与数据库不太相关的技术是切换到记录值的变化——每分钟至少有 n 条记录左右。因此,例如,如果 1 号传感器发送类似以下内容:

Id  Date              Value
-----------------------------
1 2010-10-12 11:15:00 100
1 2010-10-12 11:15:02 100
1 2010-10-12 11:15:03 100
1 2010-10-12 11:15:04 105

那么只有第一条和最后一条记录会在数据库中结束。为确保传感器处于“实时”状态,每分钟至少输入 3 条记录。这样可以减少数据量。

不确定这是否有帮助,或者它在您的应用程序中是否可行——只是一个想法。

编辑

是否可以根据访问概率归档数据?说旧数据比新数据更不可能被访问是否正确?如果是这样,您可能想看看 Bill Inmon 的 DW 2.0 Architecture for The Next Generation of Data Warehousing,他在其中讨论了通过不同 DW 区域(交互式、集成、近邻)移动数据的模型。行,档案)基于访问的概率。访问时间从非常快(交互区)到非常慢(存档)不等。每个区域都有不同的硬件要求。目的是防止大量数据堵塞 DW。

【讨论】:

  • 好主意。减少存储量,但不知何故使记录处理加倍,因为每次获得新测量值时都必须读取最新测量值。
  • @iDevelop,是的,不过——取决于传感器的数量——最后的读数可能会缓存在应用程序级别。
  • 谢谢。我们已经在服务器端和客户端(传感器固件)上实现了这种压缩。通常传感器在状态变化时存储新值而不存储轮询值。一个例外是来自在线传感器的保持活动数据包,但它们没有存储在数据库中。
  • 谢谢,我会看看 Bill Inmon 的下一代数据仓库的 DW 2.0 架构
【解决方案3】:

存储方面你可能会没事的。 SQL Server 将处理它。

让我担心的是您的服务器将承受的负载。如果您不断收到交易,那么今天您每秒将有大约 400 笔交易。将其增加 20 倍,您将看到每秒约 8,000 笔交易。考虑到您正在报告相同的数据,这不是一个小数字...

顺便说一句,我是否理解正确,因为您在处理传感器数据时会丢弃它?所以你的总数据集将是一个“滚动”的 10 亿行?还是只是追加数据?

【讨论】:

  • 谢谢。是的,我们也担心。目前我们的实现是在内存中收集传入的数据并根据时间和/或接收到的数据量形成批量插入操作。这降低了 tps 要求。此外,我们实现了非常高效的内存(我们希望它至少类似于我在第一个选项中描述的结构)缓存,因此部分数据(有时是构建报告所需的所有数据)从缓存而不是数据库中获取。同样在内存缓存中存储了至少最近 2 周的数据和所有新值,因此我们尽量避免 DB 命中。因此,我们在启动和“报告”创建期间使用 DB 来预热缓存。
  • 我们也使用我们的缓存而不是数据库本身来构建所有需要的处理。我想也许这个设计不太好,我们必须更多地依赖 DBMS?尤其是在重负载下。
  • @nick,我打算沿着这条线提出一些建议(收集和批量),但更粗略,而且你似乎已经领先于我了 :) 如果你可以用你的内容更新你的问题一旦数据进入数据库,我们可能会想出更多的想法。现在,我要退出了,因为我对 SQL Server 的经验不足,无法提出其他建议。
  • @Ronnis,感谢您的建议并尽力提供帮助。我们在过去的不同场景中进行了尝试,但最终使用 DBMS 作为存储并提供附加服务(方便的备份、复制、容错等),能够足够快地选择日期范围内的数据(使用适当的索引)。我们尝试使用 DBMS 函数进行一些预处理,但我们所有的尝试都会导致无法接受的性能,因此我们将所有处理都交给了应用服务器。
  • @nick,嗯。您介意共享此表的实际 DDL 以及您存储的二进制数据的示例吗?
【解决方案4】:

您可以将日期时间戳存储为整数。我相信日期时间戳在 SQL 中使用 8 个字节,而整数只使用 4 个字节。您必须离开年份,但由于您按月进行分区,所以这可能不是问题。

所以 '12/25/2010 23:22:59' 将存储为 1225232259 -MMDDHHMMSS

只是一个想法......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-21
    • 1970-01-01
    • 2012-01-06
    • 1970-01-01
    • 2016-05-21
    • 2016-12-16
    • 1970-01-01
    相关资源
    最近更新 更多