2010-05-28 109 views
1

偶尔,随机间隔,我们的网站完全瘫痪。MySQL查询瘫痪网站

看着SHOW FULL PROCESSLIST;,我注意到,当出现这种情况,有可能是“Copying to tmp table”的过长...时间(有时350秒)特定的查询,几乎所有的查询都是“Locked”。

我不明白的部分是,90%的时间,这个查询运行良好。我看到它在进程列表中出现,大部分时间都很快完成。 此查询正在我们的主页上通过ajax调用,根据您的浏览历史记录(亚马逊)显示产品推荐。

有时,随机(但是太频繁),它会卡在“复制到tmp表”中。

下面是该查询的实例钓到这是增长109秒的时候,我看了看:

SELECT DISTINCT product_product.id, product_product.name, product_product.retailprice, product_product.imageurl, product_product.thumbnailurl, product_product.msrp 
FROM product_product, product_xref, product_viewhistory 
WHERE 
(
(product_viewhistory.productId = product_xref.product_id_1 AND product_xref.product_id_2 = product_product.id) 
OR 
(product_viewhistory.productId = product_xref.product_id_2 AND product_xref.product_id_1 = product_product.id) 
) 
AND product_product.outofstock='N' 
AND product_viewhistory.cookieId = '188af1efad392c2adf82' 
AND product_viewhistory.productId IN (24976, 25873, 26067, 26073, 44949, 16209, 70528, 69784, 75171, 75172) 
ORDER BY product_xref.hits DESC 
LIMIT 10 

当然的“Cookie编号”,并动态根据请求“的productId”修改名单。

我使用php与PDO。

编辑:我想通了一些涉及到的表结构可能会有所帮助:

CREATE TABLE IF NOT EXISTS `product_viewhistory` (
    `userId` int(10) unsigned NOT NULL default '0', 
    `cookieId` varchar(30) collate utf8_unicode_ci NOT NULL, 
    `productId` int(11) NOT NULL, 
    `viewTime` timestamp NOT NULL default CURRENT_TIMESTAMP, 
    KEY `userId` (`userId`), 
    KEY `cookieId` (`cookieId`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

CREATE TABLE IF NOT EXISTS `product_xref` (
    `id` int(11) NOT NULL auto_increment, 
    `product_id_1` int(11) default NULL, 
    `product_id_2` int(11) default NULL, 
    `hits` int(11) NOT NULL default '0', 
    PRIMARY KEY (`id`), 
    KEY `IDX_PROD1` (`product_id_1`), 
    KEY `IDX_PROD2` (`product_id_2`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=184531 ; 

CREATE TABLE IF NOT EXISTS `product_product` (
    `id` int(11) NOT NULL auto_increment, 
    `supplierid` int(11) NOT NULL default '0', 
    `suppliersku` varchar(100) NOT NULL default '', 
    `name` varchar(100) NOT NULL default '', 
    `cost` decimal(10,2) NOT NULL default '0.00', 
    `retailprice` decimal(10,2) NOT NULL default '0.00', 
    `weight` decimal(10,2) NOT NULL default '0.00', 
    `imageurl` varchar(255) NOT NULL default '', 
    `thumbnailurl` varchar(255) NOT NULL default '', 
    `sizechartlink` varchar(255) NOT NULL default '', 
    `content` text NOT NULL, 
    `remark` varchar(100) NOT NULL default '', 
    `colorchartlink` varchar(255) default NULL, 
    `outofstock` char(1) NOT NULL default '', 
    `summary` text NOT NULL, 
    `freehandoutlink` varchar(255) default NULL, 
    `msrp` decimal(10,2) default NULL, 
    `enabled` tinyint(1) NOT NULL default '1', 
    `sales_score` float NOT NULL default '0', 
    `sales_score_offset` float NOT NULL default '0', 
    `date_added` timestamp NULL default CURRENT_TIMESTAMP, 
    `brand` varchar(255) default NULL, 
    `tag_status` varchar(20) default NULL, 
    PRIMARY KEY (`id`), 
    KEY `product_retailprice_idx` (`retailprice`), 
    KEY `suppliersku` (`suppliersku`), 
    FULLTEXT KEY `product_name_summary_ft` (`name`,`summary`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1; 

另外,根据要求,结果的解释道:

+----+-------------+---------------------+------+---------------------+----------+---------+-------+-------+------------------------------------------------+ 
| id | select_type | table    | type | possible_keys  | key  | key_len | ref | rows | Extra           | 
+----+-------------+---------------------+------+---------------------+----------+---------+-------+-------+------------------------------------------------+ 
| 1 | SIMPLE  | product_xref  | ALL | IDX_PROD1,IDX_PROD2 | NULL  | NULL | NULL | 30035 | Using temporary; Using filesort    | 
| 1 | SIMPLE  | product_viewhistory | ref | cookieId   | cookieId | 92  | const | 682 | Using where         | 
| 1 | SIMPLE  | product_product  | ALL | PRIMARY    | NULL  | NULL | NULL | 31880 | Range checked for each record (index map: 0x1) | 
+----+-------------+---------------------+------+---------------------+----------+---------+-------+-------+------------------------------------------------+ 
3 rows in set (0.00 sec) 
当我意识到我做了

新的更新版本根本不需要product_viewhistory。我是从旧的代码左起:

SELECT DISTINCT product_product.id, product_product.name, product_product.retailprice, product_product.imageurl, product_product.thumbnailurl, product_product.msrp 
FROM product_product, product_xref 
WHERE 
(
(product_xref.product_id_1 IN (24976, 25873, 26067, 26073, 44949, 16209, 70528, 69784, 75171, 75172) AND product_xref.product_id_2 = product_product.id) 
OR 
(product_xref.product_id_2 IN (24976, 25873, 26067, 26073, 44949, 16209, 70528, 69784, 75171, 75172) AND product_xref.product_id_1 = product_product.id) 
) 
AND product_product.outofstock='N' 
ORDER BY product_xref.hits DESC 
LIMIT 10 

而新的解释:

+----+-------------+-----------------+-------------+---------------------+---------------------+---------+------+-------+-------------------------------------------------------------------------------------+ 
| id | select_type | table   | type  | possible_keys  | key     | key_len | ref | rows | Extra                    | 
+----+-------------+-----------------+-------------+---------------------+---------------------+---------+------+-------+-------------------------------------------------------------------------------------+ 
| 1 | SIMPLE  | product_xref | index_merge | IDX_PROD1,IDX_PROD2 | IDX_PROD1,IDX_PROD2 | 5,5  | NULL | 32 | Using sort_union(IDX_PROD1,IDX_PROD2); Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | product_product | ALL   | PRIMARY    | NULL    | NULL | NULL | 31880 | Range checked for each record (index map: 0x1)          | 
+----+-------------+-----------------+-------------+---------------------+---------------------+---------+------+-------+-------------------------------------------------------------------------------------+ 
2 rows in set (0.00 sec) 
+0

注意它是如何显示“可能的键”,但在“键”中它说“NULL”。为什么?? – 2010-05-28 17:41:47

+0

确实存在问题。 'product_xref'和'product_product'表在这个查询中没有使用索引,导致扫描超过30000行(每个!)'临时使用;在EXPLAIN outout中使用filesort'通常是一个红旗,如果可能的话应该检查并修复它。 不幸的是,我不是这方面的大师,所以希望这里的其他人能够指出你正确的方向,只要你需要采取的实际步骤。您可能还想检查谷歌。使用EXPLAIN优化MySQL查询的信息很多。 – x1a4 2010-05-28 17:43:06

+0

好吧,我认为我刚刚查询了很多,因为我意识到我不需要product_viewhistory表。这是多余的,从以前的版本留下的代码... – 2010-05-28 18:07:13

回答

1

做的第一件事就是看看MySQL是引擎盖下做与EXPLAIN,然后再从那里。这听起来像你有一些索引要做。

+0

EXPLAIN * may * help,但是如果它大部分时间运行良好,它可能不是单独的查询结构。 – 2010-05-28 17:32:35

+0

谢谢。我用结果更新了我的问题。我也看看所有的条件,并检查是否有每个索引。唯一没有索引的是“AND product_product.outofstock ='N'”。 – 2010-05-28 17:33:14

+0

这就是为什么我说*首先*的东西:) – x1a4 2010-05-28 17:35:26

0

您需要优化您的查询。从mysql提示符或mysql客户端运行它EXPLAIN并检查执行计划。您可能需要为表格添加索引。请记住,如果连续运行该查询几次 ,mysql服务器将缓存结果,并且不应该依赖它们的快速执行时间。也许这就是为什么你的查询在90%的时间内正常运行的原因。

+0

请参阅已编辑的关于EXPLAIN结果的问题。我连续运行几次,但输入不同。有关系吗? – 2010-05-28 17:39:48

+0

连续几次,我也意味着不同的访客。 – 2010-05-28 17:40:28

+0

如果使用不同的输入,通常它也会缓存结果。例如,第一次outofstock ='N' - 慢,第二次outofstock ='Y' - 慢,第三次 - outofstock ='N' - 快(从第一次执行缓存)。 顺便说一下,我只是看看你的CREATE TABLE,注意到'product_viewhistory'没有主键。你应该添加它。 – a1ex07 2010-05-28 17:51:34

1

我改写了您的查询为:

SELECT DISTINCT 
      pp.id, 
      pp.name, 
      pp.retailprice, 
      pp.imageurl, 
      pp.thumbnailurl, 
      pp.msrp 
    FROM PRODUCT_PRODUCT pp 
LEFT JOIN PRODUCT_XREF px1 ON px1.product_id_2 = pp.id 
LEFT JOIN PRODUCT_XREF px2 ON px2.product_id_1 = pp.id 
    WHERE EXISTS(SELECT NULL 
        FROM PRODUCT_VIEWHISTORY pvh 
        WHERE pvh.productid = px1.product_id_1 
        AND pvh.cookieId = '188af1efad392c2adf82' 
        AND pvh.productId IN (24976, 25873, 26067, 26073, 44949, 16209, 70528, 69784, 75171, 75172)) 
     OR EXISTS(SELECT NULL 
        FROM PRODUCT_VIEWHISTORY pvh 
        WHERE pvh.productid = px2.product_id_2 
        AND pvh.cookieId = '188af1efad392c2adf82' 
        AND pvh.productId IN (24976, 25873, 26067, 26073, 44949, 16209, 70528, 69784, 75171, 75172)) 
     AND pp.outofstock = 'N' 
ORDER BY GREATEST(px1.hits, px2.hits) DESC 
    LIMIT 10 

它会一直,如果ORDER BY没有依靠PRODUCT_XREF.hits列更容易。太糟糕了MySQL不支持Common Table Expressions(CTE)/ Subquery Factoring ...

有两个不同的product_id引用是一个非常可疑的方法。我建议查看数据模型。

+0

是不是“存在”缓慢? – 2010-05-28 17:47:34

+0

@nute:来自寻求帮助的人提出的一个有趣的问题,这个问题已经使他们的网站停止了。你可以随时测试它,并与你现有的查询进行比较...... – 2010-05-28 18:16:45

+0

根据“解释”,它归结为同样的事情。当然,不需要product_viewhistory使它更快。但是在任何情况下,“product_product”仍然显示为“ALL”,它必须遍历所有行。 – 2010-05-28 18:31:38