哪个数据库可以处理数十亿/万亿条记录的存储?

som*_*ike 78 nosql rdbms mongodb sql-server cassandra

我们正在研究开发一种工具来捕获和分析我们收集的大量网络流量数据。每天我们捕获大约 14 亿条流记录,它们的 json 格式如下所示:

{
   "tcp_flags": "0",
   "src_as": "54321",
   "nexthop": "1.2.3.4",
   "unix_secs": "1352234521",
   "src_mask": "23",
   "tos": "0",
   "prot": "6",
   "input": "105",
   "doctets": "186",
   "engine_type": "0",
   "exaddr": "2.3.4.5",
   "engine_id": "2",
   "srcaddr": "9.8.7.6",
   "dst_as": "12345",
   "unix_nsecs": "752265174",
   "sysuptime": "2943529544",
   "dst_mask": "24",
   "dstport": "80",
   "last": "2943523241",
   "srcport": "52672",
   "dpkts": "4",
   "output": "111",
   "dstaddr": "6.5.4.3",
   "first": "2943517993"
}
Run Code Online (Sandbox Code Playgroud)

我们希望能够对数据集进行快速搜索(少于 10 秒),最有可能在很短的时间内(10 - 30 分钟间隔)。我们还希望索引大部分数据点,以便我们可以快速搜索每个数据点。我们还希望在执行搜索时拥有最新的数据视图。留在开源世界会很棒,但我们不反对为这个项目寻找专有解决方案。

这个想法是保留大约一个月的数据,这将是大约 432 亿条记录。粗略估计,每条记录将包含大约 480 字节的数据,相当于一个月内约 18.7 TB 的数据,可能是索引的三倍。最终,我们希望增加该系统存储数万亿条记录的能力。

我们已经(非常基本地)评估了 couchbase、cassandra 和 mongodb 作为这个项目的可能候选者,但是每个人都提出了自己的挑战。使用 couchbase,索引是每隔一段时间完成的,而不是在插入数据期间完成,因此视图不是最新的,cassandra 的二级索引在返回结果方面效率不高,因为它们通常需要扫描整个集群以获取结果,而 mongodb 看起来很有希望但是由于它是主/从/分片,因此扩展似乎要困难得多。我们计划评估的其他一些候选者是 elasticsearch、mysql(不确定这是否适用)和一些面向列的关系数据库。任何建议或现实世界的经验将不胜感激。

ole*_*sii 61

在我工作的一家公司,我们正在处理类似数量的数据(大约 10 TB 的实时可搜索数据)。我们使用 Cassandra 解决了这个问题,我想提几个想法,它们可以让您在多 TB 数据库上进行 O(1) 搜索。不过,这并非特定于 Cassandra db,您也可以将它与其他 db 一起使用。

理论

  • 分片您的数据。单个服务器无法可靠且实际地保存如此大量的数据。
  • 为硬件故障和整个节点故障做好准备,复制数据。
  • 从一开始就开始使用许多后端服务器。
  • 与高端高性能服务器相比,使用许多更便宜的商品服务器。
  • 确保数据均匀分布在分片上。
  • 花大量时间计划您的查询。从查询中派生 API,然后仔细设计表。这是最重要和最持久的任务。
  • 在 Cassandra 中,您可以设计一个复合列键并在 O(1) 中访问该键。花时间研究它们。这将用于访问可搜索记录而不是二级索引。
  • 利用宽行。它们可用于存储带时间戳的事件。
  • 切勿在此类卷上执行全扫描或实际上任何超过 O(Log N) 的操作。如果您需要的不仅仅是 O(Log N),请将此类操作卸载到 Map-Reduce 算法。

实践

  • 不要花时间在物理机器上构建操作系统映像或安装服务器。使用基于云的提供商进行快速原型设计。我与 Amazon EC2 一起工作,并强烈推荐它,因为它的原型设计的简单性、可靠性和速度。
  • Windows 机器在启动时往往会变慢,并且在空闲状态下会占用更多的资源。考虑使用基于 Unix 的操作系统。就我个人而言,我发现 Ubuntu 服务器是一个可靠的操作系统,而且askubuntu有一个非常好的社区
  • 考虑网络,理想情况下节点应该彼此靠近,以允许快速闲聊和元数据交换。
  • 不要进入极端情况:非常宽的列行或特别长的列族(表)。在理智的边界内实现最佳性能 - 如果 db通过设计支持那么多N行,这并不意味着它表现良好。
  • 我们的搜索大约需要 3-5 秒,很大程度上是由于 UI 和数据库之间的中间节点。考虑如何使请求更接近数据库。
  • 使用网络负载平衡器。选择一个既定的。我们使用 HAProxy,它很简单,但速度很快。从来没有问题。
  • 喜欢简单而不是复杂的解决方案。
  • 寻找免费的开源解决方案,除非您有公司规模预算的支持。一旦您使用多台服务器,基础设施的成本可能会飙升。

我不为亚马逊工作,与 HAProxy 和 Ubuntu 团队没有关系。这是个人意见,而不是任何形式的宣传。

  • @oleksii 十亿美元的谷歌预算并不是一个合理的比较。 (9认同)
  • 除了极其琐碎/无用的情况外,我很确定 O(1) 搜索是不可能的。 (5认同)
  • 我可以将前面的 3 条评论与`O(1) search <=> 无限存储空间 <=> 无限现金供应` 联系起来 (4认同)
  • 请不要冒犯,但请告诉 Google。在精心设计下,可以在 PB 规模上进行 O(1) 搜索。 (3认同)
  • 可以使用 [线性哈希表] (http://en.wikipedia.org/wiki/Linear_hashing) 完成对单个记录的 O(1) 搜索。但是,这不会给您按顺序搜索(范围)的任何效率。为此,您需要 BTree 结构的一些变体,对于单个项目,它是 O(log n)。 (3认同)

Han*_*non 43

如果我打算把它放到 SQL Server 中,我会建议一个类似的表:

CREATE TABLE tcp_traffic
(
    tcp_traffic_id bigint constraint PK_tcp_traffic primary key clustered IDENTITY(1,1)
    , tcp_flags smallint    /* at most 9 bits in TCP, so use SMALLINT */
    , src_as int        /* Since there are less than 2 billion A.S.'s possible, use INT */
    , netxhop bigint    /* use a big integer for the IP address instead of storing
                             it as dotted-decimal */
    , unix_secs bigint  
    , src_mask int      /* an assumption */
    , tos tinyint       /* values are 0-255, see RFC 791 */
    , prot tinyint      /* values are 0-255, see RFC 790 */
    , input int         /* an assumption */
    , doctets int       /* an assumption */
    , engine_type int   /* an assumption */
    , exaddr bigint     /* use a big integer for the IP address instead of storing
                             it as dotted-decimal */
    , engine_id int     /* an assumption */
    , srcaddr bigint    /* use a big integer for the IP address instead of storing
                             it as dotted-decimal */
    , dst_as int        /* Since there are less than 2 billion A.S.'s possible, use INT */
    , unix_nsecs bigint /* an assumption */
    , sysuptime bigint  /* an assumption */
    , dst_mask int      /* an assumption */
    , dstport smallint  /* ports can be in the range of 0 - 32767 */
    , [last] bigint     /* an assumption */
    , srcport smallint  /* ports can be in the range of 0 - 32767 */
    , dpkts int         /* an assumption */
    , output int        /* an assumption */
    , dstaddr bigint    /* use a big integer for the IP address instead of storing
                            it as dotted-decimal */
    , [first] bigint    /* an assumption */
);
Run Code Online (Sandbox Code Playgroud)

这导致单个表的总估计存储需求,对于 43.2 beeellion 记录(您指定的需求)没有 5.5 TB 的进一步索引。这计算为数据本身的 130 个字节,加上每行开销 7 个字节,加上每页开销 96 个字节。SQL Server 将数据存储在 8KB 页中,允许每页 59 行。这相当于一个月的数据有 732,203,390 页。

SQL Server 喜欢以 8 页块 (64KB) 的形式写入磁盘,这相当于每个物理 I/O 472 行。由于每秒生成 16,203 条流记录,因此您需要保证每秒 34 次 IOps 的最低 I/O 速率。虽然这本身并不是一个巨大的数量,但系统中的其他 I/O(SQL Server 和其他)需要永远不会侵犯这个必要的 IOps 速率。因此,您需要设计一个至少能够提供一个数量级的 IOps 或 340 个持续 IOps 的系统——我倾向于估计您需要 2 个数量级的更可持续的 IOps 来保证吞吐量。

您会注意到我没有以点分十进制形式存储 IP 地址。这节省了大量存储空间(每个地址 7 个字节),并且还使索引、检索、排序和比较 IP 地址的效率大大提高。这里的缺点是您需要在存储之前将点分十进制 IP 转换为 8 字节整数,然后再转换回点分十进制 IP 以进行显示。执行此操作的代码很简单,但是您的行速率会为正在处理的每个流行增加大量处理开销 - 您可能希望在物理上与 SQL Server 不同的计算机上执行此转换过程。

讨论您需要的索引是完全不同的事情,因为您没有列出任何具体要求。该表的设计将按照 SQL Server 接收到的物理顺序存储流行,该tcp_traffic_id字段对于每条记录都是唯一的,并允许按记录的顺序对行进行排序(在这种情况下很可能是一对一相关的)到流事件的时间)。

  • @MrTelly 我不同意。仅当您需要 HA 或大型故障转移时,在 SQL Server 中执行此操作的成本很高。对于一个非常容易使用的可靠数据存储,SQL Server 非常适合这一点。如果需要 HA,所有系统都会变得非常昂贵(且复杂)。 (7认同)
  • 我可能会分别使用 `binary(4)` 或 `binary(16)`。4 字节/行乘以 1,000,000,000,000 时会增加很多存储空间。 (4认同)
  • @MrTelly 有两项费用:a) 磁盘存储(5-8 tb,取决于索引使用的空间)b) RAM(支持查询、索引缓存)。要以整体方式执行此操作,通常需要使用大型 RAID10 阵列或 SAN。但是,请注意,当然可以进行分片,并且可以让您使用应用程序级逻辑将工作负载分片到多个 SQL Server 上。这可以让您使用便宜的服务器,每个 0.5-2tb,甚至可能使用免费的 SQL Server 版本。(请注意,分片是一个通用概念,通常在应用程序级别完成,适用于任何持久化方法) (3认同)
  • 并且端口号的范围是 0-65535,因此您可以使用“SMALLINT”,但那里也必须有一个转换例程。 (2认同)
  • IMO,SQL Server 绝对可以*存储* 数据;我仍然不确定它是否是解决项目*分析*部分的正确解决方案,主要是因为我对正在考虑的其他系统不够熟悉。 (2认同)

Sum*_*man 5

我会推荐HBase。您可以将所有原始数据存储在一个或多个 HBase 表中,具体取决于您需要查询的内容。HBase 可以处理大型数据集并通过区域拆分进行自动分片。

此外,如果你设计好行键,你可以获得极快的速度,甚至 O(1) 查询。请注意,如果您正在检索大型数据集,那仍然会很慢,因为检索数据是一个 O(n) 操作。

由于您想跨每个字段进行查询,我建议为每个字段创建一个唯一的表。src_address 数据的示例,有一个如下所示的表:

1.2.3.4_timestamp1 : { data }
1.2.3.4_timestamp2 : { data }
Run Code Online (Sandbox Code Playgroud)

因此,如果您想查询从 3 月 27 日凌晨 12:00 到 3 月 27 日凌晨 12:01 之间 1.2.3.4 的所有数据,您可以使用指定的开始和停止行进行范围扫描。

恕我直言,行键设计是使用 HBase 最关键的部分——如果你设计得好,你将能够进行快速查询并存储大量数据。