加速MYSQL中的自我加入

时间:2013-11-11 15:31:56

标签: mysql sql innodb inner-join

我有一个不同对象之间的连接表,我基本上尝试使用自连接进行图遍历。 我的表定义为:

CREATE TABLE `connections` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `position` int(11) NOT NULL,
  `dId` bigint(20) NOT NULL,
  `sourceId` bigint(20) NOT NULL,
  `targetId` bigint(20) NOT NULL,
  `type` bigint(20) NOT NULL,
  `weight` float NOT NULL DEFAULT '1',
  `refId` bigint(20) NOT NULL,
  `ts` bigint(20) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `sourcetype` (`type`,`sourceId`,`targetId`),
  KEY `targettype` (`type`,`targetId`,`sourceId`),
  KEY `complete` (`dId`,`sourceId`,`targetId`,`type`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

该表包含大约3M条目(类型1~1K,类型2 1M,类型3M)。

超过2或3跳的查询实际上非常快(当然接收所有结果需要一段时间),但获得3跳的查询计数非常慢(> 30秒)。

这里是查询(返回2M):

SELECT
  count(*)
FROM
  `connections` AS `t0`
JOIN
  `connections` AS `t1` ON `t1`.`targetid`=`t0`.`sourceid`
JOIN
  `connections` AS `t2` ON `t2`.`targetid`=`t1`.`sourceid`
WHERE
  `t2`.dId = 1
  AND
  `t2`.`sourceid` = 1
  AND
  `t2`.`type` = 1
  AND
  `t1`.`type` = 2
  AND
  `t0`.`type` = 3;

这里是相应的EXPLAIN:

id  select_type  table  type  possible_keys                   key         key_len  ref                         rows  Extra  
1   SIMPLE       t2     ref   targettype,complete,sourcetype  complete    16       const,const                  100  Using where; Using index
1   SIMPLE       t1     ref   targettype,sourcetype           targettype   8       const                       2964  Using where; Using index
1   SIMPLE       t0     ref   targettype,sourcetype           sourcetype  16       const,travtest.t1.targetId  2964  Using index

编辑:这是在向type添加和索引后的EXPLAIN:

id  select_type  table  type  possible_keys                        key         key_len  ref                         rows  Extra     
1   SIMPLE       t2     ref   type,complete,sourcetype,targettype  complete    16       const,const                 100   Using where; Using index
1   SIMPLE       t1     ref   type,sourcetype,targettype           sourcetype  16       const,travtest.t2.targetId    2   Using index
1   SIMPLE       t0     ref   type,sourcetype,targettype           sourcetype  16       const,travtest.t1.targetId    2   Using index

有没有办法改善这个?

第二次编辑:

EXPLAN EXTENDED:
+----+-------------+-------+------+-------------------------------------+------------+---------+----------------------------+------+----------+--------------------------+
| id | select_type | table | type | possible_keys                       | key        | key_len | ref                        | rows | filtered | Extra                    |
+----+-------------+-------+------+-------------------------------------+------------+---------+----------------------------+------+----------+--------------------------+
|  1 | SIMPLE      | t2    | ref  | type,complete,sourcetype,targettype | complete   | 16      | const,const                |  100 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | t1    | ref  | type,sourcetype,targettype          | sourcetype | 16      | const,travtest.t2.targetId |    1 |   100.00 | Using index              |
|  1 | SIMPLE      | t0    | ref  | type,sourcetype,targettype          | sourcetype | 16      | const,travtest.t1.targetId |    1 |   100.00 | Using index              |
+----+-------------+-------+------+-------------------------------------+------------+---------+----------------------------+------+----------+--------------------------+

SHOW WARNINGS;
+-------+------+--------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                    |
+-------+------+--------------------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select count(0) AS `count(*)` from `travtest`.`connections` `t0`            |
|       |      | join `travtest`.`connections` `t1` join `travtest`.`connections` `t2`                      |
|       |      | where ((`travtest`.`t0`.`sourceId` = `travtest`.`t1`.`targetId`) and                       |
|       |      | (`travtest`.`t1`.`sourceId` = `travtest`.`t2`.`targetId`) and (`travtest`.`t0`.`type` = 3) |
|       |      | and (`travtest`.`t1`.`type` = 2) and (`travtest`.`t2`.`type` = 1) and                      |
|       |      | (`travtest`.`t2`.`sourceId` = 1) and (`travtest`.`t2`.`dId` = 1))                          |
+-------+------+--------------------------------------------------------------------------------------------+

4 个答案:

答案 0 :(得分:1)

sourceidtargetidtype列创建索引,然后尝试使用此查询:

SELECT
  count(*)
FROM
  `connections` AS `t0`
JOIN
  `connections` AS `t1` ON `t1`.`targetid`=`t0`.`sourceid` and `t1`.`type` = 2
JOIN
  `connections` AS `t2` ON `t2`.`targetid`=`t1`.`sourceid` and `t2`.dId = 1 AND `t2`.`sourceid` = 1 AND `t2`.`type` = 1
WHERE
  `t0`.`type` = 3;

------- ----- UPDATE

我认为那些指数是正确的,并且通过这些大表,您可以获得最佳优化。我不认为您可以使用表分区/分片等其他优化来改进此查询。

如果这些数据不经常更改或我看到的唯一方法是垂直缩放,您可以实现某种缓存

答案 1 :(得分:1)

可能你正在处理图表数据,对不对?

您的3跳查询有一点优化机会。它是浓密的树。建立了很多联系。我认为JOIN订单和INDEX是对的。

EXPLAIN告诉我t2产生大约100个targetId。如果你从连接中删除t2并添加t1.sourceId IN (100 targetId)。这将需要3次自我加入。

但是如何将100个目标分解为10个子IN列表。如果这减少响应时间,多线程一次运行10个查询。

MySQL没有平行的特质。所以你要自己做。

你是否尝试过像jena,芝麻这样的图形数据库?我不确定graph datdabase比MYSQL快。

答案 2 :(得分:1)

这不是答案。仅供参考。

如果MySQL或其他数据库对您来说很慢,您可以实现自己的图形数据库。那么这篇论文Literature Survey of Graph Databases [1]是关于图数据库的一个很好的工作。调查了几个图形数据库,让我们了解了很多技术。

SCALABLE SEMANTIC WEB DATA MANAGEMENT USING VERTICAL PARTITIONING [2]引入垂直分区但是,你的3M边缘不大,垂直分区无法帮助你。 [2]介绍了另一个概念Materialized Path Expressions。我认为这可以帮到你。

[1] http://www.systap.com/pubs/graph_databases.pdf

[2] http://db.csail.mit.edu/projects/cstore/abadirdf.pdf

答案 3 :(得分:0)

您评论您的查询返回2M(假设您的意思是200万)是最终计数,或者只是它通过200万。您的查询似乎是专门查找连接到其他表的单个T2.ID,源和类型,但从连接0开始。

我会删除你现有的索引并拥有以下内容,因此引擎不会尝试使用其他引擎并导致它加入的混乱。此外,通过在两者中都具有目标ID(正如您已经拥有)意味着这些将覆盖索引,并且引擎不必转到页面上的实际原始数据以确认任何其他条件或从中提取值。

唯一的索引是基于您的最终标准,如T2中的来源,类型和ID。由于targetID(索引的一部分)是菊花链中下一个的源,因此您对链上的源和类型使用相同的索引。对任何索引都没有混淆

INDEX ON(sourceId,type,dId,targetid)

我会尝试通过逆转到最小的设置并努力工作......像

这样的东西
SELECT
      COUNT(*)
   FROM
      `connections` t2
         JOIN `connections` t1
            ON t2.targetID = t1.sourceid
            AND t1.`type` = 2
            JOIN `connections` t0
               ON t1.targetid = t0.sourceid
               AND t0.`type` = 3
   where
          t2.sourceid = 1
      AND t2.type = 1
      AND t2.dID = 1