MySQL可以合理地对数十亿行执行查询吗?


283

我正计划将来自质谱仪的扫描结果存储在MySQL数据库中,并想知道是否可以远程存储和分析这一数量的数据。我知道性能会因环境而异,但是我正在寻找一个大致的数量级:查询需要5天还是5毫秒?

输入格式

每个输入文件都包含一个光谱仪。每次运行都由一组扫描组成,并且每次扫描都有一个有序的数据点数组。有一些元数据,但是文件的大部分由32位或64位int或float数组组成。

主机系统

| ---------------- + ------------------------------- |
| 操作系统| Windows 2008 64位|
| MySQL版本| 5.5.24(x86_64)|
| CPU | 2个Xeon E5420(共8核)|
| 内存 8GB |
| SSD文件系统| 500 GiB |
| 硬盘RAID | 12 TiB |
| ---------------- + ------------------------------- |

使用可忽略的处理器时间在服务器上运行其他一些服务。

文件统计

| ------------------ + -------------- |
| 文件数| 〜16,000 |
| 总尺寸| 1.3钛|
| 最小尺寸| 0字节|
| 最大尺寸| 12 GiB |
| 意思 800 MiB |
| 中位数 500 MiB |
| 总数据点| 约2000亿|
| ------------------ + -------------- |

数据点的总数是非常粗略的估计。

拟议方案

我打算做“正确的”事情(例如,疯狂地规范化数据),因此将有一个runs表,一个spectra带有外键runsdatapoints表和一个带有外键的表spectra

2000亿数据点问题

我将跨多个光谱甚至可能多次运行进行分析,导致查询可能涉及数百万行。假设我对所有内容进行了正确索引(这是另一个问题的主题),并且没有试图在网络上随机播放数百个MiB,那么MySQL处理此问题在远程看来是否合理?

附加信息

扫描数据将来自基于XML的 mzML格式的文件。这种格式的<binaryDataArrayList>内容在存储数据的 元素中。每次扫描都会产生> = 2个<binaryDataArray>元素,这些元素加在一起形成形式为的二维(或更多)数组[[123.456, 234.567, ...], ...]

这些数据是一次写入的,因此不必担心更新性能和事务安全性。

我对数据库架构的简单计划是:

runs

| 列名| 类型
| ------------- + ------------- ||
| id | 主键|
| start_time | 时间戳|
| 名称| VARCHAR |
| ------------- + ------------- ||

spectra

| 列名| 类型
| ---------------- + ------------- |
| id | 主键|
| 名称| VARCHAR |
| 索引| INT |
| spectrum_type | INT |
| 代表性| INT |
| run_id | 外键|
| ---------------- + ------------- |

datapoints

| 列名| 类型
| ------------- + ------------- ||
| id | 主键|
| spectrum_id | 外键|
| mz | 双|
| num_counts | 双|
| 索引| INT |
| ------------- + ------------- ||

这合理吗?


因此,您可能已经推断出,我是程序员,而不是实验室的生物学家,所以我对科学的了解不及实际科学家。

这是我将要处理的数据类型的单个频谱图(扫描图):

查看器屏幕截图

该软件的目的是弄清楚峰的位置和重要性。我们现在使用专有的软件包来解决这个问题,但是我们想编写自己的分析程序(用R语言编写),因此我们知道表中到底发生了什么。如您所见,绝大多数数据都不有趣,但是我们不想丢掉算法遗漏的潜在有用数据。一旦获得了我们满意的可能峰列表,其余管道将使用该峰列表,而不是数据点的原始列表。我认为将原始数据点存储为一个大斑点就足够了,因此可以根据需要对其进行重新分析,但仅将峰保留为不同的数据库条目。在那种情况下,每个频谱只有几个峰值,所以疯狂的缩放比例不应该



8
由于这是原始的A / D轮询质谱仪数据,因此将其存储在数据库中似乎很愚蠢。我将获取原始数据,将其转储,处理并将处理后的结果存储在数据库中。结果将是(a)每行存储一个波形的波形,(b)与这些波形相关的其他数据(如校准曲线),以及(c)数据库中的结果行。这将减少设计中的数十亿行膨胀。当您想重新运行初始分析时,您将有效地编辑一些参数,运行巨大的计算操作,并将新结果存储在数据库中。
沃伦·P

Answers:


115

我对您的需求不是很熟悉,但是将每个数据点存储在数据库中可能有点过大。听起来几乎就像是通过将每个像素作为单独的记录存储在关系数据库中来采用存储图像库的方法。

通常,在大多数情况下将二进制数据存储在数据库中是错误的。通常有更好的方法来解决问题。尽管将二进制数据存储在关系数据库中并不是天生的错误,但通常弊端大于收益。顾名思义,关系数据库最适合存储关系数据。二进制数据不是关系数据。它增加了数据库的大小(通常很大),可能损害性能,并可能引起有关维护十亿记录MySQL实例的问题。好消息是,有些数据库特别适合存储二进制数据。其中一个虽然并不总是很明显,但却是文件系统!只需为二进制文件提供目录和文件命名结构,

另一种方法是将基于文档的存储系统用于您的数据点(可能是光谱)数据,并使用MySQL进行运行(或将运行与其他数据库放置在同一数据库中)。


5
为什么将二进制数据存储在数据库中被认为是错误的?(部分原因是因为我很好奇,也因为我能想到用例。)

15
如果二进制数据没有单独的值,则不应将其存储为唯一行。图像上的像素500x325无关紧要。

1
这是非常好的一点。我们可能应该保留原始文件,以防日后需要再次拉出东西时使用,但类似于存储图像是一个很好的选择。我们将永远不需要访问每个数据点(除非我们要重新进行峰提取),因此简单地存储提取的统计信息会更好。
haxney 2012年

107

我曾经使用过非常大的(Terabyte +)MySQL数据库。我们拥有的最大表实际上超过十亿行。该版本使用的是MySQL 5.0,因此可能情况有所改善。

有效。MySQL在大多数情况下都能正确处理数据。但是,这非常笨拙。(如果您希望具有TB级数据的六个sigma级可用性,请不要使用MySQL。我们是一家没有DBA和资金有限的初创公司。)

仅备份和存储数据是一个挑战。如果需要的话,恢复表将需要几天的时间。

我们有10-100百万行范围内的许多表。对表的任何重要连接都非常耗时,并且要花很长时间。因此,我们编写了存储过程来“遍历”表,并针对“ id”的范围进行过程联接。这样,我们可以一次处理10-100,000行的数据(加入id的1-100,000,然后加入100,001-200,000,依此类推)。这比加入整个表的速度快得多。

在不基于主键的非常大的表上使用索引也要困难得多。Mysql 5.0将索引存储为两部分-它存储索引(主索引除外)作为主键值的索引。因此,索引查找分为两个部分:首先,MySQL进入索引并从中获取需要查找的主键值,然后对主键索引进行第二次查找,以找到这些值在哪里。

这样做的结果是,对于非常大的表(1-200百万行),对表的索引的限制更为严格。您需要更少,更简单的索引。而且即使执行不直接在索引上的简单选择语句也可能永远不会回来。where子句必须命中索引或忘记索引。

但话虽这么说,事情确实有效。我们能够对这些非常大的表使用MySQL,并进行计算并获得正确的答案。

试图对2000亿行数据进行分析将需要非常高端的硬件以及大量的耐心和耐心。仅以可以还原的格式备份数据将是一项重要的工作。

我同意srini.venigalla的回答,认为像疯狂一样标准化数据可能不是一个好主意。在具有大量数据的多个表之间进行联接将使您面临文件排序的风险,这可能意味着您的某些查询将永远不会回来。使用简单的整数键进行非正规化将为您提供更大的成功机会。

我们拥有的一切都是InnoDB。关于MyISAM与InnoDB:主要是不要将两者混为一谈。由于MySQL缓存密钥和其他数据的方式,您不能真正为这两者优化服务器。如果可以,请为服务器中的所有表选择一个或另一个。MyISAM可能有助于解决某些速度问题,但可能无法解决需要完成的所有DBA工作-这可能是致命的。


1
自5.0以来,MySQL在索引(...)部门有了很大改进。看看它现在的表现会很有趣。
环Ø

70

疯狂地标准化数据

在这种情况下,像疯狂一样标准化数据可能不是正确的策略。通过以规范化形式和高度适合您的应用程序的物化视图形式存储数据,从而保持选择状态不变。这类应用程序的关键是不编写临时查询。查询建模比数据建模更重要。从目标查询开始,朝着最佳数据模型努力。

Is this reasonable?

我还将创建一个包含所有数据的附加平面表。

run_id | spectrum_id | data_id | <data table columns..> |

我将使用该表作为所有查询的主要来源。原因是避免进行任何连接。没有索引的联接将使您的系统非常无法使用,而在如此大的文件上建立索引同样会很糟糕。

策略是,首先在上表中查询,将结果转储到临时表中,并将该临时表与“运行”和“频谱”的查找表连接起来,以获得所需的数据。


您是否分析了写入需求与读取需求?放弃SQL并使用非标准数据存储机制将非常诱人。我认为,这应该是最后的手段。

为了加快写入速度,您可能需要尝试Handler Socket方法。如果我记得,Percona会将Handler Socket打包在其安装包中。(与Percona无关!)

http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html


33

简短的答案是肯定的答案-随着行数的增长,您选择的精确模式,数据类型和操作的重要性也随之增长。

对数据进行标准化的程度取决于您计划对存储的数据执行的操作。特别是您的“数据点”表似乎有问题-您是否打算将任何给定光谱的第n个点与其他光谱的第m个点进行比较?如果没有,分开存放可能是一个错误。如果您的数据点不是单独存在的,而是仅在关联光谱的上下文中才有意义,则您不需要主键-光谱的外键和“第n个”列(您的“索引”列?)就足够了。

定义必须执行的频谱间和频谱内操作,然后找出完成这些操作的最便宜的方法。如果只需要相等性,则可以将它们归一化-可能带有一些有助于您的操作的预先计算的统计元数据。如果您确实需要对单个数据点进行SQL访问,请确保将每行的大小减小到字段的最小数量和最小数据类型。

我亲自管理过的最大的MySQL是大约1亿行。在这种大小下,您希望保持行大小,从而使字段保持固定大小,这使MySQL可以通过乘以每行的固定大小来乘以有效的计算表中任何行的位置(请考虑指针算术),尽管具体细节取决于您计划使用的存储引擎。如果可以使用MyISAM,它在速度上弥补了可靠性方面的不足,在您的情况下就足够了。将可变大小的字段(例如VARCHAR)替换为CHAR(n),并在读取的查询上使用RTRIM()。

一旦表行是固定宽度的,就可以通过仔细评估MySQL的整数数据类型(其中一些是非标准的)来减少字节数。通过将4字节的INT转换为3字节的MEDIUMINT,您可以节省出的每1字节节省为每百万行节省约1MB,这意味着更少的磁盘I / O和更有效的缓存。使用您可以避免最小数据类型。仔细评估浮点类型,看看是否可以用4字节FLOAT甚至<8字节定点NUMERIC替换 8字节的DOUBLE。运行测试以确保以后选择的内容都不会对您造成伤害。

根据数据集的预期属性和所需的操作,可能会进一步节省更多不寻常的值编码(预期的模式/重复可以编码为一组值的索引,原始数据可能仅对元数据并被丢弃等)-尽管只有在尝试了所有其他选项后,才有必要进行奇特,不直观,破坏性的优化。

最重要的是,无论您最终要做什么,都不要假设您选择了完美的架构,然后盲目地开始转储数千万条记录。好的设计需要时间来发展。创建一个大型但可管理(例如1-5%)的测试数据集,并验证模式的正确性和性能。查看不同操作的执行方式(http://dev.mysql.com/doc/refman/5.0/en/using-explain.html),并确保平衡架构以支持最频繁的操作。

我说短了吗?哎呀 无论如何,祝你好运!


23

似乎将数据点数据从XML(与运行时间和类型等元数据相对)分解为数据库形式的唯一原因是,当您分析跨阵列的光谱时-即也许找到所有具有一定的签名运行。现在只有您知道您的问题域,但这可能类似于存储以96kHz采样的音乐(每行1个采样)。我不确定大小的问题比数据的使用方式更多。跨数据查询等同于由甲壳虫乐队询问所有歌曲中2分钟的相对振幅。如果您知道可以执行的分析类型,则很有可能对信号进行分析并将其存储在有关运行的元数据中可能更有意义。

我也不确定您的源数据是否稀疏。数据库中的频谱完全可能只包含非零条目,而原始XML确实包含零条目,因此您的总行数可能比源数据少。

因此,就像许多问题一样,在询问有关MySQL处理模型的问题之前,退后一步看看模型以及模型的使用方式可能比担心性能还合适。


在查看了您的问题更新之后,我认为将二进制数据存储为BLOB或仅指向文件的指针的模型就足够了,并致力于修改您的模型以存储有关首次数据时已确定的重要峰的数据。读。


18

我运行了一个Web分析服务,其中包含约50个数据库服务器,每个服务器包含许多表,这些表超过1亿行,而有些表往往超过10亿行,有时每台服务器多达20亿行。

这里的表现很好。这是非常标准化的数据。但是,我主要的关注点是,对于这些表,您将远远超过42亿行的标记(可能不是“运行”,可能还有其他两个),这意味着您需要使用BIGINT而不是INT主/外键。

与BIGINT场索引列MySQL的性能是可笑可怕相比INT。我犯了一次错误的操作,因为我认为这张桌子可能会超过这个大小,并且一旦达到几亿行,性能就简直太糟糕了。我没有原始数据,但是当我说不好的时候,我的意思是Windows ME不好。

此列是主键。我们将其转换为仅是INT和presto magico,性能再次良好。

当时我们所有的服务器都在Debian 5和MySQL 5.0上。从那以后,我们已升级到Debian 6和Percona MySQL 5.5,因此此后情况可能有所改善。但是根据我在这里的经验,不,我认为它不会很好地工作。


17

不管它是否有效,您总是会在单个整体存储介质中遇到相同的问题:磁盘速度很慢。以100 MB / s(非常适合旋转媒体)的速度,读取 1TB表需要3个小时;假设没有任何分析,搜寻或其他延迟会使您失望。

这就是为什么几乎每个“大数据”安装都使用某种分布式数据存储的原因。您可以花费8倍的钱来构建一台超级出色的计算机来运行数据库,但是如果您有大量可以并行扫描的数据,则最好总是在8台便宜的计算机上分配负载。

hadoop这样的项目是专门为此类目的而构建的。您将由一堆廉价计算机组成一个集群,将数据分布在所有这些计算机中,然后并行查询它们。这只是基于相同想法构建的六种解决方案之一,但这是一个非常受欢迎的解决方案。


13

嗯...我看到您选择这种数据结构的两个原因:

  • 您确实需要执行任何数据点与任何数据点查询
  • 您打算在SQL中执行所有逻辑

现在,我建议您认真研究一下您的要求,并验证至少上述假设之一是正确的。如果都不是,那只会使事情变慢。对于这种数据集,我建议先找出期望如何访问数据,需要什么样的准确性等,然后再根据这些数据来设计数据库。

PS:请记住,每个数据点至少需要36 + 5字节,因此使用200B数据点应至少提供8.2 TB的所需空间。

PPS:您不需要表中的iddatapoints,这PRIMARY KEY (spectrum_id, index)可能就足够了(请注意,这index可能是保留字)


12

编辑:

在MySQL中不要将数据存储在单个磁盘上。仅从单个介质中读取大量数据将花费数小时。您需要向外扩展,而不是向上。

如果要进行有效的数据分析,则需要对数据进行规范化处理。您不是在这里设计在线系统。您想计算数字,进行相应的设计。

下面的原始答案。


答案会因您的查询而异,MySQL可能不是此工作的最佳工具。您可能需要查看可以“向外扩展”而不是“向上扩展”的解决方案。如果您愿意付出一些努力,也许您应该考虑使用Map Reduce解决方案,例如Hadoop。

如果您想执行更多临时查询,则Google的BigQuery解决方案可能非常适合您。Google I / O 2012的相关演示:使用BigQuery处理大数据

因此,解决方案将取决于这是否是一次性的事情,以及您是否要合理地支持即席查询。


9

没有人提到,因此是我的建议。看一下大规模分片的MySQL解决方案。例如,请参阅此备受推崇的tumblr演示

这个概念是:

  • 代替一个超大型数据库
  • 使用许多保存原始数据的小文件

因此,您可以水平缩放,而不必尝试提高垂直性能。谷歌的BigTableGFS还使用便宜的水平可伸缩节点来存储和查询PB级数据。

但是,如果您需要对不同的分片运行查询,则会遇到麻烦。


如果有人感兴趣,我前一阵子做了一个hello-world sharding应用程序。这是讨论在这里的博客文章。我使用了RavenDB和C#,但细节无关紧要,想法是相同的。


7

数据将存储在哪种机器上?它是共享存储设备吗?

决定您查询时间的最终因素将是硬盘驱动器。数据库及其查询优化器旨在减少磁盘I / O的数量。假设您只有3个表,这将非常可靠地完成。

硬盘的读/写速度将比内存速度慢200-300倍。寻找具有非常快的延迟和快速读写速度的硬盘驱动器。如果所有这些数据都在一个2 TB驱动器上,则可能要等待很长时间才能完成查询。硬盘驱动器延迟约为10-15毫秒,而内存延迟则小于10纳秒。硬盘驱动器延迟可能比内存延迟慢1000-2000倍。在整个系统中,机械臂在硬盘驱动器上的移动是最慢的事情。

你有多少RAM?16 GB?可以说使您拥有32条记录。您有16000个文件。如果要线性扫描所有数据点,则很可能仅在寻找时间上就花费5-10秒。然后考虑传输速率50mb / s?大约7个小时。此外,任何临时保存的数据都必须存储在硬盘驱动器上,以便为读取新数据腾出空间。

如果您使用的是其他用户正在积极使用的共享存储设备,则最好的选择是晚上运行所有内容。

减少嵌套查询的数量也很有帮助。嵌套查询会生成临时表,这将进一步打击您的硬盘。我希望您的硬盘驱动器上有足够的可用空间。

查询优化一次只能查看1个查询。因此无法优化嵌套的select语句。但是,如果您知道特定的嵌套查询将导致返回小的数据集,请保留该数据集。查询优化使用直方图和粗略的假设,如果您知道一些有关数据和查询的知识,请继续进行操作。

您对数据存储在磁盘上的方式了解得越多,就能越快地编写查询。如果所有内容都按顺序存储在主键上,则对从嵌套查询返回的主键进行排序可能是有益的。另外,如果您可以减少所有需要预先分析的数据集,请执行此操作。根据您的系统,每个文件大约需要1秒钟的数据传输时间。

如果您要修改Name值(varchars),则将其更改为具有最大大小的数据类型,这将防止碎片,并且权衡只是更多的内存字节。也许是NVARCHAR,最大为100。

至于有关对表进行非规范化的注释。我认为最好将数据点存储在较大的组中(可能是光谱),然后使用python或与数据库交互的语言进行数据分析。除非您有SQL向导。


3
您强调了硬盘驱动器与内存延迟之间的巨大差异,但是数量却相差1000倍。如果硬盘驱动器的延迟约为10ms,而内存的延迟为10ns,则延迟的相差不会相差1000倍,而是相差1000倍。 1,000,000!
spectre256

6

对我来说,这听起来像是一种使用场景,您需要此处所述的“关系列存储”之类的东西。

我可能会误解设计,但是如果您主要处理大量数组,则将它们存储在典型的面向行的表中意味着每个元素都类似于一个切片。如果您有兴趣以一种典型的方式查看切片,这是有道理的,但是如果您一次真正查看整个列,则效率可能会较低。

检索数组时,不仅可能不需要将标准化后的表与其他表连接,而且还可以将序列作为数组而不是哈希进行检索。

我确实可能对这个问题有误解,甚至没有提出具体的解决方案。

即使这不是当前的或可部署的解决方案,这又可能是有意义的。



5

对,但是...

我已经处理了具有20亿行的表。但是,只有使用PK的查询才有望很快。

最重要的是,硬件具有足够的RAM以适合内存中的整个表。当这成为一个问题时(当时最大为96GB),进行了垂直分区,将每台计算机上设置的表的大小保持足够小,以便仍可容纳在内存中。而且,这些机器通过10Gb光纤连接,因此网络吞吐量并不是什么大问题。

顺便说一句。您的架构看起来像某种适合NoSQL解决方案的东西, run_id用作光谱的spectrum_id哈希键和数据点的哈希键。


4

我已经在我的博客上写过有关此主题的文章:http//www.tocker.ca/2013/10/24/improving-the-performance-of-large-tables-in-MySQL.html

重复一些关键点:

  • B树会随着它们的变大而退化,并且无法容纳到内存中(MySQL在这里并不孤单)。
  • InnoDB确实具有一些有助于维持某些性能的功能(更改缓冲;以前称为“插入缓冲区”)。
  • 分区也可以提供帮助。

在我的帖子中,蒂姆·卡拉汉(Tim Callaghan)的评论与此链接:http : //www.tokutek.com/resources/benchmark-results/benchmarks-vs-innodb-hdds/#iiBench

该图显示了使用iibench基准插入10亿行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.