2017-09-15 43 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命令查询将执行更快。 希望这有助于。