MySQL with InnoDB のインデックスの基礎知識とありがちな間違い


 (@a_bicky) 

RDBMS 

 Rails  MySQL 5.6MySQL  InnoDB 使MySQL 5.6  InnoDB 

InnoDB 

 (B+ )


InnoDB  B+ 使B+ 


b b - 1 b*1







O(\log_bn) 


3

f:id:a_bicky:20170417093420p:plain:w552
key 4 value 辿

f:id:a_bicky:20170417093427p:plain:w440
key 29 value 辿

f:id:a_bicky:20170417093430p:plain:w440
InnoDB  B+ 使 value 

 (c1, c2) key  4 bytes  c1  4 bytes  c2 

f:id:a_bicky:20170418083840p:plain:w440
c2 = 2 c1, c2 辿c1 = 4 AND c2 = 2  c1 辿

c1 >= 2 AND c2 <= 4 c1 >= 2 辿 c2 <= 4  [7,5]  [9,3] c1 >= 2 

 c1 = 2 AND c2 <= 4  c1 *2

 MySQL  8.2.1.2 Range Optimization 

The optimizer attempts to use additional key parts to determine the interval as long as the comparison operator is =, <=>, or IS NULL. If the operator is >, <, >=, <=, !=, <>, BETWEEN, or LIKE, the optimizer uses it but considers no more key parts.

InnoDB  B+ 

B+Tree index structures in InnoDB  Jeremy Cole


InnoDB  (clustered indexes) B+ 

使 B+ 辿 B+ 辿

f:id:a_bicky:20170417093437p:plain:w440
辿

MySQL 


MySQL executor*3  storage engine (e.g. InnoDB) 

storage engine  InnoDB 


(一)executor  storage engine 

(二)storage engine (InnoDB) 辿

使 executor 



(三)storage engine 2使 (Using index condition)

使



(四)storage engine 3使

SELECT  WHERE  (Using index)



(五)storage engine  executor 

(六)executor  storage engine  (Using where)

storage engine 




MySQL

Explain  extra  Using index, Using index condition, Using where  Using index condition  ICP (Index Condition Pushdown) MySQL 5.6 c1 >= 2 AND c2 <= 4  (c1, c2) ICP 







WHERE c1 = 1 AND c2 = 2 AND c3 = 3 AND c4 = 4 (c1, c2, c3, c4) 

c1 













ICP 

使*4













CREATE TABLE `products` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `shop_id` int(10) unsigned NOT NULL,  -- 商品を掲載している店舗のID
  `name` varchar(255) NOT NULL,         -- 商品名
  `price` int(10) unsigned NOT NULL,    -- 商品の価格
  `starts_at` datetime NOT NULL,        -- 商品の掲載開始日時
  `ends_at` datetime NOT NULL,          -- 商品の掲載終了日時
  PRIMARY KEY (`id`)
) ENGINE=InnoDB




ALTER TABLE products ADD INDEX ix_ends_at_starts_at (ends_at, starts_at);

ends_at *5
-- 現在掲載されている商品を抽出する
SELECT * FROM products WHERE starts_at <= NOW() AND ends_at >= NOW();

 B+  (c1, c2)  B+ c1 >= 2 AND c2 <= 5 [2,8]  c2  [3,1]  c2 c1 >= 2 

f:id:a_bicky:20170418084900p:plain:w440
MySQL 5.6  ICP starts_at >= NOW() ICP 

ICP  session status  Handler_read_next 
-- ICP 有効
FLUSH STATUS;
SET @@optimizer_switch = "index_condition_pushdown=on";
SELECT * FROM products WHERE starts_at <= NOW() AND ends_at >= NOW();
SHOW SESSION STATUS LIKE 'Handler%';

-- ICP 無効
FLUSH STATUS;
SET @@optimizer_switch = "index_condition_pushdown=off";
SELECT * FROM products WHERE starts_at <= NOW() AND ends_at >= NOW();
SHOW SESSION STATUS LIKE 'Handler%';

ICP ix_ends_at_starts_at 
ALTER TABLE products DROP INDEX ix_ends_at_starts_at,
  ADD INDEX ix_ends_at (ends_at);


ALTER TABLE products ADD INDEX ix_ends_at_shop_id (ends_at, shop_id);

 ends_at 
-- shop_id = 1234 の店舗の現在掲載される商品を抽出する
SELECT * FROM products WHERE shop_id = 1234 AND starts_at <= NOW() AND ends_at >= NOW();

shop_id 使(shop_id, ends_at) 
ALTER TABLE products DROP INDEX ix_ends_at_shop_id,
  ADD INDEX ix_shop_id_ends_at (shop_id, ends_at);



ALTER TABLE products ADD INDEX ix_shop_id_starts_at (shop_id, starts_at);


-- shop_id = 1234 の店舗の現在掲載される商品を抽出する
SELECT * FROM products WHERE shop_id = 1234 AND starts_at <= NOW() AND ends_at >= NOW();

ix_shop_id_starts_at products starts_at <= NOW() ends_at >= NOW() 

(shop_id, ends_at) 
ALTER TABLE products DROP INDEX ix_shop_id_starts_at,
  ADD INDEX ix_shop_id_ends_at (shop_id, ends_at);



ALTER TABLE products ADD INDEX ix_shop_id (shop_id),
  ADD INDEX ix_shop_id_ends_at (shop_id, ends_at);

2ix_shop_id 
-- shop_id = 1234 の店舗に掲載されたことがある、または掲載される予定の商品を全て抽出する
SELECT * FROM products WHERE shop_id = 1234;
-- shop_id = 1234 の店舗の現在掲載される商品を抽出する
SELECT * FROM products WHERE shop_id = 1234 AND starts_at <= NOW() AND ends_at >= NOW();

shop_id  ix_shop_id 2 ix_shop_id_ends_at  ix_shop_id 

f:id:a_bicky:20170417093440p:plain:w440
ix_shop_id 
ALTER TABLE products DROP INDEX ix_shop_id;


MySQL (InnoDB) InnoDB 

*1:次数の解釈は文献によって異なるので、2017 年 4 月 17 日時点の Wikipedia に合わせています

*2:後述する ICP が効果を発揮する場合はその限りではありません

*3:executor を mysql server と表現している記事を見かけることがありますが、sql_executor.cc に実装されているので executor という表現の方が適切だと思います

*4:本エントリではソートには触れないので、興味のある方は「漢(オトコ)のコンピュータ道: Using filesort」を参照すると良いと思います

*5:Explain の key_len 的には starts_at も使われるように見えるので、ソースコードを読んでその理由を調べようと前々から思ってますが、未だに調査できてません…