簡體   English   中英

為什么MySQL不使用最佳索引

[英]Why does MySQL not use optimal indexes

我正在嘗試優化查詢,但是,MySQL似乎在查詢上使用了非最佳索引,而且我似乎無法弄清楚出了什么問題。 我的查詢如下:

SELECT  SQL_CALC_FOUND_ROWS deal_ID AS ID,dealTitle AS dealSaving,
       storeName AS title,deal_URL AS dealURL,dealDisclaimer,
       dealType, providerName,providerLogo AS providerIMG,createDate,
       latitude AS lat,longitude AS lng,'local' AS type,businessType,
       address1,city,dealOriginalPrice,NULL AS dealDiscountPercent,
       dealPrice,scoringBase, smallImage AS smallimage,largeImage AS image,
       storeURL AS storeAlias,
       exp(-power(greatest(0, 
             abs(69.0*DEGREES(ACOS(0.82835377099147 *
               COS(RADIANS(latitude)) * COS(RADIANS(-118.4-longitude)) +
               0.56020534635454*SIN(RADIANS(latitude)))))-2),
                       2)/(5.7707801635559)) *
            scoringBase * IF(submit_ID IN (18381),
               IF(businessType = 1,1.3,1.2),IF(submit_ID IN (54727),1.19, 1)
                          ) AS distance
    FROM  local_deals
    WHERE  latitude BETWEEN 33.345362318841 AND 34.794637681159
      AND  longitude BETWEEN -119.61862872928 AND -117.18137127072
      AND  state = 'CA'
      AND  country = 'US'
    ORDER BY  distance DESC
    LIMIT  48 OFFSET 0; 

列出表上的索引將顯示:

+-------------+------------+-----------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table       | Non_unique | Key_name        | Seq_in_index | Column_name     | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------------+------------+-----------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| local_deals |          0 | PRIMARY         |            1 | id              | A         |      193893 |     NULL | NULL   |      | BTREE      |         |               |
| local_deals |          0 | unique_deal_ID  |            1 | deal_ID         | A         |      193893 |     NULL | NULL   |      | BTREE      |         |               |
| local_deals |          1 | deal_ID         |            1 | deal_ID         | A         |      193893 |     NULL | NULL   |      | BTREE      |         |               |
| local_deals |          1 | store_ID        |            1 | store_ID        | A         |      193893 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | storeOnline_ID  |            1 | storeOnline_ID  | A         |           3 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | storeChain_ID   |            1 | storeChain_ID   | A         |         117 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | userProvider_ID |            1 | userProvider_ID | A         |           5 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | expirationDate  |            1 | expirationDate  | A         |        3127 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | createDate      |            1 | createDate      | A         |       96946 |     NULL | NULL   | YES  | BTREE      |         |               | 
| local_deals |          1 | city            |            1 | city            | A         |       17626 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | state           |            1 | state           | A         |         138 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | zip             |            1 | zip             | A         |       38778 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | country         |            1 | country         | A         |          39 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | latitude        |            1 | latitude        | A         |      193893 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | longitude       |            1 | longitude       | A         |      193893 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | eventDate       |            1 | eventDate       | A         |        4215 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | isNowDeal       |            1 | isNowDeal       | A         |           3 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | businessType    |            1 | businessType    | A         |           5 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | dealType        |            1 | dealType        | A         |           5 |     NULL | NULL   | YES  | BTREE      |         |               |
| local_deals |          1 | submit_ID       |            1 | submit_ID       | A         |           5 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------------+------------+-----------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

運行說明擴展顯示:

+------+-------------+-------------+------+----------------------------------+-------+---------+-------+-------+----------+----------------------------------------------------+
| id   | select_type | table       | type | possible_keys                    | key   | key_len | ref   | rows  | filtered | Extra                                              |
+------+-------------+-------------+------+----------------------------------+-------+---------+-------+-------+----------+----------------------------------------------------+
|    1 | SIMPLE      | local_deals | ref  | state,country,latitude,longitude | state | 35      | const | 52472 |   100.00 | Using index condition; Using where; Using filesort |
+------+-------------+-------------+------+----------------------------------+-------+---------+-------+-------+----------+----------------------------------------------------+

表格中大約有20萬行。 奇怪的是它忽略了緯度和經度索引,因為它們應該更多地過濾表。 在刪除命令的“州”和“國家”的地方運行查詢,可以得到以下解釋:

+------+-------------+-------------+-------+--------------------+-----------+---------+------+-------+----------+----------------------------------------------------+
| id   | select_type | table       | type  | possible_keys      | key       | key_len | ref  | rows  | filtered | Extra                                              |
+------+-------------+-------------+-------+--------------------+-----------+---------+------+-------+----------+----------------------------------------------------+
|    1 | SIMPLE      | local_deals | range | latitude,longitude | longitude | 5       | NULL | 30662 |   100.00 | Using index condition; Using where; Using filesort |
+------+-------------+-------------+-------+--------------------+-----------+---------+------+-------+----------+----------------------------------------------------+

這表明經度索引可以更好地將表篩選為30,662行。 我在這里想念什么嗎? 如何獲得MySQL以使用所有查詢。 請注意,該表是InnoDB,並且我正在使用MySQL 5.5。

您查詢的最佳索引是(country, state, latitude, longitude)的綜合索引(country, state, latitude, longitude)可以交換country state )。 MySQL在這里提供了有關多列索引的良好文檔。

基本上, latitudelongitude並不是單獨選擇的。 不幸的是,標准B樹索引僅支持一個不等式,而您的查詢有兩個。

實際上,如果要進行GIS處理,則應使用MySQL的空間擴展。

根據表的大小,Gordon建議的索引可能“足夠好”。 如果需要更高的性能,則需要使用2D分區技術,其中您可以按latitude分區,並安排InnoDB PRIMARY KEYlongitude開頭。 我的文章中提供了更多詳細信息和示例代碼。

解決此類問題的一種通用技術是使用以下屬性構建子查詢:

  • 它返回的行數不超過LIMIT個; 這些就是您所需要的。
  • 所涉及的列以及“ PRIMARY KEY ”都有一個“覆蓋索引”。
  • 您正在使用InnoDB。

就像是

SELECT b. ..., a.distance
    FROM  local_deals b
    JOIN  (
        SELECT id,
               (...) AS distance,
            FROM local_deals
            WHERE  latitude  BETWEEN   33.34536 AND   34.79464
              AND  longitude BETWEEN -119.61863 AND -117.18137
              AND  state = 'CA'
              AND  country = 'US'
            ORDER BY  distance ASC
            LIMIT  48 OFFSET 0
          ) AS a  ON b.id = a.id
    ORDER BY a.distance;

INDEX(country, state, latitude, longitude, id)  -- `id` is the PK
-- country and state first (because of '='); id last.

為什么這有幫助...

  • 索引是“覆蓋的”,因此漫長的掃描(遠遠超過48行)完全在索引的BTree中完成。 這樣可以減少表的I / O。
  • 其他所有字段(b。*)不會在tmp表等中拖拉。僅處理48組這些字段。
  • 由於“集群式PK”,ID的48個查詢在InnoDB中特別有效。

當使用“巨大”表(其中I / O占主導)時,這種技術可以算作:

  • 子查詢需要索引中的1個塊或少量的塊。 請注意,所需記錄是連續的,或者幾乎是連續的。 (好的,如果有30K可以瀏覽,則可能超過100個塊;因此,我對縮小邊框的評論從此開始。)
  • 然后,通過id 48( LIMIT )次隨機獲取將獲得48行。

如果沒有子查詢,則需要獲取龐大的行。 並且,根據所使用的索引,最多可以提取30K塊。 那要慢幾個數量級。

同樣,將48行與30K行進行比較以寫入排序表( ORDER BY )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM