innodb table compression

75
InnoDB Table Compression 瀬島 貴則 瀬島 貴則 revision 2

Upload: takanori-sejima

Post on 14-Feb-2017

2.321 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: InnoDB Table Compression

InnoDB Table Compression瀬島 貴則瀬島 貴則

revision 2

Page 2: InnoDB Table Compression

免責事項

- 本資料は個人の見解であり、私が所属する組織の見解とは必ずしも一致しません。

- 内容の一部に偏ったものがあるかもしれませんが、各自オトナの判断でよろしくお願いします。

Page 3: InnoDB Table Compression

自己紹介

- わりとMySQLでごはんたべてます- 一時期は Resource Monitoring もよくやってました

- Twitter: @ts4th

Page 4: InnoDB Table Compression

ちょっと宣伝

- 最近はわりとスライドを公開してますので- よろしかったら参考までに

- http://www.slideshare.net/takanorisejima

Page 5: InnoDB Table Compression

今日のお題

- (2008年あたり)InnoDB Plugin のころ、 InnoDB に圧縮機能が追加されました

- Facebook など一部の大企業が酷使しました- MySQL 5.7 になって、 InnoDB Page

Compression というものが追加されました- なぜ Page Compression というものが追加されたのか、むかしの圧縮機能はどういうものだったのか、振り返ってみましょう

Page 6: InnoDB Table Compression

基本的に参照するソースコードは

- MySQL5.6 をベースにお話します- 5.7 はまだ使い込んでないので

Page 7: InnoDB Table Compression

まずはじめに

- InnoDB の圧縮機能、公式ではそれぞれどういう呼び分けをするんだろうと思ってたんですが

- MySQL5.7のドキュメントに、昔ながらの圧縮機能は InnoDB Table Compression 、 5.7 で追加された機能は InnoDB Page Compression と書かれているので、とりあえずそう呼び分けます

Page 8: InnoDB Table Compression

では、InnoDB Table Compression

について

Page 9: InnoDB Table Compression

まずは参考資料

- 日本語だと平塚さんのblogが一番メジャーかなと思います- MySQL InnoDB Pluginのデータ圧縮機能 - SH2の日

- The Art of Work:MySQL InnoDB Pluginのデータ圧縮

機能 性能編 - SH2の日記

Page 11: InnoDB Table Compression

あとは

- BLOB などの可変長カラムが実はインラインで格納されるという、この話でしょうね- Externally Stored Fields in InnoDB- 公式ドキュメントにも書いてあります

- 14.9.3 DYNAMIC および COMPRESSED 行フォーマット- 14.7.5 InnoDB テーブルでの圧縮の動作

- 文字列がインラインで格納されてるページ、そこそこ圧縮率良くなるんじゃないかなぁ

Page 12: InnoDB Table Compression

文字列がインラインで格納されるなら

- 多数の数値型カラムと、少数の可変長カラムで構成されたテーブルだと「可変長カラムすべてが overflow page に書き込まれるなら、圧縮機能を使っても圧縮効率が悪いのでは?」と思われるかもしれませんが

- 可変長カラムのすべてが overflow page に書き込まれるとは限らないので、そんなに圧縮効率悪くならないかも

Page 13: InnoDB Table Compression

これらの資料を読んでいるならば

- 既知の話が多いかもしれませんが、一応、基本事項について触れていきます

- 平塚さんの記事と Nizam さんの資料を読んどけば、だいたい把握できるでしょう

Page 14: InnoDB Table Compression

図に描くとこう

Page 15: InnoDB Table Compression

compressed page(圧縮されたページ)

- *.ibd との I/O の単位は KEY_BLOCK_SIZE。- 圧縮されたデータと modification log というログを内包している

Page 16: InnoDB Table Compression

uncompressed page(展開済ページ)

- compressed data が展開され modification log が適用された状態のページ

- InnoDB Table Compression 使うときに出てくる特別な概念

- レコードのデータを読んだり書いたりするときに使う

- MySQL5.6以降では padding されたりするんだけど詳しくは後述

Page 17: InnoDB Table Compression

ややこしい話だけど

- buffer pool 内の page はふつう innodb_page_size で指定したサイズになってるけど

- 圧縮された page は、 KEY_BLOCK_SIZE で指定されたサイズの page になる- SHOW ENGINE INNODB STATUS などでみると、

innodb_buffer_pool_size/innodb_page_size より page の数がはるかに多くなったりする

Page 18: InnoDB Table Compression

unzip_LRU

- buffer pool のページはLRUリストで管理されているのだが、展開済みページを管理するLRUリスト(unzip_LRU)というものもある- 圧縮された*.ibdも圧縮されてない*.ibdも、それらのペー

ジは通常のLRUリストにマッピングされる。展開済み

ページとそのLRUリストが特別扱いである

- buffer pool から、展開済みページをどれくらい確保するかというのは、動的に決定される

Page 19: InnoDB Table Compression

MySQL5.6では

- page cleaner thread が lru_scan_depth にもとづいて free page 確保するときに、 unzip_LRU 参照して展開済みページ減らしたりする

- buf_do_LRU_batch() あたりから読むとわかる- buf_LRU_evict_from_unzip_LRU() が true のときは、

優先して展開済みページを捨てる

Page 20: InnoDB Table Compression

buf_LRU_evict_from_unzip_LRU()

- これが true になる条件は- unzip_LRU のリストが空ではなく

- unzip_LRU のリストが (buffer pool 全体の)LRU list の 1/10 以上で

- つまり、展開済みページは少なくとも LRU list の 10%は確保される

- (unzip するより I/O 発生する方が50倍遅い前提みたい

なので)、 unzip の頻度 =< IOの頻度*50 を満たすとき

- IO多いと展開済みページは容赦なく捨てる

Page 21: InnoDB Table Compression

ちなみに unzip_LRU listの長さは

- SHOW ENGINE INNODB STATUS\G でわかります- このへん

- InnoDB から I/O バウンドだとみなされていれば、 unzip_LRU len は LRU len の 10% しかないでしょう

Page 22: InnoDB Table Compression

まずここまででわかること

- ストレージが遅いという前提の設計だが- ストレージが変わろうとも、I/Oのコストは均一な想定。

HDD / SATA SSD / PCI-e SSD どれを使っても、 unzip より I/O の方が50倍遅いという前提になってる

- ワークロードによっては unzip されまくる可能性あり- 最近のSSDかなり速いのでちょっとビミョウ

Page 23: InnoDB Table Compression

では、 zip/unzip するのは誰なのか

- ほとんどがSQL実行するスレッド。クライアントからの connection 受け付けてSQL実行する thread や、slave の SQL thread- SQLなど実行して page を参照/更新する thread たち

- page cleaner thread はすでに圧縮されたページを disk に flushするのであって、 zip/unzip をするわけではない

- ということは、ワークロードによっては、 SQL thread が unzip しまくる可能性

Page 24: InnoDB Table Compression

ここまでで想像できること

- 遅いストレージでI/O多いなら気にしなくていい- HDDならとても相性良さそう

- IOPSを減らしたい、あるいは、とにかくでかいデータを扱う人には嬉しそう

- 古いデータを参照することが少なければ、直近のデータは展開済みページから unzip なしでデータ参照できそうで良さそう- Facebook が酷使するのも納得

Page 25: InnoDB Table Compression

次に、 zip されるタイミング

- INSERTなど更新系クエリが実行される度に圧縮されるわけではない

- Nizam さんのスライド がわかりやすいんですが、圧縮されてるページは、圧縮されたデータと modification log とで構成されている

Page 26: InnoDB Table Compression

modification log

- 先ずは非圧縮なデータとしてページ内の modification log に書き出される。

- modification log が溜まってくると、はじめて圧縮される

Page 27: InnoDB Table Compression

ここで

お手元の gdb で試してみましょう

Page 28: InnoDB Table Compression

gdb は function 定義できるので

- こちらの記事を参考にして定義しておきます- Memory dump formatted like xxd from gdb

-(gdb) define xxd>dump binary memory dump.bin $arg0 $arg0+$arg1>shell xxd dump.bin>end

Page 29: InnoDB Table Compression

こういうtableをつくります

mysql> show create table test\G*************************** 1. row *************************** Table: testCreate Table: CREATE TABLE `test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `val` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=81 row in set (0.00 sec)

Page 30: InnoDB Table Compression

page_zip_write_rec() あたりで break

- このへんが modification log への書き込みになるので

- 適当に break point 張りつつ- 次のような INSERT 流すと

- insert into test values (NULL, 'takanori');- insert into test values (NULL, 'sejima');- insert into test values (NULL, 'test');

Page 31: InnoDB Table Compression

ここからちょくちょくステップ進めていくと

Breakpoint 1, page_zip_write_rec (page_zip=0x7f610ec5bdd0, rec=0x7f6116af00bd "\200", index=0x7f5cdc13eef8, offsets=0x7f5cdc007e88, create=1) at /usr/local/mysql-5.6.28/storage/innobase/page/page0zip.cc:35853585 ut_ad(PAGE_ZIP_MATCH(rec, page_zip));(gdb) p *page_zip$3 = {data = 0x7f6116aee000 "9\302\033\371", m_start = 112, m_external = false, m_end = 140, m_nonempty = 1, n_blobs = 0, ssize = 4}

Page 32: InnoDB Table Compression

log の領域に書き込まれるとこうなる

(gdb) xxd page_zip->data 1600000000: 39c2 1bf9 0000 0003 ffff ffff ffff ffff 9...............0000010: 0000 0000 0018 9b17 45bf 0000 0000 0000 ........E.......0000020: 0000 0000 0002 0002 00d2 8005 0000 0000 ................0000030: 00bd 0002 0002 0003 0000 0000 0000 0000 ................0000040: 0000 0000 0000 0000 0012 0000 0002 0000 ................0000050: 0002 00f2 0000 0002 0000 0002 0032 6805 .............2h.0000060: e294 ae63 0400 0000 ffff 0300 0176 00a4 ...c.........v..0000070: 0200 0880 0000 0174 616b 616e 6f72 6904 .......takanori.0000080: 0006 8000 0002 7365 6a69 6d61 0600 0480 ......sejima....0000090: 0000 0374 6573 7400 0000 0000 0000 0000 ...test.........(gdb)

Page 33: InnoDB Table Compression

というわけで

- modification log には生データが入ってます- 毎回圧縮されるわけではないです- で、この圧縮云々でチューニングの余地があるのです

Page 34: InnoDB Table Compression

MySQL5.6でmergeされたpatch

- InnoDB Table Compression のヘビーユーザである Facebook からの patch が、 5.6 で merge されました

- InnoDBチームの昔のblogにまとめられています- InnoDB Compression Improvements in MySQL 5.6

- しかし

Page 35: InnoDB Table Compression

なにがうれしいのか

ぱっとみ

わかりにくい(かも)

Page 36: InnoDB Table Compression

一つ一つ

見ていきましょう

Page 37: InnoDB Table Compression

innodb_log_compressed_pages

- REDO log への書き込みを減らせるので- これは純粋に嬉しいですね- REDO log への書き込みが減ると、 MySQL5.6 以降では write combining 効きやすくなります

- write combining については、こちらのスライドを参照してください- 5.6 以前の InnoDB Flushing

Page 38: InnoDB Table Compression

innodb_compression_level

- ふつう、ここいじるとしたら圧縮効率を良くしたいって想像すると思うんですけど

- たぶん、 Facebook 的には違うと思うんですよね

- levelを下げる方向で使いたいんだと思います- 詳しくは後ほど

Page 40: InnoDB Table Compression

結論から

先にいうと

Page 41: InnoDB Table Compression

compress と recompress 遅いので

そのあたりを軽減

Page 42: InnoDB Table Compression

圧縮率の良し悪しが与える影響

- 先ずは平塚さんのblogを振り返ってみましょう- 「半分より小さく圧縮できても無駄ってこと?」という疑問があります。「もったいない」感はありますが、無駄というのは言いすぎかもしれません

- 圧縮されたデータと一緒に modification log が格納されるので、圧縮率が良いと、 modification log がたくさん格納できます

Page 43: InnoDB Table Compression

- ただ、 「modification log たくさん詰め込めないけど圧縮軽い」と、「modification log たくさん詰め込めるけど圧縮重い」だと、後者の方が更新処理遅くなる可能性があります。

- 詳しくは後ほど

Page 44: InnoDB Table Compression

recompression の改善

- MySQL5.5 以前は、 Padding されてなかったので、 compression failure からの recompression 発生しやすかった。- (長い文字列のように、圧縮効率よいものだと起きにくい

かもしれませんが)、 modification log 適用して再圧縮

したとき、圧縮済みのページにデータが収まらない可能

性があります。- そうなると、ページを分割して、それぞれ圧縮し直しに

Page 45: InnoDB Table Compression

そこで Adaptive Padding

- modification log が一杯になる前に圧縮し始めて、「compression failure からの recompression」の頻度を下げるのが狙い

- innodb_compression_pad_pct_max で展開済みページ内の空き領域の最大値を決める- 空き領域使い切ったら予め page を分割して、

compression failure を避ける

Page 47: InnoDB Table Compression

このへんの挙動は

- 詳細が気になる人は、 dict_index_zip_pad_update() や dict_index_zip_pad_optimal_page_size() あたりを読んでいただけばよいのでは

Page 48: InnoDB Table Compression

Padding は動的に最適化されるので

- innodb_compression_pad_pct_max と innodb_compression_failure_threshold_pct は、とりあえずデフォルトで試せばいいんじゃないかなぁ

- innodb_compression_level は用途次第ですけど

Page 49: InnoDB Table Compression

幅広く使いたいなら

- innodb_compression_level = 1- compression を軽く

- transaction の実行時間などが compression の影

響を受けにくくなる

- KEY_BLOCK_SIZE=8- compression failure 避ける

- 次の二つはとりあえずデフォルトでいいかな?- innodb_compression_pad_pct_max- innodb_compression_failure_threshold_pct

Page 50: InnoDB Table Compression

とにかく圧縮したいなら

- innodb_compression_level = 9- KEY_BLOCK_SIZE は 4 以下

- 圧縮しまくる方向 == modification log を最小にする方

向で

- KEY_BLOCK_SIZE を 4 以下に下げるときって、とに

かく圧縮率がよいデータを入れるときくらいじゃないかな

- 次の二つはデフォルトでもいいかも- innodb_compression_pad_pct_max- innodb_compression_failure_threshold_pct

Page 51: InnoDB Table Compression

- とりあえず INNODB_CMP で次の比率(compressの成功率)を確認するのが良いと思います- COMPRESS_OPS_OK / COMPRESS_OPS

- 更新頻度高いと、使っているうちにcompression failure 発生して、compress の成功率が変わったりするでしょうから、ときどきは確認しても良いでしょう

Monitoring については

Page 53: InnoDB Table Compression

patch を理解するには背景を知るとよい

- MySQL 5.6 で merge された InnoDB Table Compression の patch は

- 初見の人が Facebook のことを考えずに理解するのは難しい(と思う)

- よくわからん patch が入ったら、その人たちの blog 読むなりして、その背景を理解するのが近道

Page 54: InnoDB Table Compression

InnoDB Table Compression の弱点

- わりと複雑。初見でヒント無しでパラメータチューニングまで理解するのは難しい- コード追いかけるのもだいぶめんどくさい

- buffer pool 上に圧縮済みページと展開済みページとを持つので、メモリをけっこう使う

Page 55: InnoDB Table Compression

参照性能はそこそこだけど

- 何はともあれ、 zlib での圧縮/展開が重い- unzip しなくていい状態はけっこう速い

- あと、mutex の競合が増える- 展開済みページを割り当てるとき、 buffer pool の

instance 単位でしか存在しない mutex が競合

- innodb_buffer_pool_instances を増やすのが有効

なのは このあたりのはず

- ただ、(5.6以降だと改善してるのか)read only なと

きは、 unzip の方が性能に与える影響大きいかも

Page 56: InnoDB Table Compression

そして何より

Page 57: InnoDB Table Compression

更新性能が

イマイチ

Page 58: InnoDB Table Compression

sysbench 0.5 で MySQL5.6 試すと

- 次のように prepare して- https://gist.github.

com/sejima/dabfb85eea99459f78dd- 次のようにalterして

- https://gist.github.com/sejima/64eb1cc7d7fd0dd9e683

- これで 2.4GB くらいなので、 buffer pool には楽勝で収まります

Page 59: InnoDB Table Compression

- 次のように SHOW ENGINE INNODB STATUS しながら- https://gist.github.

com/sejima/7e7d3d31341ee957d35a- 次のように oltp.lua を実行すると

- https://gist.github.com/sejima/d22d8814b46ae29b6131

Page 61: InnoDB Table Compression

いわゆる

index->lock

Page 63: InnoDB Table Compression

何よりもつらいのは

- index->lock を取得してから圧縮するケース- B+Tree が更新されるとき、(5.6以前は)ツリー全体が

排他ロックされるのだが、排他ロック取得してからページ

が圧縮されるケースがある。

- というわけで、 B+Tree 更新するようなクエリが飛んで来

ると、B+Tree経由でのそのテーブルへのアクセスがブ

ロックされる(超ざっくりいえばテーブルロック)- 少ない table を多くの Thread から更新するとつらい

Page 64: InnoDB Table Compression

B+Tree の更新とは

- page 内のレコードの件数がある程度増えたり減ったりすると、 page の分割やマージが発生する- これは compression failure が発生しない状況でもさけ

られない。極論すると、 16KB の page にMB単位の

データを INSERT できるはずがない。

- page の分割やマージが発生したタイミングで、 index->lock つかんだまま圧縮されてしまう

Page 65: InnoDB Table Compression

mutex 競合しやすいと core 使いにくい

- 少ない mutex を取り合うと、 CPUのcoreが増えたとき使い切るのが難しくなる

- mutex を取り合ってるとき、 spin loop が回って、 spin lock で CPU 浪費してる可能性もある- InnoDB の spin lock について興味のある方は、詳しく

はこの記事読んでください- InnoDB の mutex の話(入門編)

Page 66: InnoDB Table Compression

閑話休題

- ベンチマークには要注意- 実行する Thread の数と Table の数が等しかった場合、 index->lock の競合が発生しない場合もある- 実際、 index->lockの競合が発生するケースとしない

ケースで、 InnoDB Table Compression の性能はぜん

ぜん違う

- 実際のワークロードを想定して評価しよう

Page 67: InnoDB Table Compression

5.5 と比べ、 5.6 でだいぶ良くなったけど

- 5.6 のデフォルトパラメータでもけっこういけるけど、パラメータチューニングすると、 spin round や os wait などを減らせる。- innodb_compression_level 下げて- Adaptive Padding に期待する

- innodb_buffer_pool_instances 増やすのも試していい

かも

Page 68: InnoDB Table Compression

あと、5.6では

- lru_scan_depth という概念ができて、なるべく free page 確保されるようになった- 5.5 は free page 使い切る設計なので、 single page

flush が 5.6 より発生しやすい

- single page flush と compression failure のコンボとか

どう考えても辛そう

- single page flush などについては こちら を参考に

Page 69: InnoDB Table Compression

個人的に思うのは

- InnoDB Table Compresion で CPU をうまく使い切れるか?と言われると、ちと難しい。CPUスケーラビリティがよくない- index->lock を排他ロックしつつ圧縮はしんどい。更新

が増えると mutex が競合せざるを得ないのでは- CPU活用したいなら、テーブル分割がオススメ

- spin round や os wait を Monitoring しても良いかも

Page 70: InnoDB Table Compression

ただ、用途次第

- 更新処理が多いところに使いたいなら、CPUはスカスカになるよう、割りきって運用するのがいいかも。- index->lock の排他ロック取った後に圧縮するケースあ

るから、どうしても並列度下がるんで

- I/OバウンドなDBや、 とにかくデータがデカイDBを、50%くらいに圧縮して保存したいならあり。テキストデータなら相性いいだろうし。

Page 71: InnoDB Table Compression

なるほどこれは

複雑だ

Page 72: InnoDB Table Compression

思うに

- InnoDB Table Compression は、とても良く出来ていると思いますし、テキストデータが多い大規模SNSの Facebook にはマッチしたんでしょうが

- もう7年以上前にリリースされた機能みたいですし、将来のハードウェアの進化を考えると、リファクタリングしたほうが良いのかな?という時期が差し迫ってきているんでしょう

Page 73: InnoDB Table Compression

そして出てきた

- Worklog 7696- Allow transparent page level compression in the IO

layer. The InnoDB row level compression a.k.a InnoDB compressed tables is not fast enough and secondly the implementation is more complicated than it should be. The transparent page level compression complements the old scheme it doesn't aim to replace it.

Page 74: InnoDB Table Compression

InnoDB Page Compression は

どのように設計されたのか?

Page 75: InnoDB Table Compression

つづく