MySQLのストレージエンジンを高速にビルドする際のTIPS
こんにちわ,NTT コンピュータ&データサイエンス研究所の中園 @nikezono です.
今回はMySQLのストレージエンジンを開発するにあたってのちょっとしたTIPSを3つ,紹介します.
TL;DR;
1. mysql-server のすべてをビルドする必要はないので,ライブラリだけをビルドしよう
2. ftls_model コンパイルオプションに気をつけよう
3. github actionsでビルドできるようにコンパクトに作っておこう
背景
これはMySQL内部に複数のストレージエンジン実装を用意するもので,DBAが自分のアプリケーションのワークロードに合わせて適切な実装を使い分けられるようになっています.
この記事は,このような新しいストレージエンジンを作る方に向けた,ビルドに関するTIPSです.開発そのものについてはこの記事では取り扱いませんが,以下の記事などが日本語の資料としてはオススメです.
こちらは少し情報が古いですが,アウトラインを掴むには良かったです
少し前までは "MySQL Internals: Writing a Custom Storage Engine" というGetting Started なドキュメントが公式で用意されていたのですが,現在は見つかりません.MySQL公式のドキュメントでもリンクは変わらず貼られているのですが,アクセスはできません.また読みたいので,どなたか知っている方がいれば教えて下さい.まあ,結局デバッガ片手にMySQLのコードリーディングすることになるのですが・・・
TIPS(本編)
1. ストレージエンジンだけをビルドする
多くの資料では,ストレージエンジンをビルドするためにmysql-server のリポジトリ全体をビルドしています.
例えば以下のような感じです.
code: (bash)
# build mysql-server
git clone git@github.com:mysql/mysql-server.git
cd mysql-server
mkdir storage/my_awesome_engine;
# ...develop a storage engine...
cmake
make
このようなやり方は初回のビルド時間が長くなります.具体的には,この記事を執筆している時点(2022/12)のGitHub Actions環境ではおよそ1時間以上かかります.
差分ビルドが効くローカル環境ではいいのですが,毎回クリーンからビルドするCI/CD環境などでは特にきついです.
CI環境でもキャッシュを使うという手もありますが・・・.クリーンインストール or ビルドする際の時間が短いに越したことはないですよね.
これを改善するために,以下のようにしましょう.
1. ビルド済みのバイナリを拾ってくる
ビルド済みのバイナリは apt でも brew でも好きなところから引っ張ってきましょう.
2. ストレージエンジンだけをビルドする
自分のストレージエンジンは plugin_output_dir 以下にビルドされます.
3. ビルド済みのバイナリに作ったストレージエンジンをインストール/リンクする
プラグインのインストール先は SHOW VARIABLES LIKE 'plugin_dir' コマンドを投げれば確認できますので,ここにビルドした共有ライブラリを配置しましょう
こうすることで, apt や brew でリリースされているメジャーバージョンに合わせて開発することができ,互換性も検証できます.
ただし,一つ注意点があります.apt などからインストールするMySQLのバージョン ( mysql --version )と,ビルドする際に使うソースのバージョンは一致していなければなりません.
そうでないと
code: (log)
Can't open shared library 'my_awesome_engine.so' (errno: 0 API version for MY_AWESOME_ENGINE plugin is too different)
というエラーが出ます.
この記事を執筆している時点では apt でインストールできるMySQLのバージョンは8.0.31ですので, git checkout mysql-8.0.31 しないといけないということです.このバージョンは SELECT version(); コマンドを投げれば得られます.
まとめると,以下のような流れです.
code: (bash)
sudo apt install mysql-server # or brew install mysql
sudo service mysql start
# build only the module
git clone git@github.com:mysql/mysql-server.git
cd mysql-server
git checkout mysql-$(echo "select version();" | mysql -uroot -N)
mkdir storage/my_awesome_engine; ... # develop a storage engine
cmake -DCMAKE_BUILD_TYPE=Debug
make my_awesome_engine
# install plugin
sudo cp plugin_output_directory/my_awesome_engine.so $(echo "SHOW VARIABLES LIKE 'plugin_dir';" | mysql -uroot -N | awk '{print $2}')
echo "INSTALL PLUGIN my_awesome_engine SONAME 'my_awesome_engine.so'" | mysql -uroot
これでかなりビルドが高速化します.この記事を執筆している時点のGitHub Actions環境ではおよそ4分でビルドが終わりました!
2. ftls_model コンパイルオプションに気を使う
上記のようなビルド手順を踏んでも, INSTALL PLUGIN でコケることがあります.具体的には
code: (log)
Server Can’t open shared library ‘/path/to/dev-dir/plugin_output_directory/my_awesome_engine.so’ (errno: 2 /usr/lib/x86_64-linux-gnu/libjemalloc.so.2: cannot allocate memory in static TLS block). といったエラーが出ます.
このエラーは単純に jemalloc をリンクするのを辞めるか,あるいは ftls_model コンパイルオプションを適切に指定すると回避できます.
code: (cmake)
find_package(jemalloc)
target_link_libraries(my_awesome_engine ${JEMALLOC_LIBRARIES})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftls-model=local-dynamic")
ftls_model コンパイルオプションに指定できる値はこちらなどに詳しいです. global-dynamic か local-dynamic を指定しておけば,ストレージエンジンをdlopenできます.
個人的には,そもそも予めMySQLのストレージエンジンとして実装すると決まっているプロジェクトなら,jemallocをリンクする必要はないかと思います.
3. GitHub ActionsでCI/CDする
以上の手順を踏まえて,GitHub ActionsでCI/CDしてみましょう.
ここでは,以下のようなテストが通ればよしとします.
code: (python)
import mysql.connector
if __name__ == '__main__':
db = mysql.connector.connect(host="localhost", user="root")
cursor = db.cursor()
cursor.execute('DROP DATABASE IF EXISTS st_test')
cursor.execute('CREATE DATABASE st_test')
cursor.execute('CREATE TABLE st_test.items (\
title VARCHAR(50) NOT NULL,\
content TEXT\
) ENGINE = my_awesome_engine')
db.commit()
cursor.execute(
'INSERT INTO st_test.items (\
title, content\
) VALUES ("alice", "alice meets bob")'
)
db.commit()
以下のようなYAMLをプロジェクトの .github/workflow/ci.yml として置いておきましょう.
code: (yaml)
name: Build & test the example storage engine
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: start MySQL server
run: |
sudo systemctl start mysql.service
- name: build
run: |
./build.sh
sudo cp build/plugin_output_directory/my_awesome_engine.so $(echo "SHOW VARIABLES LIKE 'plugin_dir';" | mysql -uroot -proot -N | awk '{print $2}')
echo "INSTALL PLUGIN my_awesome_engine SONAME 'my_awesome_engine.so'" | mysql -uroot -proot
- name: test
run: |
pip3 install -r requirements.txt
pyton3 test.py
build.sh には各自のビルド手順を書いておきます.これで GitHub Actions環境でのテストができました.
サンプル
サンプルコードを用意しました.
こちらはEXAMPLEストレージエンジンをビルドするもので,上記のTIPSを盛り込んでいます.よければテンプレとしてどうぞ. 終わりに
私の所属するNTT研究所でも,LineairDBというキーバリューストアのOSSを公開しており,これをコアにしたMySQLストレージエンジンを開発しています.まだ実用には程遠いものですが,高速な書き込み処理や先進的なインデックス実装など,色々な機能を盛り込んでいます.Contributeしてもらえると嬉しいです!