2

我有两个名为 table_1 (1GB) 和 reference (250Mb) 的表。

当我查询引用的交叉连接时,更新 table_1 需要 16 小时。我们为 XFS 更改了系统文件 EXT3,但仍然需要 16 小时。我做错了什么?

这是更新/交叉连接查询:

  mysql> UPDATE table_1 CROSS JOIN reference ON
  -> (table_1.start >= reference.txStart AND table_1.end <= reference.txEnd)
  -> SET table_1.name = reference.name;
  Query OK, 17311434 rows affected (16 hours 36 min 48.62 sec)
  Rows matched: 17311434  Changed: 17311434  Warnings: 0

这是 table_1 的 show create table 和参考:

    CREATE TABLE `table_1` (
     `strand` char(1) DEFAULT NULL,
     `chr` varchar(10) DEFAULT NULL,
     `start` int(11) DEFAULT NULL,
     `end` int(11) DEFAULT NULL,
     `name` varchar(255) DEFAULT NULL,
     `name2` varchar(255) DEFAULT NULL,
     KEY `annot` (`start`,`end`)
   ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;


   CREATE TABLE `reference` (
     `bin` smallint(5) unsigned NOT NULL,
     `name` varchar(255) NOT NULL,
     `chrom` varchar(255) NOT NULL,
     `strand` char(1) NOT NULL,
     `txStart` int(10) unsigned NOT NULL,
     `txEnd` int(10) unsigned NOT NULL,
     `cdsStart` int(10) unsigned NOT NULL,
     `cdsEnd` int(10) unsigned NOT NULL,
     `exonCount` int(10) unsigned NOT NULL,
     `exonStarts` longblob NOT NULL,
     `exonEnds` longblob NOT NULL,
     `score` int(11) DEFAULT NULL,
     `name2` varchar(255) NOT NULL,
     `cdsStartStat` enum('none','unk','incmpl','cmpl') NOT NULL,
     `cdsEndStat` enum('none','unk','incmpl','cmpl') NOT NULL,
     `exonFrames` longblob NOT NULL,
      KEY `chrom` (`chrom`,`bin`),
      KEY `name` (`name`),
      KEY `name2` (`name2`),
      KEY `annot` (`txStart`,`txEnd`)
   ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
4

5 回答 5

4

您应该索引table_1.startreference.txStarttable_1.endtablereference.txEnd字段:

ALTER TABLE `table_1` ADD INDEX ( `start` ) ;
ALTER TABLE `table_1` ADD INDEX ( `end` ) ;
ALTER TABLE `reference` ADD INDEX ( `txStart` ) ;
ALTER TABLE `reference` ADD INDEX ( `txEnd` ) ;
于 2011-07-04T02:42:53.737 回答
1

交叉连接是笛卡尔积,它可能是计算成本最高的东西之一(它们不能很好地扩展)。

对于i = 1到n的每个表T_i,交叉表T_1到T_n生成的行数是每个表的大小乘以其他表的大小,即

|T_1| * |T_2| * ... * |T_n|

假设每个表有 M 行,则计算交叉连接的最终成本为

M_1 * M_2 ... M_n = O(M^n)

这是连接中涉及的表数量的指数。

于 2011-07-04T02:51:15.327 回答
0

试试这个:

UPDATE table_1 SET
table_1.name = (
  select reference.name
  from reference
  where table_1.start >= reference.txStart
  and table_1.end <= reference.txEnd)
于 2011-07-04T02:45:31.193 回答
0

我看到该UPDATE声明有 2 个问题。

End字段没有索引。您拥有的复合索引 ( annot) 将仅用start于此查询中的字段。您应该按照 Emre 的建议添加它们:

ALTER TABLE `table_1` ADD INDEX ( `end` ) ;
ALTER TABLE `reference` ADD INDEX ( `txEnd` ) ;

其次,JOIN可能(并且可能确实)找到许多referencetable_1. 因此,其中一些(或全部)行table_1被更新,被更新了很多次。检查此查询的结果,看看它是否与您更新的行数相同 ( 17311434):

SELECT COUNT(*)
FROM table_1
  WHERE EXISTS
    ( SELECT *
      FROM reference
      WHERE table_1.start >= reference.txStart
        AND table_1.`end` <= reference.txEnd
    )

可以有其他方法来编写这个查询,但是PRIMARY KEY两个表上都没有 a 使得它更难。如果您在 上定义主键table_1,请尝试id使用主键替换。

更新:不,不要在有 34M 行的表上尝试。检查执行计划并首先尝试使用较小的表。

UPDATE table_1 AS t1
  JOIN 
    ( SELECT t2.id
           , r.name
      FROM table_1 AS t2
        JOIN
          ( SELECT name, txStart, txEnd
            FROM reference
            GROUP BY txStart, txEnd
          ) AS r
          ON  t2.start >= r.txStart
          AND t2.`end` <= r.txEnd
      GROUP BY t2.id
    ) AS good
    ON good.id = t1.id
SET t1.name = good.name;

您可以通过在等效的 SELECT 上运行 EXPLAIN 来检查查询计划:

EXPLAIN
SELECT t1.id, t1.name, good.name
FROM table_1 AS t1
  JOIN 
    ( SELECT t2.id
           , r.name
      FROM table_1 AS t2
        JOIN
          ( SELECT name, txStart, txEnd
            FROM reference
            GROUP BY txStart, txEnd
          ) AS r
          ON  t2.start >= r.txStart
          AND t2.`end` <= r.txEnd
      GROUP BY t2.id
    ) AS good
    ON good.id = t1.id ;
于 2011-07-04T09:13:19.413 回答
0

有人已经建议您添加一些索引。但我认为使用这两个索引可以获得的最佳性能:

ALTER TABLE `test`.`time` 
    ADD INDEX `reference_start_end` (`txStart` ASC, `txEnd` ASC),
    ADD INDEX `table_1_star_end` (`start` ASC, `end` ASC);

MySQL 查询只会使用其中一个,但 MySQL 会自动决定哪个更有用。

于 2011-07-06T07:41:00.993 回答