MyRocks エンジンとは?
Facebook社が開発したMySQLのストレージエンジンです。RocksDBをバックグラウンドのストレージに使用しており、テーブル圧縮をしたInnoDBと比較して、高い圧縮性能をもち、高速な書き込み処理が可能という特徴があります。
Facebookでは独自に改良されたMySQL(5.6ベース)にMyRocksが組み込まれ、一部本番環境でも動いているようです。
MyRocksはオープンソースとして公開されており、Percona Server や MariaDB ではこれをマージする取り組みが進んでいます。
Percona Server 5.7.18 から実験的ながら このMyRocks エンジンが使えるようになりました。
この記事では、Percona Server でMyRocksのインストール方法、同じデータをインポートして InnoDBとMyRocksでどの程度ディスクスペースに差が出るかを調べたいと思います。
参考
- MyRocks | A RocksDB storage engine with MySQL | MyRocks
- MyRocks Deep Dive
- Percona Server 5.7.18-16
- Announcing MyRocks in Percona Server for MySQL
- Facebook MyRocks at MariaDB
RocksDBとは?
Google社が開発した LevelDB を元に、Facebook社のデータベースエンジニアリングチームが開発を進めているKey/Value型の組み込みデータベースです。
Cassandra や MongoDB の WiredTiger ストレージエンジンでも使われている Log-Structured Merge Tree 構造を採用することでSSDなどの高速ストレージを効率的に使えるように最適化されています。
参考
Log-Structured Merge Tree (LSM Tree)
LSM Tree は挿入処理において高い性能を持つデータ構造です。
複数からなるツリー上のデータ構造を持ち、マージソートを繰り返しながらツリーを大きくします。
参考 : Log-structured merge-tree – Wikipedia
InnoDBのテーブル圧縮処理
InnoDBではデフォルトで16KBのページと呼ばれるスペースで管理されており、1つのページで複数のレコードを保持しています。圧縮はこのページ単位で行われ、ディスクのブロック単位で格納されます。
仮に、16KBのページが圧縮されて5KBになったとしても、ディスクのブロックサイズが4KBの場合、2ブロック消費されるため(8KB)、3KB分無駄なスペースが生まれてしまうことになります。
参考 : MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.7 InnoDB 圧縮テーブル
MySQL5.7からは透過的ページ圧縮の機能が追加され、ページ圧縮を有効にすると、OSとファイルシステムがホールパンチに対応していれば、空のブロックがページの最後から解放されるようになり、テーブル圧縮よりも効率よくスペースが使われるようになりました。
RocksDBの実装と圧縮処理
RcoksDBではソート済みテーブルデータを複数のSST(Sorted Static Table)ファイルに書き出され管理されます。
圧縮処理はこのSSTファイル単位で行われるため、高い圧縮率とディスクスペースに対する効率がInnoDBと比較すると優れています。
MyRocksのデータ削減処理
それ以外にも、MyRocksでは以下の機能によってさらにデータサイズを小さくしています。
Prefix Key Encoding
列単位で同じ値が続く場合は省略することでデータサイズを減らしています。
Zero-filling row metadata
MyRocksでは7byteのシーケンスIDと1バイトのオペレーションタイプを各行ごとに保持しています。ただし、プライマリキーがある場合は二重管理となるため、このシーケンスIDを0にすることでサイズを減らしています。
参考 : MyRocks advantages over InnoDB · facebook/mysql-5.6 Wiki · GitHub
MyRocksのインストール(Percona Server)
MyRocksのコードがマージされていますが、まだテストレポジトリからしか使えないようです。
検証では CentOS 7 で試しました。
1.レポジトリを追加します
1 |
$ sudo yum install -y http://www.percona.com/downloads/percona-release/redhat/0.1-4/percona-release-0.1-4.noarch.rpm |
2.テストレポジトリを有効化します
1 |
$ sudo vi /etc/yum.repos.d/percona-release.repo |
[percona-testing-$basearch] と [percona-testing-noarch] セクションを enabled = 1 に変更する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#################################################################### # Testing & pre-release packages. You don't need it for production # #################################################################### [percona-testing-$basearch] name = Percona-Testing YUM repository - $basearch baseurl = http://repo.percona.com/testing/$releasever/RPMS/$basearch enabled = 1 # 0から1に変更する gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona [percona-testing-noarch] name = Percona-Testing YUM repository - noarch baseurl = http://repo.percona.com/testing/$releasever/RPMS/noarch enabled = 1 # 0から1に変更する gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona |
3.Percona Server をインストールします
1 |
$ sudo yum install -y Percona-Server-server-57 |
4.Percona Server を起動します
1 |
$ sudo systemctl start mysql |
5.rootの 一時パスワードを取得します
MySQL 5.7 (Percona Server) から yum によるインストールの場合、rootの一時パスワードはmysqlのエラーログに出力されれます。
1 2 |
sudo grep password /var/log/mysqld.log 2017-08-14T07:07:17.309459Z 1 [Note] A temporary password is generated for root@localhost: JZquAJe3)uIt |
6.Percona Server にログインしてパスワードを変更します
1 2 3 |
$ mysql -u root -p mysql> alter user root@localhost identified by 'Password1!'; Query OK, 0 rows affected (0.00 sec) |
7.RocksDB パッケージをインストールします
MyRocksエンジンを使うには別途パッケージをインストールする必要があります。
1 |
$ sudo yum install -y Percona-Server-rocksdb-57.x86_64 |
インストール時に以下のメッセージが出るのでスクリプトを動かします。
1 2 3 |
* Run the following script to enable the RocksDB storage engine in Percona Server: ps-admin --enable-rocksdb -u <mysql_admin_user> -p[mysql_admin_pass] [-S <socket>] [-h <host> -P <port>] |
8.MyRocksエンジンを有効にします
1 2 3 4 5 6 7 8 9 |
$ ps-admin --enable-rocksdb -u root -pPassword1! Checking if RocksDB plugin is available for installation ... INFO: ha_rocksdb.so library for RocksDB found at /usr/lib64/mysql/plugin/ha_rocksdb.so. Checking RocksDB engine plugin status... INFO: RocksDB engine plugin is not installed. Installing RocksDB engine... INFO: Successfully installed RocksDB engine plugin. |
9.RocksDB が有効になっていることを確認します
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
mysql> select engine, support from information_schema.engines; +--------------------+---------+ | engine | support | +--------------------+---------+ | ROCKSDB | YES | | CSV | YES | | MEMORY | YES | | BLACKHOLE | YES | | MRG_MYISAM | YES | | PERFORMANCE_SCHEMA | YES | | ARCHIVE | YES | | MyISAM | YES | | FEDERATED | NO | | InnoDB | DEFAULT | +--------------------+---------+ 10 rows in set (0.00 sec) |
これで ENGINE= RocksDB
を指定すれば使えます。
MyRocksのデータ構造
デフォルトでは /var/lib/mysql/.rocksdb
以下にデータが格納されます。
1 2 3 4 5 6 7 |
mysql> show global variables like 'rocksdb_datadir'; +-----------------+------------+ | Variable_name | Value | +-----------------+------------+ | rocksdb_datadir | ./.rocksdb | +-----------------+------------+ 1 row in set (0.01 sec) |
データが投入された ./rocksdb の中はこんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# ls -l /var/lib/mysql/.rocksdb/ total 4268020 -rw-r-----. 1 mysql mysql 67592176 Aug 16 04:28 000068.sst -rw-r-----. 1 mysql mysql 67591582 Aug 16 04:30 000079.sst ...(省略)... -rw-r-----. 1 mysql mysql 41392255 Aug 16 04:55 000286.log -rw-r-----. 1 mysql mysql 41520363 Aug 16 04:56 000288.log ...(省略)... -rw-r-----. 1 mysql mysql 16 Aug 16 04:16 CURRENT -rw-r-----. 1 mysql mysql 37 Aug 16 04:14 IDENTITY -rw-r-----. 1 mysql mysql 0 Aug 16 04:14 LOCK -rw-r-----. 1 mysql mysql 748785 Aug 16 05:10 LOG -rw-r-----. 1 mysql mysql 21396 Aug 16 04:14 LOG.old.1502856862614052 -rw-r-----. 1 mysql mysql 26946 Aug 16 05:10 MANIFEST-000052 -rw-r-----. 1 mysql mysql 6309 Aug 16 04:16 OPTIONS-000057 -rw-r-----. 1 mysql mysql 6310 Aug 16 04:16 OPTIONS-000059 |
[シーケンス番号].sst
ソート済みデータが格納されています。SSTファイルと呼ばれます。InnoDBの場合の .ibd と役割はおおよそ同じです。
[シーケンス番号].log
書き込みリクエストが MemTable と同時にファイルも書き出されます。WALファイルと呼ばれます。ib_logfile と役割はおおよそ同じです。
LOG
MyRocksのログです。現状ではビルド時のファイルパスが出力されていたりと、デバッグログに近い内容だと思われます。
1 2 |
2017/08/16-05:21:31.862038 7f0d84ff9700 EVENT_LOG_v1 {"time_micros": 1502860891862016, "cf_name": "default", "job": 167, "event": "table_file_creation", "file_number": 613, "file_size": 41444261, "table_properties": {"data_size": 41066905, "index_size": 376004, "filter_size": 0, "raw_key_size": 29240265, "raw_average_key_size": 26, "raw_value_size": 19696267, "raw_average_value_size": 17, "num_data_blocks": 10128, "num_entries": 1097620, "filter_policy_name": "", "__indexstats__": "[...5 records...]", "kDeletedKeys": "0", "kMergeOperands": "0"}} 2017/08/16-05:21:31.862049 7f0d84ff9700 [/mnt/workspace/percona-server-5.7-redhat-binary-rocks/label_exp/centos7-64/rpmbuild/BUILD/percona-server-5.7.18-16/percona-server-5.7.18-16/storage/rocksdb/rocksdb/db/flush_job.cc:317] [default] [JOB 167] Level-0 flush table #613: 41444261 bytes OK |
データ圧縮性能比較
今回のデータ圧縮性能比較では英語版Wikipediaのpageテーブルをインポートした後のディスクサイズを比較したいと思います。
使用したデータは以下です。
https://dumps.wikimedia.org/enwiki/20170801/enwiki-20170801-page.sql.gz (1.5GBほどあるので注意)
ダンプファイルではエンジンが InnoDB になっているので RocksDB に変更してデータをインポートします。
1 |
$ zcat enwiki-20170801-page.sql.gz | sed -e s/ENGINE=InnoDB/ENGINE=ROCKSDB/g | mysql -u root -pPassword1! |
my.cnf などの設定値は myrocks を含め全てデフォルトです。
InnoDB(圧縮無し) | MyRocks |
---|---|
11.0GB | 9.1GB (-18%) |
Facebookのベンチマークでは、LinkBenchのデータでInnoDBの圧縮無しの場合とMyRocksのデータベースサイズを比較すると3.5倍ほど小さくなる結果でしたが、今回の検証ではデータサイズが小さいことやパラメーターがデフォルトであることが影響してか想定ほどデータサイズに差が出ませんでした。
(FacebookのベンチマークではInnoDBの圧縮無しで250GB〜330GB程度のデータを使用)
参考 : MyRocks: A space- and write-optimized MySQL database
まとめ
- Percona Server を使えばソースコードからビルドしなくてもパッケージインストールでMyRocksが使えます。
- 今回の検証では残念ながらFacebookのベンチマークほどデータ圧縮効果は見られませんでした。
(追記:9月8日)InnoDBのページ圧縮も含めて追加検証
追加検証では、RocksDBエンジンとInnoDBの圧縮無しだけでなく、InnoDBのページ圧縮も含め、データ投入量を増やしてデータ圧縮性能を比較してみました。
Percona Server のバージョンは前回と同様です。
データ
ベンチマークツールのsysbenchで使われるテーブル構成と同じにしました。また投入するデータについても極力sysbenchと同様にしました。
テーブル構成
1 2 3 4 5 6 7 |
CREATE TABLE `sbtest1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `k` int(11) NOT NULL DEFAULT '0', `c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) /* エンジンの設定等はそれぞれ異なります */; |
投入データ例
1 2 3 4 5 6 7 |
mysql> select * from sbtest1 limit 1\G *************************** 1. row *************************** id: 1 k: 5414058 c: 41630123464-96279403393-27441347157-42730090719-30024255058-97145952815-23248133111-14108627592-92193768271-50491332571 pad: 28754115668-89173835433-74358149582-57373739247-51359594493 1 row in set (0.00 sec) |
測定方法
1回のイテレーションで1テーブル作成し、10,000,000レコードを挿入します。
1イテレーションごとにMySQLのデータディレクトリのサイズを計測しました。
今回はそれを100イテレーション行いました。
測定結果
100イテレーション終了時
エンジン | データサイズ | 圧縮率 |
---|---|---|
InnoDB(圧縮無し) | 214GB | |
InnoDB(圧縮) | 117GB | -46% |
RocksDB | 187GB | -13% |
InnoDBのページ圧縮がもっとも圧縮性能が良いという結果が出ました。
追加検証のまとめ
データサイズを増やしてみましたが、残念ながら思ったほど圧縮性能が出ない結果となりました。
今の段階で性能が出ない原因として考えられるのは
- RocksDBのオプション設定をデフォルトにしているため、圧縮性能がフルに使えていない可能性があります
- Percona ServerへRocksDBのコードがマージされたが最適化が不十分だった可能性があります
- LinkBenchとsysbenchではデータの構成、傾向が異なるため、圧縮に不利だった可能性があります