【问题标题】:MySQL join on table with partitions selecting all partitionsMySQL连接表,分区选择所有分区
【发布时间】:2014-11-04 18:37:59
【问题描述】:

我的网站上有一个照片库,里面有 100 万张照片。有 2 个与之关联的搜索表。表 #1 包含照片中使用的单词列表。表 #2 包含哪些单词与哪些照片匹配的列表。表 #2 是 7M 行。我正在测试对这个 7M 行表进行分区,因为我有另一组包含 120,000,000 行的表。查询下面的 120M 行 wordmatch 表,无论是否再次连接下面的 wordlist 表,都需要几秒钟才能运行。

我正在尝试在这 2 个表之间执行连接,并且 MySQL 5.6 EXPLAIN PARTITIONS 显示它正在使用所有分区。如何重做此查询以使其正确使用仅一个分区?

两张桌子:

CREATE TABLE wordlist (
  word_text varchar(50) NOT NULL DEFAULT '',
  word_id mediumint(8) unsigned NOT NULL AUTO_INCREMENT
  PRIMARY KEY (word_text),
  KEY word_id (word_id)
) ENGINE=InnoDB

CREATE TABLE wordmatch (
  pic_id int(11) unsigned NOT NULL DEFAULT '0',
  word_id mediumint(8) unsigned NOT NULL DEFAULT '0',
  title_match tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (word_id,pic_id,title_match),
  KEY pic_id (pic_id)
) ENGINE=InnoDB
/*!50100 PARTITION BY HASH (word_id)
PARTITIONS 11 */;

我正在执行的 SQL 查询:

EXPLAIN PARTITIONS SELECT m.pic_id FROM wordlist w, wordmatch m WHERE w.word_text LIKE 'bacon' AND m.word_id = w.word_id 
+----+-------------+-------+-----------------------------------+-------+-----------------+---------+---------+----------------------------+------+-------------+
| id | select_type | table | partitions                        | type  | possible_keys   | key     | key_len | ref                        | rows | Extra       |
+----+-------------+-------+-----------------------------------+-------+-----------------+---------+---------+----------------------------+------+-------------+
|  1 | SIMPLE      | w     | NULL                              | range | PRIMARY,word_id | PRIMARY | 52      | NULL                       |    1 | Using where |
|  1 | SIMPLE      | m     | p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10 | ref   | PRIMARY         | PRIMARY | 3       | w.word_id                  |   34 | Using index |
+----+-------------+-------+-----------------------------------+-------+-----------------+---------+---------+----------------------------+------+-------------+

连接产生一个使用所有分区的查询。 如果我先检索 word_id # 并直接对照 wordmatch 表,一切正常:

EXPLAIN PARTITIONS SELECT m.pic_id FROM wordmatch m WHERE m.word_id = 219657;
+----+-------------+-------+------------+------+---------------+---------+---------+-------+-------+-------------+
| id | select_type | table | partitions | type | possible_keys | key     | key_len | ref   | rows  | Extra       |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+-------+-------------+
|  1 | SIMPLE      | m     | p9         | ref  | PRIMARY       | PRIMARY | 3       | const | 18220 | Using index |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+-------+-------------+

如何让它正常工作? 如果可能,我不希望将其拆分为多个查询。 您可能已经注意到我在上面使用了 LIKE。人们经常会搜索 bacon% 以获取单词的复数等。 示例:

SELECT m.pic_id FROM wordlist w, wordmatch m WHERE w.word_text LIKE 'bacon%' AND m.word_id = w.word_id

我意识到这种通配符搜索可能会导致选择 2 个或更多分区。这可能没问题,但如果有办法更改分区以防止这种情况发生,我欢迎任何提示。

编辑 #1: 添加了详细信息,因为我最初的问题令人困惑。在做我的 120M 行表之前,我先测试了我的 7M 行表。

编辑#2:解决我的整体问题:我的性能问题似乎得到了解决,因为我根据这篇文章将我的 120M 行表划分为 101 个分区:MySQL performance: partitions我不知道 MySQL 是否在运行时反对所有分区 - Ollie Jones 说它不在下面的 cmets 中并且 EXPLAIN PARTITIONS 不正确 - 但现在它很快,所以我很高兴。

【问题讨论】:

  • 只是一个评论:我相信七兆行不足以证明对您描述的表进行分区是合理的。如果对表进行分区,您将承担或多或少的永久系统管理员负担和查询开销负担。我可以建议您花精力为wordmatch 表编制索引吗?
  • 谢谢。我会更改或添加哪些索引?我应该提到,在我对另一个有 1.2 亿行的表做同样的事情之前,我实际上是在使用这个表作为测试。

标签: mysql database-partitioning


【解决方案1】:

在深入研究分区项目之前,让您的查询使用高效的索引可能是一个好主意。这是您的查询重构为使用JOIN

SELECT m.pic_id 
  FROM wordlist w
  JOIN wordmatch m ON w.word_id = m.word_id
 WHERE w.word_text LIKE 'bacon%' 

此查询可以在wordlist (word_test, word_id) 上使用复合索引。它将随机访问第一个匹配的word_text 的索引,然后扫描索引以检索word_id 值,直到找到最后一个匹配的`word_text。

它还可以使用您在wordmatch (word_id, pic_id) 上的现有主键它加快了您的查询速度,因为数据库引擎可以直接从索引满足您的查询,而无需将硬盘驱动器来回敲击到表本身。

所以,试试这些索引吧。您的大表wordmatch 应该在没有分区的情况下工作得很好。对包含大量内容(如文章文本)的表进行分区比对这种固定行大小的连接表进行分区更为常见。

请注意,您的EXPLAIN 宣布它将查看所​​有分区,因为EXPLAIN 无法确定您的w.word_text LIKE 'bacon%' WHERE 子句需要检查哪个(或多个)分区。 EXPLAIN 不像一盒锤子那么笨,但很接近。 MySQL 不会检查它不需要的分区,但它直到运行时才知道涉及哪些分区。

您是否考虑过使用 FULLTEXT 搜索?它可能会简化你正在做的事情。

【讨论】:

  • 好答案。在解释替代选项方面做得比我的做得更好(特别是在 FULLTEXT 索引上的建议,我只能在我的答案中来回使用 cmets)。这对后代来说是更好的答案。
  • @Ollie 谢谢,我更改了 wordlist 的索引。现在使用 120M 表 - 不幸的是没有速度变化。问题: 1. 此索引更改与仅word_text 有何不同?我不需要对 word_ids 进行排序,并且单个 word_text 索引已经将每个单词组合在一起,对吗? 2.您使用JOIN 重新编写的查询与我正在执行的, 连接没有什么不同,对吗?是的,我正在考虑将 FULLTEXT 搜索作为另一种选择。我不知道 EXPLAIN 与运行时的差异,所以也谢谢你。
【解决方案2】:

您的第一个查询在wordmatch 表上没有任何可能限制正在使用的分区的过滤条件,因此它需要访问所有分区。如果不对作为分区基础的字段添加过滤器 (word_id),则无法重做此查询以仅使用必要的分区。

第二个查询过滤特定的word_id 值,因此索引确切知道要指向哪个分区。

我也同意@OllieJones 的评论,我不确定您是否真的应该担心分区只有 7M 行。在事物的宏伟架构中,这并不是一张那么大的表。

【讨论】:

  • 哇,所以查询执行计划器不知道要先查找 wordlist.word_id 才能知道要为 wordmatch.word_id 使用哪个分区。所以唯一的解决方案是将其拆分为多个查询,对吗?在对 wordmatch 包含 1.2 亿行的另一组表执行相同操作之前,我将这组表作为测试。我将编辑原始帖子。
  • 从7M的表中检索记录有时会出现一些延迟,可能与大小无关。我可以运行一个查询,第一个查询需要 7-8 秒,但下一个查询需要 0.008 秒,就像它必须将其加载到内存中或以某种方式加速一样?我有 9GB 分配给 INNODB,其中 INNODB 表总共为 7G,所以它不应该是内存问题。欢迎任何建议。
  • @63bus 在规划阶段,规划器还不知道哪个 word_id 将匹配用于w.word_text 的标准,因此它不知道需要哪些分区。
  • @63bus 很多这可能与您的搜索条件有关。如果您使用诸如LIKE 'bacon'(实际上与= 'bacon' 相同)或LIKE 'bacon%' 之类的东西,则可以使用word_text 上的索引。如果您使用LIKE '%bacon%',那么您将根本无法使用索引,因为索引匹配从此类字段的字符串开头开始。您可能会遇到慢查询,然后是快查询,因为查询第二次命中查询缓存。
  • @63bus 对于此类问题,您可以考虑使用自然语言搜索。链接到自然语言搜索文档 - dev.mysql.com/doc/refman/5.6/en/fulltext-natural-language.html。通过这种方法,您可以使用更适合此类查询的 FULLTEXT 索引。
猜你喜欢
  • 2019-11-18
  • 1970-01-01
  • 1970-01-01
  • 2019-06-08
  • 2015-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多