mysql
TRANSCRIPT
俺のSQLがこんなに遅いわけがない
id:paulowniatwitter : @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: ALLpossible_keys: point key: NULL key_len: NULL ref: NULL rows: 100000 Extra: Using where*************************** 2. row *************************** id: 2 select_type: SUBQUERY table: ranking type: constpossible_keys: PRIMARY key: PRIMARY key_len: 14 ref: rows: 1 Extra:
テーブルフルスキャンしてる…
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=memoryPARTITION 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 AirOSX 10.7.3
Core i5 1.7GHz4GB MemRedis 2.4.4MySQL 5.5.15
write read
RedisSortedSet 12,483ms 460,709ms
MySQLPartition Pruning 13,841ms 598,233ms
MySQLTable Full Scan 13,711ms 736,358ms
orz
Table Full Scanより速いものの思ったほど速くならなかった→パーティション刈り込みには最適化の余地あり?
CAブログの結果と比べるとRedisはほぼ同じだが、MySQLフルスキャンがかなり速い→MySQLは環境依存が大きい?
2倍速い理由がRead件数で説明できなくなったし→てへぺろ
Redis速ぇ…
おしまい