簡體   English   中英

mariadb在子查詢中未使用正確的索引

[英]mariadb not using the correct index with subquery

我在mariadb數據庫上遇到性能問題。 在我看來,mariadb在使用子查詢進行請求時未使用正確的索引,而在請求中手動注入子查詢的結果則成功使用了索引:

這是行為不良的請求(請注意,第二個子查詢讀取的行多於必要的行):

ANALYZE SELECT  `orders`.* FROM `orders` 
  WHERE `orders`.`account_id` IN (SELECT `accounts`.`id` FROM `accounts` WHERE `accounts`.`user_id` = 88144) 
  AND (               orders.type not in ("LimitOrder", "MarketOrder")
                   OR orders.type     in ("LimitOrder", "MarketOrder") AND orders.state <> "canceled"
                   OR orders.type     in ("LimitOrder", "MarketOrder") AND orders.state =  "canceled" AND orders.traded_btc > 0 ) 
  AND (NOT (orders.type = 'AdminOrder' AND orders.state = 'canceled'))  ORDER BY `orders`.`id` DESC LIMIT 20 OFFSET 0 \G;
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: accounts
         type: ref
possible_keys: PRIMARY,index_accounts_on_user_id
          key: index_accounts_on_user_id
      key_len: 4
          ref: const
         rows: 7
       r_rows: 7.00
     filtered: 100.00
   r_filtered: 100.00
        Extra: Using index; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: PRIMARY
        table: orders
         type: ref
possible_keys: index_orders_on_account_id_and_type,index_orders_on_type_and_state_and_buying,index_orders_on_account_id_and_type_and_state,index_orders_on_account_id_and_type_and_state_and_traded_btc
          key: index_orders_on_account_id_and_type_and_state_and_traded_btc
      key_len: 4
          ref: bitcoin_central.accounts.id
         rows: 60
       r_rows: 393.86
     filtered: 100.00
   r_filtered: 100.00
        Extra: Using index condition; Using where

當手動注入子查詢的結果時,我具有正確的行為(和預期的性能):

ANALYZE SELECT  `orders`.* FROM `orders`      
    WHERE `orders`.`account_id` IN (433212, 433213, 433214, 433215, 436058, 436874, 437950)
    AND (               orders.type not in ("LimitOrder", "MarketOrder")                           
                     OR orders.type     in ("LimitOrder", "MarketOrder") AND orders.state <> "canceled"
                     OR orders.type     in ("LimitOrder", "MarketOrder") AND orders.state =  "canceled" AND orders.traded_btc > 0 )
   AND (NOT (orders.type = 'AdminOrder' AND orders.state = 'canceled')) 
   ORDER BY `orders`.`id` DESC LIMIT 20 OFFSET 0\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: orders
         type: range
possible_keys: index_orders_on_account_id_and_type,index_orders_on_type_and_state_and_buying,index_orders_on_account_id_and_type_and_state,index_orders_on_account_id_and_type_and_state_and_traded_btc
          key: index_orders_on_account_id_and_type_and_state_and_traded_btc
      key_len: 933
          ref: NULL
         rows: 2809
       r_rows: 20.00
     filtered: 100.00
   r_filtered: 100.00
        Extra: Using index condition; Using where; Using filesort
1 row in set (0.37 sec)

請注意,加入兩個表時,我有完全相同的問題。

這是我的訂單表的定義的摘錄:

SHOW CREATE TABLE orders \G;
*************************** 1. row ***************************
       Table: orders
Create Table: CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `account_id` int(11) NOT NULL,
  `traded_btc` decimal(16,8) DEFAULT '0.00000000',
  `type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `state` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `index_orders_on_account_id_and_type_and_state_and_traded_btc` (`account_id`,`type`,`state`,`traded_btc`),
  CONSTRAINT `orders_account_id_fk` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=8575594 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

有人知道這是怎么回事嗎? 有沒有一種方法可以強制數據庫在子請求中使用我的索引。

IN ( SELECT ... )優化效果不佳。 通常的解決方案是將JOIN變成:

FROM accounts AS a
JOIN orders AS o  ON a.id = o.account_id
WHERE a.user_id = 88144
  AND ... -- the rest of your WHERE

還是您對“請注意,在合並兩個表時遇到完全相同的問題”的做法是? 如果是這樣,讓我們​​看一下查詢,它是EXPLAIN

您指的是“預期的性能” ...您指的是EXPLAIN的數字嗎? 還是您有時間備份聲明?

我喜歡這樣做,以便更好地了解正在進行的“工作”量:

FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';

這些數字通常可以清楚地表明是否涉及表掃描或在OFFSET+LIMIT之后查詢是否停止。 這些數字是精確的計數,與EXPLAIN不同,后者只是估計值。

大概您通常通過account_id查找orders 這是加快此類查詢速度的一種方法:

替換當前的兩個索引

 PRIMARY KEY (`id`),
 KEY `account_id__type__state__traded_btc` 
    (`account_id`,`type`,`state`,`traded_btc`),

用這些:

 PRIMARY KEY (`account_id`, `type`, `id`),
 KEY (id)  -- to keep AUTO_INCREMENT happy.

這會聚集給定帳戶的所有行,從而使查詢運行更快,尤其是如果您現在受 I / O限制。 如果某些列組合形成“自然” PK,則完全折騰id

(請注意,我如何在不丟失任何信息的情況下縮短了您的鍵名?)

另外,如果您受I / O約束,則可以通過將那些冗長的VARCHARs (狀態和類型)轉換為ENUMs縮小表。

更多

鑒於查詢涉及

WHERE ... mess with INs and ORs ...
ORDER BY ...
LIMIT 20

並且該用戶有200萬行,沒有INDEX可以通過WHERE進入ORDER BY ,從而可以使用LIMIT 也就是說,它必須以這種方式執行:

  1. 篩選該用戶的2M行
  2. 排序( ORDER BY )2M行的相當大的一部分
  3. 剝去20行。 (是的,5.6使用“優先級”隊列,使排序為O(1)而不是O(log N),但這並沒有太大幫助。

實際上,我驚訝於IN( constants )運作良好。

我有同樣的問題。 請使用內部聯接而不是子查詢。

暫無
暫無

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

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