2010-05-28 39 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

相關問題