MySQL 优化器之Index merge Multi-Range Read MRR与Batched Key Access使用案例详解
MySQL 優化器之Index merge Multi-Range Read ?MRR與Batched Key Access介紹
索引合并Index merge
覆蓋索引是在索引里即有查詢時需要的字段,而在一個表上的查詢往往條件會有多個組合且較為復雜,所以很難全部由覆蓋索引包含到。那么此時我們就會用到索引合并(index merge)算法。
該算法主要包含三類情況:
| 算法 | 應用場景 | 執行計劃 |
| Intersection | and | Using intersect(...) |
| Union | or | Using or(...) |
| Sort-Union | 帶范圍查詢的or | sort_union(...) |
交Intersection算法
EXPLAIN ANALYZE SELECT * FROM sakila.payment WHERE staff_id = 1 AND customer_id = 75; --結果 -> Filter: ((payment.customer_id = 75) and (payment.staff_id = 1))? (cost=8.12 rows=20) (actual time=2.329..2.637 rows=22 loops=1)-> Index range scan on payment using intersect(idx_fk_customer_id,idx_fk_staff_id)? (cost=8.12 rows=20) (actual time=2.325..2.617 rows=22 loops=1)從執行計劃里我們不難看出這里用到了索引交(intersect)
而通過explain命令則可以看出是
mysql> EXPLAIN SELECT * FROM sakila.payment WHERE staff_id = 1 AND customer_id = 75\G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: paymentpartitions: NULLtype: index_merge possible_keys: idx_fk_staff_id,idx_fk_customer_idkey: idx_fk_customer_id,idx_fk_staff_idkey_len: 2,1ref: NULLrows: 20filtered: 100.00Extra: Using intersect(idx_fk_customer_id,idx_fk_staff_id); Using where通過觀察該查詢和表的結構可以看出
show create table sakila.payment --結果 CREATE TABLE `payment` (`payment_id` smallint unsigned NOT NULL AUTO_INCREMENT,`customer_id` smallint unsigned NOT NULL,`staff_id` tinyint unsigned NOT NULL,`rental_id` int DEFAULT NULL,`amount` decimal(5,2) NOT NULL,`payment_date` datetime NOT NULL,`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`payment_id`) USING BTREE,KEY `idx_fk_staff_id` (`staff_id`) USING BTREE,KEY `idx_fk_customer_id` (`customer_id`) USING BTREE,KEY `fk_payment_rental` (`rental_id`) USING BTREE,CONSTRAINT `fk_payment_customer` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`) ON DELETE RESTRICT ON UPDATE CASCADE,CONSTRAINT `fk_payment_rental` FOREIGN KEY (`rental_id`) REFERENCES `rental` (`rental_id`) ON DELETE SET NULL ON UPDATE CASCADE,CONSTRAINT `fk_payment_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`staff_id`) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci字段staff_id 和customer_id 有各自的索引idx_fk_staff_id、idx_fk_customer_id,而我們的查詢是等值條件的交集。
同理如下查詢也是應用到了index merge的intersection算法。
EXPLAIN SELECT * FROM sakila.payment WHERE payment_id > 10 AND customer_id = 318;并Union算法
當一個表查詢中用到一系列的含OR的等值條件時一般會用到并算法。示例查詢見下:
SELECT * FROM sakila.payment WHERE staff_id = 1 OR customer_id = 318;SELECT * FROM sakila.payment WHERE payment_id > 15000 OR customer_id = 318; -- 通過explain或者explain、EXPLAIN ANALYZE() mysql> EXPLAIN SELECT * FROM sakila.payment WHERE staff_id = 1 OR customer_id = 318\G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: paymentpartitions: NULLtype: index_merge possible_keys: idx_fk_staff_id,idx_fk_customer_idkey: idx_fk_staff_id,idx_fk_customer_idkey_len: 1,2ref: NULLrows: 8069filtered: 100.00Extra: Using union(idx_fk_staff_id,idx_fk_customer_id); Using wheremysql> EXPLAIN ANALYZE SELECT * FROM sakila.payment WHERE staff_id = 1 OR customer_id = 318\G *************************** 1. row *************************** EXPLAIN: -> Filter: ((sakila.payment.staff_id = 1) or (sakila.payment.customer_id = 318)) (cost=2306.30 rows=8069) (actual time=0.224..103.595 rows=8062 loops=1) -> Index range scan on payment using union(idx_fk_staff_id,idx_fk_customer_id) (cost=2306.30 rows=8069) (actual time=0.221..98.988 rows=8062 loops=1)mysql> EXPLAIN FORMAT=tree SELECT * FROM sakila.payment WHERE staff_id = 1 OR customer_id = 318\G *************************** 1. row *************************** EXPLAIN: -> Filter: ((sakila.payment.staff_id = 1) or (sakila.payment.customer_id = 318)) (cost=2116.85 rows=8069)-> Index range scan on payment using union(idx_fk_staff_id,idx_fk_customer_id) (cost=2116.85 rows=8069)排序并Sort Union算法
跟并算法類似,不過這里用的不是等值條件而是范圍,詳見如下例子:
-- 查詢payment表里customer_id小于30或者rental_id小于10的信息。 mysql> EXPLAIN SELECT * FROM sakila.payment WHERE customer_id < 30 OR rental_id < 10\G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: paymentpartitions: NULLtype: index_merge possible_keys: idx_fk_customer_id,fk_payment_rentalkey: idx_fk_customer_id,fk_payment_rentalkey_len: 2,5ref: NULLrows: 826filtered: 100.00Extra: Using sort_union(idx_fk_customer_id,fk_payment_rental); Using where查詢payment表里customer_id小于20或者rental_id大于18000的信息。 mysql> EXPLAIN SELECT * FROM sakila.payment WHERE customer_id < 20 OR rental_id >18000\G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: paymentpartitions: NULLtype: index_merge possible_keys: idx_fk_customer_id,fk_payment_rentalkey: idx_fk_customer_id,fk_payment_rentalkey_len: 2,5ref: NULLrows: 514filtered: 100.00Extra: Using sort_union(idx_fk_customer_id,fk_payment_rental); Using whereEXPLAIN ANALYZE SELECT /*+ NO_INDEX_MERGE(payment) */ * FROM sakila.payment WHERE staff_id = 1 AND customer_id = 75;-> Filter: (payment.staff_id = 1) (cost=12.30 rows=21) (actual time=0.151..0.179 rows=22 loops=1) -> Index lookup on payment using idx_fk_customer_id (customer_id=75) (cost=12.30 rows=41) (actual time=0.149..0.167 rows=41 loops=1) 指定不用index merge的hint。多范圍讀MRR
多范圍讀MRR概念
多范圍讀即Multi-Range Read (MRR)的主要作用是減少掃描輔助索引時產生的IO隨機讀。優化器一般先讀取索引覆蓋的字段,其它字段通過rowid關聯到聚集索引而找出。多范圍讀(Multi-Range Read)優化可用于使用索引的范圍掃描和等價連接,它不支持虛擬列上的二級索引。
具體的說當讀取到輔助索引里字段后再關聯到主鍵,對主鍵在內存里進行排序后再順序的讀取其它字段的信息。這樣就會減少IO隨機讀。
InnoDB多范圍讀優化(MRR)的主要應用于沒有覆蓋索引的磁盤受限查詢(當寫比讀快很多時,讀就即被認為是磁盤受限)。
優化參數
用optimizer_switch的參數來控制是否使用MRR.設置mrr=on時,表示啟用MRR優化。
mrr_cost_based表示是否通過cost base的方式來啟用MRR。
當mrr=on,mrr_cost_based=on,則表示cost base的方式還選擇啟用MRR優化,當發現優化后的代價過高時就會不使用該項優化。
當mrr=on,mrr_cost_based=off,則表示總是開啟MRR優化。
多訪問讀示例
mysql> EXPLAIN SELECT /*+ MRR(city) */ * FROM world.city WHERE CountryCode BETWEEN 'AUS' AND 'CHN'\G **************************** 1. row ***************************** id: 1 select_type: SIMPLE table: city partitions: NULL type: range possible_keys: CountryCode key: CountryCode key_len: 3 ref: NULL rows: 812 filtered: 100 Extra: Using index condition; Using MRR批量鍵訪問Batched Key Access
批量鍵訪問概念
批量鍵值訪問(Batched Key Access)簡稱BKA,該優化操作是塊內嵌循環和多范圍讀的整合。這樣非索引聯接就可以使用類似連接緩沖區索引聯接的方式進行優化,通過使用多范圍讀取優化來減少隨機I/O的數量。
由于批處理鍵值訪問優化主要受益的查詢范圍相對較窄,而且其他查詢的性能可能會下降,因此默認情況下禁用優化。
批量鍵訪問示例
SET SESSION optimizer_switch = 'mrr=on,mrr_cost_based=off,batched_key_access=on';mysql> EXPLAIN-> SELECT /*+ BKA(ci) */-> co.Code, co.Name AS Country,-> ci.Name AS City-> FROM world.country co-> INNER JOIN world.city ci-> ON ci.CountryCode = co.Code\G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: copartitions: NULLtype: ALL possible_keys: PRIMARYkey: NULLkey_len: NULLref: NULLrows: 239filtered: 100.00Extra: NULL *************************** 2. row ***************************id: 1select_type: SIMPLEtable: cipartitions: NULLtype: ref possible_keys: CountryCodekey: CountryCodekey_len: 12ref: world.co.Coderows: 17filtered: 100.00Extra: Using join buffer (Batched Key Access)In this example, the Batched Key Access is enabled using an optimizer hint for the join on the city (ci) table using the CountryCode index上例中通過優化器提示(hint)啟用了批量鍵訪問選項,即在關聯city表時用到CountryCode索引。
批量鍵值范圍優化主要應用在數據量大的關聯中,關聯緩存則要相應的設置的多些,一般它對應的參數join_buffer_size要設置成4M或者更大。
總結
以上是生活随笔為你收集整理的MySQL 优化器之Index merge Multi-Range Read MRR与Batched Key Access使用案例详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server 聚集索引 clus
- 下一篇: 七年级上册政治复习资料