慢mysql查询/可能的查询问题

时间:2011-05-11 21:35:58

标签: mysql performance

我有2个(类似的)查询:

-- Query #1 - get all new products not in currently in the Product table
-- Should match any products in the temp table that do not exist in the Product table
INSERT
  INTO `tmpProductState` (`ProductId`, `ChangedOn`, `State`)
SELECT t.`ProductId`, t.`ProcessedOn`, \'Activated\'
  FROM `tmpImport` t
  LEFT JOIN `Product` p USING (`ProductId`)
 WHERE p.`ProductId` IS NULL
    ON DUPLICATE KEY UPDATE
       `State` = VALUES(`State`)

-- Query #2 - get all Products that are removed from the Product table
-- Should match any products in the Product table that do not exist in the temp table
INSERT
  INTO `tmpProductState` (`ProductId`, `ChangedOn`, `State`)
SELECT p.`ProductId`, p.`LastSeenDate`, \'Deactivated\'
  FROM `Product` p
  LEFT JOIN `tmpImport` t USING (`ProductId`)
 WHERE t.`ProductId` IS NULL
    ON DUPLICATE KEY UPDATE
       `State` = VALUES(`State`)

首次运行时(第1天),当Product表为空时,两个查询都在1秒内运行,但是,在第二次运行时(第2天),当Product表有14000条记录时,第一个查询运行在2秒内,第二个查询在244秒内运行。每个连续的数据导入是相同的(查询#2为240-250秒。在检查数据库时,所有数据似乎都是正确的,我只是无法弄清楚为什么第二个查询需要这么长时间。

--->编辑:慢查询日志:

# Query_time: 245.328784  Lock_time: 0.000000 Rows_sent: 0  Rows_examined: 187711973
SET timestamp=1305151558;

INSERT
  INTO `tmpProductState` (`ProductId`, `ChangedOn`, `State`)
SELECT p.`ProductId`, p.`LastSeenDate`, 'Deactivated'
  FROM `Product` p
  LEFT JOIN `tmpImport` t USING (`ProductId`)
 WHERE t.`ProductId` IS NULL
    ON DUPLICATE KEY UPDATE
       `State` = VALUES(`State`);

此时我最关心的是:Rows_examined:187711973(究竟是如何检查那么多行?)Product表大小是~14000条记录,导入表大小是~28000条记录,tmpProductState最多60条记录。

--->另一个编辑:

EXPLAIN SELECT p.`ProductId` , p.`LastSeenDate` , 'Deactivated'
FROM `Product` p
LEFT JOIN `tmpImport` t
USING ( `ProductId` )
WHERE t.`ProductId` IS NULL 

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE          p       ALL     NULL            NULL    NULL            NULL    14151
1   SIMPLE          t       index   NULL            PRIMARY 100             NULL    28166   Using where; Using index; Not exists

参与的表格:

CREATE TABLE IF NOT EXISTS `tmpImport` (
  `CategoryId`             smallint(5) unsigned NOT NULL,
  `ProcessedOn`            date DEFAULT NULL,
  `ProductId`              varchar(32) NOT NULL,
  `Title`                  varchar(255) DEFAULT NULL,
  `Description`            text,
  `ActivateDate`           date DEFAULT NULL,
  PRIMARY KEY (`CategoryId`,`ProductId`)
) ENGINE=MyISAM DEFAULT CHARSET = UTF8

CREATE TABLE IF NOT EXISTS `tmpProductState` (
  `ProductId` VARCHAR(32) NOT NULL,
  `ChangedOn` DATE NOT NULL,
  `State` ENUM(\'Activated\',\'Deactivated\'),
  PRIMARY KEY(`ProductId`,`ChangedOn`)
) ENGINE = Memory

CREATE TABLE `Product` (
  `ProductId` varchar(32) NOT NULL,
  `Title` varchar(255) DEFAULT NULL,
  `Description` text,
  `ActivateDate` date DEFAULT NULL,
  `LastSeenDate` date DEFAULT NULL,
  PRIMARY KEY (`ProductId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

2 个答案:

答案 0 :(得分:0)

您的表没有规范化,您没有有效的索引,并且您的联接是......不寻常。

我假设您无法对表间的数据重复进行任何操作,因此请忽略它。

看起来您要在要加入的表中的列之间复制数据,您应该使用连接中的所有列。所以应该是:

LEFT JOIN `tmpImport` t USING (`ProductId`, `Title`, `Description`, `ActivateDate`)

向表中添加与您要加入或过滤的字段对应的索引。请勿使用复合键作为主键。而是将自动递增字段添加为PK,并在需要强制唯一性时使用唯一键。 product表和tmpImport都应该为每个要连接的列提供键。

希望其中一些想法可以帮助你。

答案 1 :(得分:0)

很晚才回复此问题,但您的第一个查询是从 tmpImport 获取所有记录,并使用Product表上的主键从 Product 获取所有记录。这非常有效。第二个查询是从 Product 获取所有记录,然后从 tmpImport 获取匹配的记录,但没有 ProductId 上任何索引的好处< EM> tmpImport 。因此运行得很糟糕。

ProductId 上的索引添加到 tmpImport 表中(此键的主键中的 ProductId 将被忽略,因为它不是密钥中的第一列,您没有使用 CategoryId ,这是第一列。