hax*_*ney 289 mysql performance database-design
我计划将质谱仪的扫描结果存储在 MySQL 数据库中,并想知道存储和分析这一数量的数据是否远程可行。我知道性能因环境而异,但我正在寻找粗略的数量级:查询需要 5 天还是 5 毫秒?
每个输入文件包含一次光谱仪运行;每次运行都由一组扫描组成,每个扫描都有一个有序的数据点数组。有一些元数据,但文件的大部分由 32 位或 64 位整数或浮点数数组组成。
|----------------+---------------------------------------| | 操作系统 | Windows 2008 64 位 | | MySQL 版本 | 5.5.24 (x86_64) | | 中央处理器 | 2x 至强 E5420(共 8 核)| | 内存 | 8GB | | SSD 文件系统 | 500 GiB | | 硬盘RAID | 12 TiB | |----------------+---------------------------------------|
服务器上还有一些其他服务在使用可忽略的处理器时间运行。
|-----------+--------------| | 文件数| ~16,000 | | 总尺寸| 1.3 TiB | | 最小尺寸 | 0 字节 | | 最大尺寸 | 12 GiB | | 意思 | 800 兆字节 | | 中位数 | 500 兆字节 | | 总数据点 | ~2000 亿 | |-----------+--------------|
数据点的总数是一个非常粗略的估计。
我打算做“正确”的事情(即疯狂地规范化数据),因此会有一个runs表,spectra一个外键为 toruns的datapoints表,以及一个外键为 to的表spectra。
我将跨多个谱进行分析,甚至可能进行多次运行,从而产生可能涉及数百万行的查询。假设我正确地索引了所有内容(这是另一个问题的主题)并且我没有尝试在网络上混洗数百个 MiB,那么 MySQL 处理这个问题是否合理?
扫描数据将来自基于 XML 的
mzML格式的文件。这种格式<binaryDataArrayList>的核心在于存储数据的
元素。每次扫描产生 >= 2 个<binaryDataArray>元素,它们一起形成一个二维(或更多)形式的数组[[123.456, 234.567, ...], ...]。
这些数据是一次性写入的,因此更新性能和事务安全性不是问题。
我对数据库模式的天真计划是:
runs 桌子| 列名 | 类型 | |-------------+-------------| | 身份证 | 主键 | | 开始时间 | 时间戳 | | 姓名 | VARCHAR | |-------------+-------------|
spectra 桌子| 列名 | 类型 | |----------------+-------------| | 身份证 | 主键 | | 姓名 | VARCHAR | | 索引 | 国际 | | 频谱类型| 国际 | | 代表| 国际 | | 运行 ID | 外键 | |----------------+-------------|
datapoints 桌子| 列名 | 类型 | |-------------+-------------| | 身份证 | 主键 | | 频谱_id | 外键 | | mz | 双| | num_counts | 双| | 索引 | 国际 | |-------------+-------------|
这合理吗?
因此,正如您可能已经推断出的那样,我是程序员,而不是实验室中的生物学家,因此我对科学的了解不如真正的科学家。
这是我将处理的数据类型的单一光谱(扫描)图:

该软件的目标是找出峰值的位置和重要性。我们现在使用专有软件包来解决这个问题,但我们想编写我们自己的分析程序(用 R 语言),这样我们就知道表下发生了什么。如您所见,绝大多数数据都是无趣的,但我们不想丢弃我们的算法遗漏的潜在有用数据。一旦我们有一个我们满意的可能峰值列表,管道的其余部分将使用该峰值列表而不是原始数据点列表。我想将原始数据点存储为一个大的 blob 就足够了,因此如果需要,可以重新分析它们,但只保留峰值作为不同的数据库条目。在那种情况下,每个频谱只会有几十个峰值,所以疯狂的缩放不应该
小智 119
我对您的需求不是很熟悉,但也许将每个数据点存储在数据库中有点过头了。这听起来几乎就像通过将每个像素存储为关系数据库中的单独记录来存储图像库的方法。
一般来说,在数据库中存储二进制数据在大多数情况下是错误的。通常有更好的方法来解决问题。虽然在关系数据库中存储二进制数据本质上并没有错,但往往弊大于利。顾名思义,关系数据库最适合存储关系数据。二进制数据不是关系数据。它增加了数据库的大小(通常是显着的),可能会损害性能,并可能导致有关维护十亿记录 MySQL 实例的问题。好消息是有些数据库特别适合存储二进制数据。其中之一,虽然并不总是显而易见的,是您的文件系统!只需为您的二进制文件提出一个目录和文件命名结构,
另一种方法是使用基于文档的存储系统存储数据点(可能还有光谱)数据,并使用 MySQL 进行运行(或者可能将运行放入与其他数据库相同的数据库中)。
小智 114
我曾经使用过一个非常大的(Terabyte+)MySQL 数据库。我们拥有的最大的表实际上超过了 10 亿行。这是使用 MySQL 5.0,所以事情可能会有所改善。
有效。MySQL 大部分时间都正确处理了数据。虽然它非常笨拙。(如果您想要 1 TB 数据的 6 sigma 级别可用性,请不要使用 MySQL。我们是一家没有 DBA 且资金有限的初创公司。)
仅备份和存储数据是一项挑战。如果我们需要,恢复该表需要几天时间。
我们在 10-1 亿行范围内有许多表。对表的任何重要连接都太耗时,而且会花费很长时间。所以我们编写了存储过程来“遍历”表并根据“id”的范围处理连接。通过这种方式,我们将一次处理 10-100,000 行数据(根据 id 的 1-100,000 然后 100,001-200,000 等加入)。这比加入整个表要快得多。
在不基于主键的非常大的表上使用索引也更加困难。Mysql 5.0 将索引存储为两部分——它将索引(主索引除外)存储为主键值的索引。因此,索引查找分为两部分:首先,MySQL 访问索引并从中提取需要查找的主键值,然后对主键索引进行第二次查找以找到这些值的位置。
这样做的结果是,对于非常大的表(1-2 亿多行),对表进行索引的限制更大。您需要更少、更简单的索引。并且即使不直接在索引上执行简单的 select 语句也可能永远不会回来。Where 子句必须命中索引或忘记它。
但话虽如此,事情确实奏效了。我们能够将 MySQL 与这些非常大的表一起使用,并进行计算并获得正确的答案。
尝试对 2000 亿行数据进行分析需要非常高端的硬件以及大量的动手和耐心。仅以一种可以恢复的格式备份数据将是一项重要的工作。
我同意srini.venigalla 的回答,即疯狂地规范化数据在这里可能不是一个好主意。使用如此多的数据跨多个表进行连接将使您面临文件排序的风险,这可能意味着您的某些查询将永远不会回来。使用简单的整数键去规范化会给你一个更好的成功机会。
我们拥有的一切都是 InnoDB。关于 MyISAM 与 InnoDB:最主要的是不要将两者混合。由于 MySQL 缓存键和其他数据的方式,您无法真正为两者优化服务器。如果可以,请为服务器中的所有表选择一个。MyISAM 可能有助于解决一些速度问题,但它可能无助于需要完成的整体 DBA 工作——这可能是一个杀手。
小智 71
疯狂地规范化数据
在这种情况下,疯狂地标准化数据可能不是正确的策略。通过以规范化形式和非常适合您的应用程序的物化视图形式存储数据,让您的选择保持开放。这种类型的应用程序的关键不是编写临时查询。查询建模比数据建模更重要。从您的目标查询开始,并朝着最佳数据模型努力。
Is this reasonable?
Run Code Online (Sandbox Code Playgroud)
我还将创建一个包含所有数据的额外平面表。
run_id | spectrum_id | data_id | <data table columns..> |
Run Code Online (Sandbox Code Playgroud)
我将使用此表作为所有查询的主要来源。原因是为了避免必须进行任何连接。没有索引的连接会使您的系统非常不可用,并且在如此大的文件上建立索引也同样糟糕。
策略是,先查询上表,将结果转储到临时表中,将临时表与Run和Spectrum的查找表连接起来,得到你想要的数据。
您是否分析过您的写入需求与读取需求?放弃 SQL 并使用非标准数据存储机制将是非常诱人的。在我看来,这应该是最后的手段。
为了加快写入速度,您可能想尝试使用 Handler Socket 方法。如果我记得的话,Percona 在他们的安装包中打包了 Handler Socket。(与 Percona 无关!)
http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html
Rya*_*ynn 33
简短的回答是肯定的——随着行数的增加,您选择的精确模式、数据类型和操作的重要性也会增加。
标准化数据的程度取决于您计划对存储的数据执行的操作。特别是您的“数据点”表似乎有问题——您是否打算将任何给定光谱的第 n 个点与任何其他光谱的第 m 个点进行比较?如果没有,将它们分开存储可能是错误的。如果您的数据点不是独立的,而仅在其关联光谱的上下文中有意义,则您不需要 PRIMARY KEY - 光谱的外键和“第 n”列(您的“索引”列?)就足够了.
定义您必须执行的频谱间和频谱内操作,然后找出完成它们的最便宜的方法。如果只需要相等,它们可能会被非规范化——可能使用一些预先计算的统计元数据来帮助您的操作。如果您确实需要在 SQL 中访问单个数据点,请确保将每行的大小减少到最少的字段数和尽可能小的数据类型。
我个人管理过的最大的 MySQL 是大约 1 亿行。在这个大小下,您希望保持行和字段固定大小——这允许 MySQL通过乘以每行的固定大小(想想指针算法)来有效地计算表中任何行的位置——尽管确切的细节取决于您计划使用的存储引擎。如果可以的话,请使用 MyISAM,它在速度上弥补了可靠性上的不足,在您的情况下它应该足够了。用 CHAR(n) 替换可变大小字段,例如 VARCHAR,并在读取查询中使用 RTRIM()。
一旦您的表行是固定宽度的,您可以通过仔细评估 MySQL 的整数数据类型(其中一些是非标准的)来减少字节数。通过将 4 字节的 INT 转换为 3 字节的 MEDIUMINT,每节省 1 字节,每百万行可以节省约 1MB——这意味着更少的磁盘 I/O 和更有效的缓存。使用您可以摆脱的最小数据类型。仔细评估浮点类型,看看是否可以将 8 字节 DOUBLE 替换为 4 字节 FLOAT 甚至 <8 字节定点 NUMERIC。运行测试以确保您选择的任何东西以后都不会咬到您。
根据数据集的预期属性和所需的操作,可能会进一步节省更不寻常的值编码(预期模式/重复,可以编码为一组值的索引,原始数据可能只会有意义地有助于元数据并被丢弃等)——尽管异国情调的、不直观的、破坏性的优化只有在尝试了所有其他选项时才值得。
最重要的是,无论你最终做什么,都不要假设你已经选择了完美的模式,然后盲目地开始倾倒数百万条记录。好的设计需要时间来发展。创建大量但易于管理(例如 1-5%)的测试数据集,并验证架构的正确性和性能。查看不同的操作如何执行 (http://dev.mysql.com/doc/refman/5.0/en/using-explain.html) 并确保您平衡架构以支持最频繁的操作。
我说短了吗?哎呀。不管怎样,祝你好运!
Cad*_*oux 23
似乎将数据点数据从 XML(而不是运行时间和类型等元数据)中分解为数据库形式的唯一原因是当您分析阵列中的光谱时 - 即可能找到所有以特定签名运行。现在只有您知道您的问题域,但这可能类似于存储以 96kHz 采样的音乐,每行 1 个样本。我不确定大小比如何使用数据更重要。跨数据查询相当于在 The Beatles 的所有歌曲中询问歌曲中 2 分钟的相对振幅。如果您知道可能执行的分析类型,很可能对信号执行这些分析并将其存储在有关运行的元数据中可能更有意义。
我也不确定您的源数据是否稀疏。完全有可能数据库中的频谱应该只包含非零条目,而原始 XML 确实包含零条目,因此您的总行数可能比源数据中的少得多。
因此,像许多问题一样,在询问 MySQL 处理您的模型之前,退后一步查看模型以及它将如何使用它可能比现在担心性能更合适。
在查看您的问题更新后,我认为将二进制数据存储为 BLOB 或仅指向文件的指针的模型就足够了,并致力于修改您的模型以存储有关在数据首次出现时已识别的重要峰值的数据读。
小智 18
我运行一个包含大约 50 个数据库服务器的 Web 分析服务,每个服务器包含超过 1 亿行的许多表,还有几个往往超过 10 亿行,有时高达 20 亿(在每台服务器上)。
这里的表演很好。这是非常规范化的数据。但是 - 我对阅读本文的主要担忧是这些表的行数将远远超过 42 亿(可能不是“运行”,但可能是其他两个),这意味着您需要使用 BIGINT 而不是 INT主键/外键。
与 INT 相比,在索引列中使用 BIGINT 字段的 MySQL 性能非常糟糕。我犯了一次错误,用我认为可能会增长到这个大小的表来做这件事,一旦它达到几亿行,性能就非常糟糕。我没有原始数据,但是当我说不好时,我的意思是 Windows ME 不好。
此列是主键。我们把它改回了一个 INT 和 presto magico,性能又好了。
当时我们所有的服务器都在 Debian 5 和 MySQL 5.0 上。我们已经升级到 Debian 6 和 Percona MySQL 5.5,所以从那时起情况可能有所改善。但根据我在这里的经验,不,我认为它不会很好地工作。
小智 18
无论它是否有效,使用单个单片存储介质时您总会遇到同样的问题:磁盘速度很慢。以 100 MB/s 的速度(对于旋转媒体来说非常好),仅读取1TB 表需要 3 小时;那是假设没有分析或寻求或其他延迟会减慢您的速度。
这就是为什么几乎每个“大数据”安装都使用某种分布式数据存储的原因。您可以花费 8 倍的资金来构建一台超级出色的计算机来运行您的数据库,但是如果您有大量可以并行扫描的数据,那么您几乎总是最好将负载分配到 8 台更便宜的计算机上。
像hadoop这样的项目是专门为这样的目的而构建的。您构建了一个由一大堆廉价计算机组成的集群,将数据分布在所有这些计算机上,并并行查询它们。它只是围绕同一想法构建的六种解决方案之一,但它非常受欢迎。
小智 13
嗯...我认为你会选择这种数据结构的原因只有两个:
现在,我建议您仔细研究一下您的要求,并验证上述假设中至少有一个是正确的。如果两者都不是真的,那么你只是让事情变慢了。对于这种数据集,我建议首先找出数据的访问方式,您需要什么样的准确性等 - 然后围绕这些来设计您的数据库。
PS:请记住,每个数据点至少需要 36+5 个字节,因此对于 200B 数据点,至少需要 8.2 TB 的空间。
PPS:您不需要表中的id列datapoints,一个PRIMARY KEY (spectrum_id, index)可能就足够了(请注意这index可能是一个保留字)
小智 12
编辑:
不要在数据存储在单个磁盘上的 MYSQL 中执行此操作。仅从单一介质读取大量数据就需要数小时。你需要扩大规模,而不是扩大规模。
如果您想进行有效的数据分析,则需要对数据进行非规范化。您不是在这里设计在线系统。你想处理数字,相应地设计。
下面一行的原始答案。
答案会因您的查询而异,MySQL 可能不是这项工作的最佳工具。您可能想查看可以“向外”而不是“向上”扩展的解决方案。如果您愿意付出一些努力,也许您应该查看 Map Reduce 解决方案,例如 Hadoop。
如果您想进行更多临时查询,Google 的 BigQuery解决方案可能非常适合您。Google I/O 2012 的相关演讲:Crunning Big Data with BigQuery
因此,解决方案将取决于这是否是一次性的,以及您是否想合理地支持即席查询。
没有人提到,因此我的建议。看看大规模分片的 MySQL解决方案。例如,请参阅此备受推崇的tumblr 演示文稿。
概念是:
因此,您可以水平扩展,而不是尝试提高垂直性能。Google 的BigTable和GFS也在使用廉价的水平可扩展节点来存储和查询 PB 级数据。
但是,如果您需要在不同的分片上运行查询,则会出现问题。
小智 7
数据将存储在什么样的机器上?它是共享存储设备吗?
决定查询时间的最终因素将是您的硬盘。数据库及其查询优化器旨在尽可能减少磁盘 I/O 的数量。鉴于您只有 3 个表,这将非常可靠地完成。
硬盘驱动器的读/写速度将比内存速度慢 200-300 倍。寻找具有极快延迟和快速读写速度的硬盘。如果所有这些数据都在一个 2 TB 的驱动器上,您可能需要等待很长时间才能完成查询。硬盘延迟约为 10-15 毫秒,而内存延迟小于 10 纳秒。硬盘延迟可能比内存延迟慢 1000-2000 倍。硬盘驱动器上机械臂的移动是整个系统中最慢的事情。
你有多少内存?16 GB?可以说,让您拥有 32 条记录。您有 16000 个文件。如果您要对所有数据点进行线性扫描,那么仅搜索时间就可以轻松结束 5-10 秒。然后考虑传输速率 50mb/s?约7小时。此外,任何临时保存的数据都必须存储在硬盘上,以便为正在读取的新数据腾出空间。
如果您使用的共享存储设备正被其他用户积极使用……您最好的选择是在晚上运行所有内容。
减少嵌套查询的数量也很有帮助。嵌套查询会产生临时表,这会进一步影响您的硬盘。我希望你的硬盘上有足够的可用空间。
查询优化一次只能查看 1 个查询。因此无法优化嵌套的 select 语句。但是,如果您知道特定的嵌套查询将导致要返回的小数据集,请保留它。查询优化使用直方图和粗略假设,如果您对数据和查询有所了解,请继续执行。
您对数据在磁盘上的存储方式了解得越多,编写查询的速度就越快。如果所有内容都按顺序存储在主键上,则对从嵌套查询返回的主键进行排序可能会有所帮助。此外,如果您可以减少需要预先分析的数据集集,那就去做吧。根据您的系统,每个文件大约需要 1 秒的数据传输时间。
如果您要修改 Name 值(varchars),我会将其更改为具有最大大小的数据类型,这样可以防止碎片化,而权衡只是多几个字节的内存。也许是一个最大为 100 的 NVARCHAR。
至于关于非规范化表格的评论。我认为最好将数据点存储在更大的组中(可能作为光谱),然后用 python 或与数据库交互的语言进行数据分析。除非您是 SQL 向导。
小智 6
我建议您尝试对表进行分区。我们在一个表(股票市场数据)中有超过 8000 万行,并且可以快速访问它。
根据您打算如何搜索数据,您应该设计分区。在我们的例子中,按日期效果很好,因为我们查询特定日期。
http://dev.mysql.com/doc/refman/5.1/en/partitioning-limitations.html
http://www.slideshare.net/datacharmer/mysql-partitions-tutorial
小智 5
对,但是...
我处理过有 20 亿行的表。然而,只有使用 PK 的查询预计会很快。
最重要的是,硬件有足够的 RAM 来容纳整个内存表。当这成为一个问题(当时最大为 96GB)时,进行了垂直分区,将每台机器上设置的表的大小保持在足够小的内存中。此外,这些机器通过 10Gb 光纤连接,因此网络吞吐量不是什么大问题。
顺便提一句。您的架构看起来像某种可以适合 NoSQL 解决方案的东西, run_id用作频谱的spectrum_id散列键和数据点的散列键。
我在我的博客上写过这个主题: http://www.tocker.ca/2013/10/24/improving-the-performance-of-large-tables-in-mysql.html
重复一些要点:
在我的帖子 Tim Callaghan 的评论中链接到此: http://www.tokutek.com/resources/benchmark-results/benchmarks-vs-innodb-hdds/#iiBench
其中显示使用 iibench 基准插入 10 亿行。