MySQL连接在表上,分区选择所有分区

时间:2014-11-04 18:37:59

标签: mysql database-partitioning

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

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

2个表:

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。人们经常搜索培根%以获得复数词等。 例如:

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说它不在下面的评论中,并且EXPLAIN PARTITIONS不正确 - 但它现在很快,所以我很高兴。

2 个答案:

答案 0 :(得分:2)

在深入分区项目之前,让您的查询使用高效索引可能是一个好主意。这是您的查询重构为使用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搜索?它可以简化您正在做的事情。

答案 1 :(得分:0)

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

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

我也同意@OllieJones的评论,我不确定你真的应该担心只有7M行的分区。这在事物的宏观模式中并不是那么大的表格。