俺のSQLがこんなに
遅いわけがない
id:paulownia
twitter	 :	 @nullpon
4月
サイバーエージェント
エンジニアブログに
投稿された
あるエントリが話題に
Redis
それは危険なほどの
スピード
MySQLの2倍速い!
はてなブックマーク
でも注目
注)はてブユーザのイメージ画像
•HASH	 index	 使ってて	 
range	 search	 やってるのに	 
MySQL	 遅いとか言ってて
大丈夫なの?
•INDEX	 USING	 BTREE	 に
したらいいんじゃ
•ブックマークコメントで
マサカリが飛び交っていると聞いて
•タイトルがカッコイイw
はてブのコメント
というわけで
検証してみた
1. Hashインデックス意味ないの?
2. B-Treeならば速い?
3. 何故Redisは2倍も速いのか?
4. MySQLで危険なスピードに挑む
Hash	 インデックス
意味ないの?
①
比較してみる
write read
Hash
インデックス
14,016ms 755,127ms
インデックス
なし
13,711ms 736,358ms
ほぼ同じ…
explainしてみる
explain	 select	 count(point)	 
from	 ranking	 
where	 point	 >	 (
select	 point	 from	 ranking	 
where	 id	 =	 'user3939'	 )G
***************************	 1.	 row	 *************
	 	 	 	 	 	 	 	 	 	 	 id:	 1
	 	 select_type:	 PRIMARY
	 	 	 	 	 	 	 	 table:	 ranking
	 	 	 	 	 	 	 	 	 type:	 ALL
possible_keys:	 point
	 	 	 	 	 	 	 	 	 	 key:	 NULL
	 	 	 	 	 	 key_len:	 NULL
	 	 	 	 	 	 	 	 	 	 ref:	 NULL
	 	 	 	 	 	 	 	 	 rows:	 100000
	 	 	 	 	 	 	 	 Extra:	 Using	 where
***************************	 2.	 row	 *************
	 	 	 	 	 	 	 	 	 	 	 id:	 2
	 	 select_type:	 SUBQUERY
	 	 	 	 	 	 	 	 table:	 ranking
	 	 	 	 	 	 	 	 	 type:	 const
possible_keys:	 PRIMARY
テーブルフルスキャン
してる…
where句が	 <	 ,	 >	 ,	 betweenのとき
Hashインデックスは使えません…
Hashインデックス
意味なし!
B-Treeインデックス
ならば速い?
②
比較してみる
write read
B-Tree
インデックス
13,734ms 1,398,129ms
インデックス
なし
13,711ms 736,358ms
B-Tree遅ぇ
何故?
B-Treeが有効な理由
•Index	 Range	 Scan
•Covering	 Index
Index	 Range	 Scan
•範囲検索を高速化する
•B-Treeインデックスはソート済み
•読み込みレコード件数が減るので
高速
しかし
多くのレコードをreadする場合、
インデックス読みの込みの分、
むしろ遅くなる。
Covering	 Index
•インデックスに格納された値のみで
処理し、テーブルを読まない
•MySQL高速化の常套手段
しかし
Memoryエンジンでは
Covering	 Indexが使えない
というか、RDBのIndexは
少数のレコードを高速に検索するためのものなので
今回のようにテーブルのほとんどのデータを読む
場合には不向き
インデックスが有効なのは
select範囲がテーブルの
30%ぐらいまで
B-Treeインデックス
意味なし!
何故Redisは
MySQLの
2倍速い?
③
読み込み件数の差で
説明できる
ベンチマークでやってる事
•N	 件のデータに対して(N	 >	 1)
•1以上	 N	 以下のポイントをランダムに振り、
•M	 ポイントより大きいデータ件数を
countする(1	 <	 M	 <	 N)
N	 =	 100,000
Redis	 ソート済みセット
 読み取りレコード件数(の期待値):50,000件
MySQL	 テーブルフルスキャン
 読み取りレコード件数:100,000件
ベンチマークでやってる事
RedisのRead件数は
MySQLの半分
だから
2倍速い
④
MySQLで
危険なスピードに
チャレンジ
インデックスを使わず、
Read件数を減らせれば
高速化できそうな気がする
そんな都合のいい方法あるの?
あります
パーティショニング
(Partitioning)
pointの値でテーブルを分割し
範囲内のパーティションのみ
スキャン
パーティションの刈り込み
(Patition	 Pruning)
CREATE	 TABLE	 ranking	 (
	 	 	 	 id	 VARCHAR(12)	 NOT	 NULL,
	 	 	 	 point	 INT	 NOT	 NULL,
	 	 	 	 PRIMARY	 KEY	 (id,	 point)	 USING	 BTREE	 
)	 ENGINE=memory
PARTITION	 BY	 RANGE	 COLUMNS(point)	 (
	 	 	 	 PARTITION	 p0	 VALUES	 LESS	 THAN	 (10000),
	 	 	 	 PARTITION	 p1	 VALUES	 LESS	 THAN	 (20000),
	 	 	 	 PARTITION	 p2	 VALUES	 LESS	 THAN	 (30000),
	 	 	 	 PARTITION	 p3	 VALUES	 LESS	 THAN	 (40000),
	 	 	 	 PARTITION	 p4	 VALUES	 LESS	 THAN	 (50000),
	 	 	 	 PARTITION	 p5	 VALUES	 LESS	 THAN	 (60000),
	 	 	 	 PARTITION	 p6	 VALUES	 LESS	 THAN	 (70000),
	 	 	 	 PARTITION	 p7	 VALUES	 LESS	 THAN	 (80000),
	 	 	 	 PARTITION	 p8	 VALUES	 LESS	 THAN	 (90000),
	 	 	 	 PARTITION	 p9	 VALUES	 LESS	 THAN	 MAXVALUE	 
);
10のパーティションに分割
平均45%のレコード読み込みを
スキップできる
Redisに匹敵する速度が
出るはず!
実験してみた
MacBook	 Air
OSX	 10.7.3
Core	 i5	 1.7GHz
4GB	 Mem
Redis	 2.4.4
MySQL	 5.5.15
write read
Redis
SortedSet
12,483ms 460,709ms
MySQL
Partition Pruning
13,841ms 598,233ms
MySQL
Table Full Scan
13,711ms 736,358ms
orz
Table	 Full	 Scanより速いものの
思ったほど速くならなかった
→パーティション刈り込みには最
適化の余地あり?
CAブログの結果と比べると
Redisはほぼ同じだが、
MySQLフルスキャンがかなり速い
→MySQLは環境依存が大きい?
2倍速い理由が
Read件数で説明できなくなったし
→てへぺろ
Redis速ぇ…
おしまい

MySQL