2017-09-15 65 views
2

我有這樣的選擇查詢的ItemType爲varchar類型和ItemComments很慢爲int類型:MySQL的選擇查詢獲取與兩種情況和降序

select * from ItemInfo where ItemType="item_type" order by ItemComments desc limit 1 

你可以看到這個查詢有3個條件

  1. 其中'ItemType'等於特定值;
  2. 爲了用降序排列

有趣的是,當我選擇與所有三個條件的行,它變得非常緩慢「ItemComments」

  • 。但是,如果我放棄三個中的任何一個(條件2除外),則查詢運行速度非常快。請參閱:

    select * from ItemInfo where ItemType="item_type" order by ItemComments desc limit 1; 
    /* Affected rows: 0 Found rows: 1 Warnings: 0 Duration for 1 query: 16.318 sec. */ 
    
    select * from ItemInfo where ItemType="item_type" order by ItemComments limit 1; 
    /* Affected rows: 0 Found rows: 1 Warnings: 0 Duration for 1 query: 0.140 sec. */ 
    
    select * from ItemInfo order by ItemComments desc limit 1; 
    /* Affected rows: 0 Found rows: 1 Warnings: 0 Duration for 1 query: 0.015 sec. */ 
    

    另外,

    1. 我使用MySQL 5.7 InnoDB引擎。
    2. 我已經在ItemType和ItemComments上創建了索引,而表ItemInfo包含2百萬行。

    我已經搜索了許多可能的解釋,如MySQL支持降序索引,複合索引等。但是這些仍然不能解釋爲什麼查詢#1運行緩慢而查詢#2和#3運行良好。

    如果有人能幫助我,我將非常感激。

    更新:create table和解釋信息

    創建代碼:

    CREATE TABLE `ItemInfo` (
    `ItemID` VARCHAR(255) NOT NULL, 
    `ItemType` VARCHAR(255) NOT NULL, 
    `ItemPics` VARCHAR(255) NULL DEFAULT '0', 
    `ItemName` VARCHAR(255) NULL DEFAULT '0', 
    `ItemComments` INT(50) NULL DEFAULT '0', 
    `ItemScore` DECIMAL(10,1) NULL DEFAULT '0.0', 
    `ItemPrice` DECIMAL(20,2) NULL DEFAULT '0.00', 
    `ItemDate` DATETIME NULL DEFAULT '1971-01-01 00:00:00', 
    PRIMARY KEY (`ItemID`, `ItemType`), 
    INDEX `ItemDate` (`ItemDate`), 
    INDEX `ItemComments` (`ItemComments`), 
    INDEX `ItemType` (`ItemType`) 
    ) 
    COLLATE='utf8_general_ci' 
    ENGINE=InnoDB; 
    

    解釋結果:

    mysql> explain select * from ItemInfo where ItemType="item_type" order by ItemComments desc limit 1; 
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+ 
    | id | select_type | table | partitions | type | possible_keys | key   | key_len | ref | rows | filtered | Extra  | 
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+ 
    | 1 | SIMPLE  | i  | NULL  | index | ItemType  | ItemComments | 5  | NULL | 83 |  1.20 | Using where | 
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+ 
    
    mysql> explain select * from ItemInfo where ItemType="item_type" order by ItemComments limit 1; 
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+ 
    | id | select_type | table | partitions | type | possible_keys | key   | key_len | ref | rows | filtered | Extra  | 
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+ 
    | 1 | SIMPLE  | i  | NULL  | index | ItemType  | ItemComments | 5  | NULL | 83 |  1.20 | Using where | 
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+ 
    
    mysql> explain select * from ItemInfo order by ItemComments desc limit 1; 
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------+ 
    | id | select_type | table | partitions | type | possible_keys | key   | key_len | ref | rows | filtered | Extra | 
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------+ 
    | 1 | SIMPLE  | i  | NULL  | index | NULL   | ItemComments | 5  | NULL | 1 | 100.00 | NULL | 
    +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------+ 
    

    從O.查詢瓊斯

    mysql> explain 
        -> SELECT a.* 
        ->  FROM ItemInfo a 
        ->  JOIN (
        ->    SELECT MAX(ItemComments) ItemComments, ItemType 
        ->    FROM ItemInfo 
        ->    GROUP BY ItemType 
        ->   ) maxcomm ON a.ItemType = maxcomm.ItemType 
        ->     AND a.ItemComments = maxcomm.ItemComments 
        ->  WHERE a.ItemType = 'item_type'; 
    +----+-------------+------------+------------+-------+----------------------------------------+-------------+---------+---------------------------+---------+----------+--------------------------+ 
    | id | select_type | table  | partitions | type | possible_keys       | key   | key_len | ref      | rows | filtered | Extra     | 
    +----+-------------+------------+------------+-------+----------------------------------------+-------------+---------+---------------------------+---------+----------+--------------------------+ 
    | 1 | PRIMARY  | a   | NULL  | ref | ItemComments,ItemType     | ItemType | 767  | const      | 27378 | 100.00 | Using where    | 
    | 1 | PRIMARY  | <derived2> | NULL  | ref | <auto_key0>       | <auto_key0> | 772  | mydb.a.ItemComments,const |  10 | 100.00 | Using where; Using index | 
    | 2 | DERIVED  | ItemInfo | NULL  | index | PRIMARY,ItemDate,ItemComments,ItemType | ItemType | 767  | NULL      | 2289466 | 100.00 | NULL      | 
    +----+-------------+------------+------------+-------+----------------------------------------+-------------+---------+---------------------------+---------+----------+--------------------------+ 
    

    我不確定我是否執行此查詢是正確的,但我無法在相當長的時間內獲取記錄。

    查詢來自維傑。但我想補充的ItemType連接條件的原因,只有max_comnt返回來自其他ItemType的項目:

    SELECT ifo.* FROM ItemInfo ifo 
    JOIN (SELECT ItemType, MAX(ItemComments) AS max_comnt FROM ItemInfo WHERE ItemType="item_type") inn_ifo 
    ON ifo.ItemComments = inn_ifo.max_comnt and ifo.ItemType = inn_ifo.ItemType 
    /* Affected rows: 0 Found rows: 1 Warnings: 0 Duration for 1 query: 7.441 sec. */ 
    
    explain result: 
    +----+-------------+------------+------------+-------------+-----------------------+-----------------------+---------+-------+-------+----------+-----------------------------------------------------+ 
    | id | select_type | table  | partitions | type  | possible_keys   | key     | key_len | ref | rows | filtered | Extra            | 
    +----+-------------+------------+------------+-------------+-----------------------+-----------------------+---------+-------+-------+----------+-----------------------------------------------------+ 
    | 1 | PRIMARY  | <derived2> | NULL  | system  | NULL     | NULL     | NULL | NULL |  1 | 100.00 | NULL            | 
    | 1 | PRIMARY  | ifo  | NULL  | index_merge | ItemComments,ItemType | ItemComments,ItemType | 5,767 | NULL | 88 | 100.00 | Using intersect(ItemComments,ItemType); Using where | 
    | 2 | DERIVED  | ItemInfo | NULL  | ref   | ItemType    | ItemType    | 767  | const | 27378 | 100.00 | NULL            | 
    +----+-------------+------------+------------+-------------+-----------------------+-----------------------+---------+-------+-------+----------+-----------------------------------------------------+ 
    

    而且我想解釋一下爲什麼要放在第一位使用以便與限制:我打算從表中隨機地取記錄一個特定的概率。從python生成的隨機索引作爲變量發送給MySQL。但後來我發現它花費了很多時間,所以我決定只使用我得到的第一張唱片。

    經過O的啓發。瓊斯和維傑,我嘗試使用最大功能,但它表現不佳:

    select max(ItemComments) from ItemInfo where ItemType='item_type' 
    /* Affected rows: 0 Found rows: 1 Warnings: 0 Duration for 1 query: 6.225 sec. */ 
    
    explain result: 
    +----+-------------+------------+------------+------+---------------+----------+---------+-------+-------+----------+-------+ 
    | id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref | rows | filtered | Extra | 
    +----+-------------+------------+------------+------+---------------+----------+---------+-------+-------+----------+-------+ 
    | 1 | SIMPLE  | ItemInfo | NULL  | ref | ItemType  | ItemType | 767  | const | 27378 | 100.00 | NULL | 
    +----+-------------+------------+------------+------+---------------+----------+---------+-------+-------+----------+-------+ 
    

    謝謝你所有的這個問題。希望您可以根據上述信息提供更多解決方案。

  • +0

    你能展示一個解釋嗎? –

    回答

    1

    請提供CURRENT SHOW CREATE TABLE ItemInfo

    對於大多數的查詢,則需要綜合指數

    INDEX(ItemType, ItemComments) 
    

    對於最後一個,你需要

    INDEX(ItemComments) 
    

    對於特別緩慢的查詢,請提供EXPLAIN SELECT ...

    討論 - 爲什麼INDEX(ItemType, ItemComments)幫助where ItemType="item_type" order by ItemComments desc limit 1

    索引的結構爲BTree(請參閱維基百科),從而可以非常快地搜索單個項目,並以特定順序快速掃描。

    where ItemType="item_type"說要過濾ItemType,但索引中有很多這樣的。在此索引中,它們按ItemComments(給定ItemType)排序。方向desc建議以最高值ItemContents開始;這是索引項目的「終點」。最後limit 1說找到一個項目後停止。 (有點像找到你的Rolodex中的最後一個「S」)。

    因此,查詢是將BTree'鑽取'到合成INDEX(ItemType, ItemContents)ItemType的條目末尾,並抓取一個條目 - 非常高效任務。

    其實SELECT *意味着還有一個步驟,即獲得該行的所有列。該信息不在索引中,而是在ItemInfo的BTree中 - 其中包含所有行的所有列,由PRIMARY KEY排序。

    「二級索引」(INDEX(ItemType, ItemComments))隱含包含相關PRIMARY KEY列的副本,因此我們現在具有值ItemIDItemType。通過這些,我們可以深入研究其他BTree,找到所需的行並獲取所有列(*)。

    +0

    我添加了一個複合索引,就像你提供的那樣,而且確實有效!第一個查詢執行變得非常快。但是,請你解釋它是如何工作的? –

    +0

    閱讀http://mysql.rjweb.org/doc.php/index_cookbook_mysql。它可能不會給你你想要的解釋,但它應該給你更多的線索。 –

    +0

    @BarryZhai - 增加了一個冗長而詳細的討論。 –

    1

    您的第一個查詢順序升序可以利用您的索引ItemComment

    SELECT * ... ORDER BY ... LIMIT 1是一個臭名昭着的表演反模式。爲什麼?服務器必須對整行進行排序,只是放棄第一行。

    你可以試試這個(對於你的降序變體)。它稍微冗長些,但效率更高。

    SELECT a.* 
        FROM ItemInfo a 
        JOIN (
          SELECT MAX(ItemComments) ItemComments, ItemType 
           FROM ItemInfo 
          GROUP BY ItemType 
         ) maxcomm ON a.ItemType = maxcomm.ItemType 
            AND a.ItemComments = maxcomm.ItemComments 
        WHERE a.ItemType = 'item type' 
    

    這是爲啥工作?它使用GROUP BY/MAX()來找到最大值,而不是ORDER BY ... DESC LIMIT 1。子查詢會進行搜索。

    要使這項工作儘可能高效,您需要一個(ItemType, ItemComments)上的複合(多列)索引。創建與

    ALTER TABLE ItemInfo CREATE INDEX ItemTypeCommentIndex (ItemType, ItemComments); 
    

    當您創建新的索引,刪除索引上ItemType,因爲新的指數是多餘的那一個。

    MySQL的查詢規劃器很聰明,在運行內部GROUP BY查詢之前可以看到外部WHERE子句,因此它不必聚合整個表。

    使用該複合索引,MySQL可以使用鬆散索引掃描來滿足子查詢。那些幾乎是奇蹟般的快速。你應該閱讀這個話題。

    0

    您的查詢將基於where條件選擇所有行。之後,它將按照語句順序對行進行排序,然後它將選擇第一行。一個更好的查詢可能類似於

    SELECT ifo.* FROM ItemInfo ifo 
    JOIN (SELECT MAX(ItemComments) AS max_comnt FROM ItemInfo WHERE ItemType="item_type") inn_ifo 
    ON ifo.ItemComments = inn_ifo.max_comnt 
    

    由於此查詢僅查找列中的最大值。查找MAX()只有O(n),但排序最快的算法是O(nlogn)。因此,如果您將避免statemet命令查詢將執行更快。 希望這有助於。