临时表(内存引擎)和大型物理表(1.7GB myisam)之间的连接优化

Posted

技术标签:

【中文标题】临时表(内存引擎)和大型物理表(1.7GB myisam)之间的连接优化【英文标题】:Join optimization between Temporary table (Memory Engine) and large physical table (1.7GB myisam) 【发布时间】:2017-01-30 06:23:55 【问题描述】:

我在对大型物理表(1.7GB,2300 万行)进行查询时遇到优化问题,我们称之为 p_table。

我需要使用 p_table 的主索引选择数千行。我的第一次尝试是使用主索引进行 IN 查询,例如

SELECT * FROM p_table WHERE primary_key IN (111,222,333,[... 60.000 more]).

由于查询速度非常慢(50-60 秒),我决定通过使用内存引擎将所有主键添加到临时表中来优化它,然后按如下方式加入

CREATE TEMPORARY TABLE tmp_table (primary_key BIGINT(20) NOT NULL PRIMARY KEY) ENGINE=Memory;
INSERT IGNORE INTO t_table VALUES(111),(222),(333),[thousandsmore];
SELECT p.* FROM p_table AS p FORCE INDEX(PRIMARY) INNER JOIN tmp_table AS t  FORCE INDEX(PRIMARY)  ON t.primary_key = g.primary_key;

此解决方案加快了 x4 的查询,但仍会导致服务器负载很大,每次查询大约需要 10-20 秒(取决于临时表的大小)。

查询的解释表明它没有使用索引,即使我强制它们。

  ["id"]=>
  string(1) "1"
  ["select_type"]=>
  string(6) "SIMPLE"
  ["table"]=>
  string(1) "t"
  ["type"]=>
  string(3) "ALL"
  ["possible_keys"]=>
  string(7) "PRIMARY"
  ["key"]=>
  NULL
  ["key_len"]=>
  NULL
  ["ref"]=>
  NULL
  ["rows"]=>
  string(4) "64320"
  ["Extra"]=>
  string(0) ""

不幸的是,整个数据库超过 50GB,这意味着我买不起完整的内存数据库,而且像 p_table 这样的大表依赖于磁盘 I/O。

您对如何优化流程有什么建议吗?还有关于为什么没有使用索引的任何提示(或者更有可能没有由 EXPLAIN 显示)?

服务器信息: Debian 8.6 mysql 5.5.53 8GB 内存 Raid0 中的 SSD 磁盘(它是 Slave 之一,Raid10 在 Master 上)

非常感谢

【问题讨论】:

***.com/help/someone-answers 【参考方案1】:

为什么不使用索引

mysql 没有在表 t 上使用索引的原因是因为这完全没有必要。您的临时表上没有过滤器(别名为 t)因此您将临时表中的每一行与 p 连接起来。在这里使用索引没有任何好处。

来自手册:https://dev.mysql.com/doc/refman/5.7/en/mysql-indexes.html

MySQL 为这些操作使用索引:

快速找到匹配 WHERE 子句的行。

从考虑中排除行。如果可以选择 多个索引,MySQL 通常使用找到 最少的行数(最具选择性的索引)。

它不符合任何主要标准或上面列出的其他一些标准。但是,您的主键是覆盖索引,因此 mysql 确实可以选择使用索引而不是引用行。然而,这并没有带来任何特别的优势。因此决定不使用索引。

我实际上建议您删除临时表上的索引。这甚至可能会加快速度,因为将 60,000 行插入该表会更快。

你可以尝试的事情

看看是否可以对您的数据进行分区。例如,您插入到临时表中的 id 是否存在某种模式?看看你是否可以根据该模式进行分区

增加服务器上的内存。

使用更快的硬盘驱动器或将数据条带化到多个硬盘驱动器。

切换到可以在多个 CPU 内核上并行执行相同查询的 RDBMS。

【讨论】:

感谢您对索引的澄清。关于加快进程的任何提示?插入速度非常快,0.00001 秒,因此即使删除索引也不会影响整体性能。您认为还有其他方法可以达到更好的选择性能吗? 谢谢,但目前没有一个提议的解决方案是可行的。我正在寻找 Mysql 级别的优化。 条带化:由于工作是在单个线程中完成的,条带化没有任何好处。 如果您受 I/O 限制,并行性将无济于事。 @AndreaOlivato 您要求我们提供的解决方案,什么对您可行或不可行不属于规定条件的一部分。【参考方案2】:

发生了什么

让我解释一下 MyISAM 索引在 这种 的情况下是如何工作的。有两种结构:

数据位于普通文件(.MYD)中。 每个索引(PRIMARY KEY 或辅助键)都位于该索引的 BTree 结构中(在 .MYI 文件中)。

IN( long list ) 将向下钻取 BTree 60K 次。每次,它都会在底部找到 .MYD 文件的偏移量。然后它将从 .MYD 文件中随机获取。这可能每行获取两个磁盘命中。 (我假设BTree的非叶子节点被快速缓存,所以不要计算。)

每个磁盘命中可能由于缓存而被避免。

如果索引小于key_buffer_size,那么BTree 看起来可能不必每次都访问磁盘。 要获取数据,它会要求操作系统从 .MYD 中读取数据,并让操作系统将其缓存在任何空闲空间中。此外,根据行的分散程度,“缓存”可能没有多大帮助。

由于这两个缓存区域是分开的,并且会争夺 RAM,因此我建议将 20% 的 RAM 用于 key_buffer,而将大部分 RAM 用于数据缓存。 (不知道您的详细信息,我不能说 20% 是否适合 您的 案例。)

当您添加 MEMORY 表时,您首先通过内存表查找要查找的 id 而不是直接在查询中查找,从而减慢了它的速度。但是,你说它跑得更快?这可能是由于缓存了从一个测试到下一个测试的变化。

此外,MEMORY 表从其他缓存中拿走了 RAM,但没有提供任何好处。

部分解决方案

一种加快查询速度的方法。让我稍微解释一下 InnoDB 及其PRIMARY KEY。 PK 与数据“聚集”在一起,这两个东西在磁盘上的同一个 BTree 中(然后缓存在 innodb_buffer_pool 中)。所以每行只有一个潜在磁盘命中。所以……

ALTER TABLE ... ENGINE=InnoDB;
shrink key_buffer_size and raise innodb_buffer_pool_size
SELECT ... IN ( 60K values )

您仍然会受到磁盘速度的影响,但它应该会更快。

其他

SSD 会比旋转驱动器更快。

PARTITIONIng、“条带化”、临时表和并行性都无济于事(在这种情况下)。

如果您不需要BIGINT 的大范围(占用8 个字节),请切换到INT UNSIGNED(4 个字节,0..4 亿)或MEDIUMINT UNSIGNED(0..16M)。这将缩小大部分内容,从而使它们更易于缓存,因此 I/O 更少,因此速度更快。

【讨论】:

嗨瑞克,非常感谢您的建议。磁盘已经是 SSD(我在 Digital Ocean 上)。如果可能的话,肯定会尝试从 bigint 到更小的东西(需要检查我的最大 id)。将跟进结果 不幸的是我的最高键是“4547505810”因此它有点高于INT并且不能使用它,需要坚持使用BIGINT。还有其他建议吗? 请提供SHOW CREATE TABLE 还有SHOW TABLE STATUS LIKE 'p_table'; 请在下方找到SHOW TABLE STATUS LIKE 'gender_1' \G Engine: MyISAM Version: 10 Row_format: Dynamic Rows: 23620723 Avg_row_length: 32 Data_length: 770509332 Max_data_length: 281474976710655 Index_length: 1120541696 Data_free: 0 Auto_increment: NULL Create_time: 2017-01-07 22:21:26 Update_time: 2017-02-01 16:52:21 Check_time: 2017-01-12 22:09:43 Collation: latin1_swedish_ci Checksum: NULL Create_options: Comment:

以上是关于临时表(内存引擎)和大型物理表(1.7GB myisam)之间的连接优化的主要内容,如果未能解决你的问题,请参考以下文章

DB2 将大型物理表与小型全局临时表连接起来

Mysql存储引擎Myisam小结

Mysql 数据库优化——分区和分表个人经验

表 '/mysql-tmp/#sql_78b5_0.MYI' 的密钥文件不正确;或“从存储引擎得到错误 x”

mysql存储引擎类型都有哪些

Innodb 和 MyIsam 两种存储引擎的文件存储结构