处理大数据量的MySQL技巧

59

抱歉篇幅有点长!

我有一个包含约30个表格(使用InnoDB引擎)的数据库。其中只有两张表,“transaction”和“shift”比较大(第一张表有150万行,第二张表有23k行)。现在一切都正常运作,我没有当前数据库大小的问题。

然而,我们将会有一个类似的数据库(相同的数据类型、设计等),但更大,例如,“transaction”表将会有约10亿条记录(每天约230万次交易),我们正在思考如何应对MySQL中这么多的数据量?(读写都很密集)。我阅读了很多相关的帖子,想知道Mysql(尤其是InnoDB引擎)是否能够处理亿级别的记录,但我仍然有一些问题。我阅读过的一些相关帖子如下:

我目前了解到提高大型表性能的方法有:
  1. (对于我的情况)增加 innodb_buffer_pool_size 的大小(例如,最多可达 RAM 的 80%)。此外,我在 percona 博客中找到了其他一些 MySQL 性能调整设置
  2. 在表上使用适当的索引(使用查询的 EXPLAN)
  3. 对表进行分区
  4. MySQL 分片或集群
以下是我的问题/困惑:
关于分区,我有一些疑惑,不确定我们是否应该使用它。一方面,许多人建议在表非常大的情况下使用分区来提高性能。另一方面,我读到很多帖子说它并不能提高查询性能,并且不能使查询运行更快(例如 这里 这里)。而且,我在 MySQL参考手册中看到 InnoDB外键和MySQL分区不兼容(我们有外键)。
关于索引,现在它们表现良好,但据我所知,对于非常大的表,索引更加严格限制(正如Kevin Bedell在这里中提到的)。此外,索引加速读取操作,但会减缓写入操作(插入/更新)。因此,对于我们将要拥有这个大型数据库的新项目,我们应该先插入/加载所有数据,然后再创建索引吗?(以加快插入速度)
如果我们无法为我们的大表(“交易”表)使用分区,那么除了MySQl变量设置(例如innodb_buffer_pool_size)之外,改进性能的替代选择是什么?我们应该使用Mysql集群吗?(我们也有许多联接)

编辑

这是我们最大的表格"transaction"的show create table语句:

  CREATE TABLE `transaction` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `terminal_transaction_id` int(11) NOT NULL,
 `fuel_terminal_id` int(11) NOT NULL,
 `fuel_terminal_serial` int(11) NOT NULL,
 `xboard_id` int(11) NOT NULL,
 `gas_station_id` int(11) NOT NULL,
 `operator_id` text NOT NULL,
 `shift_id` int(11) NOT NULL,
 `xboard_total_counter` int(11) NOT NULL,
 `fuel_type` int(11) NOT NULL,
 `start_fuel_time` int(11) NOT NULL,
 `end_fuel_time` int(11) DEFAULT NULL,
 `preset_amount` int(11) NOT NULL,
 `actual_amount` int(11) DEFAULT NULL,
 `fuel_cost` int(11) DEFAULT NULL,
 `payment_cost` int(11) DEFAULT NULL,
 `purchase_type` int(11) NOT NULL,
 `payment_ref_id` text,
 `unit_fuel_price` int(11) NOT NULL,
 `fuel_status_id` int(11) DEFAULT NULL,
 `fuel_mode_id` int(11) NOT NULL,
 `payment_result` int(11) NOT NULL,
 `card_pan` text,
 `state` int(11) DEFAULT NULL,
 `totalizer` int(11) NOT NULL DEFAULT '0',
 `shift_start_time` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `terminal_transaction_id` (`terminal_transaction_id`,`fuel_terminal_id`,`start_fuel_time`) USING BTREE,
 KEY `start_fuel_time_idx` (`start_fuel_time`),
 KEY `fuel_terminal_idx` (`fuel_terminal_id`),
 KEY `xboard_idx` (`xboard_id`),
 KEY `gas_station_id` (`gas_station_id`) USING BTREE,
 KEY `purchase_type` (`purchase_type`) USING BTREE,
 KEY `shift_start_time` (`shift_start_time`) USING BTREE,
 KEY `fuel_type` (`fuel_type`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1665335 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT

谢谢您的时间,

3个回答

76
MySQL能否合理地处理数十亿行的查询?-- MySQL可以处理数十亿行。"合理"取决于查询的内容;让我们看看具体的查询。
在多达数十亿行的情况下,InnoDB(MySQL 5.5.8)是否是正确的选择?-- 5.7有一些改进,但5.5也相当不错,尽管已经有将近6年的历史了,并且即将不再受支持。
处理数十亿行的最佳数据存储方式-- 如果你指的是"引擎",那么选择InnoDB。
MySQL数据库在性能开始下降之前能有多大?-- 再次强调,这取决于查询的内容。我可以展示给你一个只有1千行的表,但性能会崩溃;我也曾经处理过数十亿行的表,性能良好。
为什么MySQL在大表上可能变慢?-- 范围扫描导致I/O,而这是最慢的部分。
MySQL能处理大约3亿条记录的表吗?-- 再次回答是,限制大约在一万亿行左右。
(对于我的情况是InnoDB表)增加innodb_buffer_pool_size(例如,增加到RAM的80%)。此外,我在Percona博客中找到了一些其他的MySQL性能调优设置-- 是的。
在表上建立适当的索引(使用EXPLAIN来查询)-- 好的,让我们看看这些查询。在这个关键领域中,有很多错误可以犯。
对表进行分区-- "分区并非万能解决方案!"我在我的博客中一直强调这一点。
MySQL分片-- 目前这是自己动手实现的。
MySQL集群-- 目前最好的选择是基于Galera的选项(PXC、MariaDB 10、自己动手使用Oracle)。Oracle的"Group Replication"("InnoDB Cluster")也是一个可行的竞争者。
分区不支持FOREIGN KEY或"全局"UNIQUE。
在你所说的规模下,UUID不仅会减慢系统速度,而且会导致系统崩溃。类型1的UUID可能是一个解决办法。
插入和索引构建速度-- 由于变数太多,无法给出一个单一的答案。让我们看看你的初步CREATE TABLE语句以及你打算如何输入数据。
大量的连接-- "规范化,但不要过度规范化。"特别是,不要对日期时间、浮点数或其他"连续"值进行规范化。
构建摘要表
每天处理230万个事务-- 如果这是2.3百万个插入(每秒30个),那么性能问题不大。如果更复杂,那么可能需要RAID、SSD、批处理等。
处理如此大量的数据-- 如果大部分活动都是在"最近"的行上进行,那么缓冲池会很好地"缓存"这些活动,从而避免I/O。如果活动是"随机"的,那么MySQL(或其他任何数据库)都会遇到I/O问题。
在像你这样的表中,缩小数据类型有所帮助。我怀疑你是否需要4个字节来指定燃料类型。有多种1字节的方法可以使用。

1
还有一件事——“MySQL NDB Cluster”与Galera不同;NDB有一个利基市场;它_可能_对您有用;让我们更多地了解您的应用程序。 - Rick James
1
感谢Rick提供详细的答案。现在我的主要问题是我不确定我们是否应该进行聚类(我以前从未做过)。我的意思是,我们什么时候应该这样做,什么时候不应该?在进行聚类之前,我应该考虑哪些因素?如果我们必须这样做,我应该从哪里开始? - mOna
另外,你说过你应该看到查询(用于索引、性能等)。我应该考虑哪些关于查询的信息?我们的应用程序需要哪些信息?我如何向你展示这些查询?(如果这是个愚蠢的问题,请原谅!) - mOna
1
数据类型 -- 转账?日志记录?数据仓库?科学研究读数? - Rick James
1
大小并不表示需要进行分区。编写活动确实表明需要进行分片。HA(高可用性)是“集群”的一个指标。每秒插入/更新超过100行表示有一些操作,但通常可以在不进行分片/集群等操作的情况下达到1000 /秒。涉及“group by”的大量“报告”表示“汇总表”。等等。 - Rick James
显示剩余5条评论

4

在收集数十亿行数据时,最好(如果可能的话)在存储之前对数据进行合并、处理、汇总等操作。如果您认为需要返回原始数据,请将其保存在文件中。

这样做将消除大部分您的疑问和担忧,并加速处理速度。


1
我同意。它基本上是在时间上分散处理量,而不是同时进行处理。 - Aeolun

1

将2.7 BL数据与实时VTS系统交叉,没有出现任何问题。特殊情况是数据库不仅存储数据,而且实时读取可用性也是至关重要的一部分,否则无法满足实时跟踪目的。以下几点首先有所帮助:

  1. 漂亮的规范化;
  2. 严格的索引;
  3. InnoDB;
  4. 计算列作为缓存;
  5. 查询优化;
  6. 在SSD(VPS)上使用x4核心和x8 GB RAM,目前仍然运行良好;
  7. 汇总表用于报告和积压处理;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接