こんにちは、株式会社リブセンスの吉田健太郎です。前回に続いて、私が体験したTritonnからmroongaのシステム移行プロジェクトを舞台裏からお届けします。
これまでのあらすじ
将来的な技術負債を残さない、そしてInnoDBの性能向上等の恩恵を受けるため、もはやレガシーとなったMySQL 5.0を捨てて、MySQL 5.6への移行を行いたい。しかしSolrへ乗り換えるほどでもなく、引き続きシンプルにSQLを用いた、リレーショナルな日本語対応の全文検索を使いたい。この構想を実現するため、groongaのMySQLバインディング版である「mroonga」を用いた、MySQL 5.6への移行プロジェクトが始動しました。
そのパート1である前回 は、主に次のトピックについて解説しました。
Tritonnとmroongaそれぞれの紹介と移行時の要注意点
現在利用できる日本語全文検索ソリューションの比較
ストレージエンジンをMyISAMからmroongaへ移行する際に気をつけたいAuto Incrementの挙動の違い
今回の流れ
Tritonnからmroongaへの移行はもちろんのこと、稼働中のシステムを最小ダウンタイムで移行したい。それを叶えるにはMySQL 5.6を、既存のMySQL 5.0を用いたMaster-Slave構成のレプリケーションに追加し、段階的移行をする必要があります。この段階的移行おうとする中で、Tritonn(MySQL 5.0)で動く更新系クエリをMySQL 5.6およびmroonga互換にするという壁が現れます。
今回はその壁を乗り越え、MySQL 5.0のレプリケーションマスタにMySQL 5.6をスレーブとして追加するところまでを、詳しく解説していきます。
MySQL 5.0からMySQL 5.6へのデータマイグレーション(事前準備編)
移行対象のサーバ構成
移行対象のサーバ構成は次の図の通り、レプリケーションマスタをDRBDで冗長化している以外、オーソドックスなMySQLのMaster-Slave構成のレプリケーションです。
移行前のサーバ構成
これらは、Tritornnの最終リリース版であるMySQL 5.0.87 + Senna 1.1.4 + mecab 0.98を利用し、レプリケーションマスタはDRBD + keepalivedを用いた冗長化を行っています。ストレージエンジンは基本的にInnoDBを、フルテキスト検索を用いる場合にはMyISAMを利用しています。
移行の進め方とサーバ構成
既にTritonnで稼働中のシステムをmroongaへ移行するわけですから、ダウンタイムを最小にしたアップグレードを行う必要があります。
そこで、既存MySQL 5.0レプリケーションマスタの下に、MySQL 5.6のみで構成するレプリケーションを追加するという手法を採用しました。この、いわゆる「孫スレーブ構成」を取ることで、プログラムからの更新系・参照系クエリの発行先をMySQL 5.6のマシンに切り替えるだけで移行ができます。
今回の記事では第1段階のステップ5、MySQL 5.0と5.6の混在レプリケーション構成を作るまでを解説します。
第1段階
MySQL 5.0のスレーブにMySQL 5.6を追加します。月に1度しか動かないCronによる定期実行タスクを考慮し、この状態で1ヶ月の稼働試験期間を設けます。後ほど紹介する、変更が必要な更新クエリを先に潰せていれば特にトラブルはありませんが、レプリケーションが止まるようなクエリに遭遇した場合には随時対処しましょう。
MySQL 5.0配下にMySQL 5.6をスレーブとして追加した後のサーバ構成
具体的には次のように、6つのステップに分けて進めます。
プロダクション環境のスレーブを複製し、MySQL 5.0用の作業サーバを用意する
MySQL 5.0用の作業サーバから、Tritonn依存のFULLTEXTインデックスを除去する
Tritonn依存の無くなったMySQLのダンプデータをMySQL 5.6へ流し込む
更新クエリをTritonn・mroonga互換の実装にする
既存のMySQL 5.0レプリケーション構成に、ダンプデータの流し込まれたMySQL 5.6をスレーブとして追加する
MySQL 5.6スレーブにmroongaをインストールし、mroongaを用いたフルテキスト検索対応のスキーマに更新する
第2段階
MySQL 5.0のスレーブに追加したMySQL 5.6を複製し、孫Slaveを追加します。これで、MySQL 5.6のみで構成するレプリケーションが構築できました。この時期には参照クエリに関してもTritonn/mroonga両対応になりましたので、影響範囲や時間を絞った上で、参照クエリのみMySQL 5.6のスレーブに振り分けるという稼働試験を行いました。
MySQL 5.6のマスタ・スレーブ構成を追加した後のサーバ構成
具体的には次のような細かいタスクとなります。
MySQL 5.6スレーブをの配下に、MySQL 5.6の孫スレーブを作る
準同期レプリケーションの設定をMySQL 5.6のレプリケーションマスタ・スレーブに行う
参照クエリ稼働試験として、影響範囲と時間を限定した上で部分的にプロダクション環境へ投入する
プロダクション環境からの参照クエリを、MySQL 5.6スレーブに完全に切り替える
第3段階
プログラムから利用するサーバを、MySQL 5.6のレプリケーションマスタ・スレーブに完全に切り替えます。レプリケーションマスタの切り替え以降は、データの整合性的な意味合いで切り戻しができません。そのため、慎重に行う必要があります。
MySQL 5.6のマスタ・スレーブ構成ヘ切替後のサーバ構成
具体的にはこのような細かいタスクとなります。
更新系クエリを止めるため、サイトをメンテナンス画面に切り替えてオフラインにする
MySQL 5.6にバイナリログがすべて到達していることを確認した後に、レプリケーションから切り離す
MySQL 5.6のレプリケーションマスタに更新クエリが直接発行されるアプリケーションをデプロイする
この段階的移行手法をとる場合の注意として、現レプリケーションマスタで実行される更新系クエリがMySQL 5.0およびmroonga (MySQL 5.6)の両方で動く必要があります。
具体的にはFULLTEXTインデックスへ作用するCREATE TABLEやALTER TABLEといったスキーマを修正するクエリが対象となります。例えば、検索テーブルにおいて参照するテーブルと更新するテーブルを分けて、更新が終わり次第その2テーブルを入れ替えるという運用の場合には確認が必要です。この2テーブル運用は、更新中は排他ロック (テーブルロック) となり、参照クエリの実行ができないというMyISAMの制約から生まれたパフォーマンスチューニングです。
更新クエリをTritonn・mroonga互換の実装にする
周知の通り、MySQL 5.0およびTritonn専用の更新クエリが発行される状態では、レプリケーションのスレーブとして追加できません。そこでまずは、MySQL 5.0で動く既存システムへ発行される更新系クエリの精査を行います。MySQL 5.6環境でも互換のある実装にしておくことでMySQLバージョン混在でのレプリケーションが可能となり、ダウンタイム最小での移行を果たせます。
それでは、どういったクエリをどのように書き換えることで実現できるのか、具体例と共に紹介します。
プログラムの修正の必要がある実装例
問題となるmroonga非互換のSQLはCREATE TABLE文のFULLTEXTインデックスにあります。厳密には、USING句からのオプションです。例えば、次のようなSQLです。
CREATE TABLE search_fulltext_ready (
id INT ,
content TEXT ,
FULLTEXT INDEX ft USING MECAB , NORMALIZE ( content )
) ENGINE = MyISAM DEFAULT CHARSET utf8 ;
Cronでの定期実行タスクなどによりTritonn専用のCREATE TABLE構文などが発行されると、mroongaでレプリケーションエラーが起きてしまいます。私のケースではCronで次のようなSQLを発行していました。
CREATE TABLE search_fulltext_ready (... FULLTEXT INDEX ft USING MECAB ( content ) ...;
LOAD DATA INTO INFILE search_fulltext_ready ...;
RENAME TABLE search_fulltext TO search_fulltext_old , search_fulltext_ready TO search_fulltext ;
DROP TABLE search_fulltext_old
では、このSQLをどのように変えれば解決できるか、追って解説しましょう。
2つのテーブルを入れ替えるという修正方法
mroonga互換の更新系クエリにするには、単純にCREATE TABLE文をCREATE TABLE search_fulltext_ready LIKE search_fulltext;
というクエリに置き換えるだけと思いきや、ここに落とし穴があります。それは、Tritonnの制限事項 にも記述されている「USING句が落ちる」問題です。
この「USING句が落ちる」とは、オリジナルが何であろうともUSING MECAB, NO NORMALIZE, 512
と書き換わる現象です。これはTritonnのデフォルトであるUSING MECAB NORMALIZE
とは異なるものです。
「TRUNCATE TABLE t1を実行するとUSING句情報が落ちる」
この問題は、WHERE句を省略したDELETE文を使うことで解決します。
DELETE FROM search_fulltext_ready ;
「CREATE TABLE t2 LIKE t1を実行するとUSING句情報が落ちる」
この問題は、事前に用意した2つのテーブルを入れ替える運用で解決します。
RENAME TABLE search_fulltext TO search_fulltext_old ,
search_fulltext_ready TO search_fulltext ,
search_fulltext_old TO search_fulltext_ready ;
具体的な修正は、次の手順で行います。
DELETE FROM search_fulltext_ready ;
LOAD DATA INTO INFILE search_fulltext_ready ...;
RENAME TABLE search_fulltext TO search_fulltext_old ,
search_fulltext_ready TO search_fulltext ,
search_fulltext_old TO search_fulltext_ready ;
特に該当するものが無ければ、影響しうるような改修が追加で行わないよう、チームへのコードフリーズの周知を行うだけで事足りるでしょう。
データマイグレーション計画
前項にてMySQLバージョン混在のレプリケーションを行うための互換プログラムへの改修を取り上げました。次は、既存データを何らかの方法でMySQL 5.6に流し込み、MySQL 5.0のスレーブとしてレプリケーションさせる準備を行います。
MySQL 5.0と5.6の作業サーバ、計2台を利用したデータのマイグレーションの手順は次の通りです。
本番稼働中の待機系スレーブを一時的に切り離す
待機系スレーブの内容をMySQL 5.0用の作業サーバに複製する
MySQL 5.0用の作業サーバからMySQL 5.6用の作業サーバにデータを流し込む
大まかにはこのような手順を踏むことで、MySQLのアップグレードができます。
それでは次に、具体的なデータマイグレーションを行う2つの方法を紹介します。
なお、事前に次の作業は済んでいるものとします。
MySQL 5.0と5.6の作業サーバを構築する
本番稼働中の待機系スレーブを一時的に切り離す
待機系スレーブの内容をMySQL 5.0用の作業サーバに複製する
方法1: 段階的なアップデート
MySQLのアップグレードを行う一般的な手法として、mysql_upgradeコマンドがよく知られています。しかし今回は2つ飛び越えての大幅なアップデートとなるため、次のようにとても手間がかかります。
MySQL 5.0用の作業サーバにて、Tritonn依存のないスキーマに変更する
この作業サーバにて、MySQLを5.1へアップデートする
mysql_upgradeコマンドを用いて データもMySQL 5.0 から 5.1へアップデートする。エラーが起きたら都度修正する
この作業サーバにて、MySQLを5.5へアップデートする
mysql_upgradeコマンドを用いて データもMySQL 5.1 から 5.5へアップデートする。エラーが起きたら都度修正する
この作業サーバにて、MySQLを5.6へアップデートする
mysql_upgradeコマンドを用いて データもMySQL 5.5 から 5.6へアップデートする。エラーが起きたら都度修正する
全文検索を行いたいテーブルを、ALTER TABLE構文を用いてmroonga化する
方法2: mysqldumpを用いた移行方法
もう一つの方法として、mysqldumpコマンドを用いる手法があります。これは、データベースをSQLファイルに書き出した後に、MySQL 5.6のクリーンなデータベースに流し込むというものです。この場合には次のように、比較的シンプルな方法で移行を行えます。
MySQL 5.0用の作業サーバにて、Tritonn依存のないスキーマに変更する
MySQL 5.6用の作業サーバにて、mysqldumpでMySQL 5.0用の作業サーバよりデータベース毎に吸い出す
MySQL 5.6用の作業サーバにて、吸い出したダンプファイルを自身に流し込む
MySQL 5.6用の作業サーバにて、全文検索を行いたいテーブルを、ALTER TABLE構文を用いてmroonga化する
そのまま移すことができないMySQLユーザを、手動で適宜追加する
今回採用した方法
段階的バージョンアップに手間を感じたことと、InnoDBのibdataファイルの肥大化を解消したいという要件があるため、mysqldumpを用いた移行方法を採用します。
というのもMySQL 5.0ではまだ目新しかったinnodb_file_per_table
設定を利用していないため、InnoDBの共有データスペースであるibdata1が数百GBレベルに肥大化していたのです。この肥大化の原因となったテーブルを削除したとしても一度拡張されたらデータサイズが減ることはないため、縮小するにはSQLダンプファイルから作り直すほかに方法はありません。今回のタイミングを機にmysqldumpで生成したSQLダンプファイルより、1から綺麗に作り直しを行うことにしました。
MySQL 5.0からMySQL 5.6へのデータマイグレーション(前編)
MySQL 5.0からMySQL 5.6へのデータマイグレーションの手順については前述の通りです。ステップを追って進めていきます。
移行時に使うサーバとその使い分け
CentOS 6.4(X86_64)のサーバを2台、次の使い分けで作業サーバとします。
MySQL 5.0用の作業サーバ
MySQL 5.6用の作業サーバ
MySQL 5.0 のインストール
MySQL 5.0用の作業サーバへ、SourceForgeのTritonnバイナリ配布ページ より必要ファイルを入手の上、yum localinstall
コマンドでインストールします。
インストール後、必要に応じて/etc/my.cnfの書き換えを行います。
yum localinstall \
mecab-0.98-tritonn.1.0.12a.x86_64.rpm \
mecab-ipadic-2.7.0.20070801-tritonn.1.0.12a.x86_64.rpm \
senna-1.1.4-tritonn.1.0.12a.x86_64.rpm \
MySQL-shared-5.0.87-tritonn.1.0.12a.x86_64.rpm \
MySQL-client-5.0.87-tritonn.1.0.12a.x86_64.rpm \
MySQL-server-5.0.87-tritonn.1.0.12a.x86_64.rpm \
MySQL-devel-5.0.87-tritonn.1.0.12a.x86_64.rpm
MySQL 5.6 のインストール
MySQL 5.6用の作業サーバへ、オラクルが配布している公式rpm を用いて、yum install
コマンドでインストールします。なお、まだこの段階ではmroongaを使いません。次回、MySQL 5.6用のrpmパッケージのビルド方法を紹介します。インストール後、必要に応じて/etc/my.cnfの書き換えを行います。
$ sudo yum install \
http://cdn.mysql.com/Downloads/MySQL-5.6/MySQL-shared-compat-5.6.12-1.el6.x86_64.rpm \
http://cdn.mysql.com/Downloads/MySQL-5.6/MySQL-shared-5.6.12-1.el6.x86_64.rpm \
http://cdn.mysql.com/Downloads/MySQL-5.6/MySQL-client-5.6.12-1.el6.x86_64.rpm
http://cdn.mysql.com/Downloads/MySQL-5.6/MySQL-server-5.6.12-1.el6.x86_64.rpm \
http://cdn.mysql.com/Downloads/MySQL-5.6/MySQL-devel-5.6.12-1.el6.x86_64.rpm
MySQL 5.0用の作業サーバへデータを投入する(プロダクション環境サーバでの作業)
まずはプロダクション環境サーバでの作業での作業を行います。Tritonn依存のないスキーマに変更するために、MySQL 5.0用の作業サーバへプロダクション環境のデータを複製します。何かしらの方法で数時間オフラインにできるサーバを確保してから、次のようにデータを吸い出します。
なお、問答無用でMySQLのサービスを止めてしまうと、一時テーブルが消失してレプリケーションエラーが発生することがあります。そのため、Slave_open_temp_tables
の値を確認しながらの作業を行います。こちらの詳細はMySQL公式ドキュメントレプリケーションとテンポラリ テーブル を参照ください。
stop slave ;
mysql > show status like 'Slave_open_temp_tables' ;
+
| Variable_name | Value |
+
| Slave_open_temp_tables | 0 |
+
1 row in set ( 0.00 sec )
start slave ;
stop slave ;
show status ;
レプリケーションを止めることができたら、続けてMySQLのサービスを停止し、MySQLのデータディレクトリを、MySQL 5.0作業マシンへscpなどで転送します。
$ mysql -uroot -p -e "SHOW SLAVE STATUS\G"
$ sudo /etc/init.d/mysql stop
$ sudo scp -p /etc/my.cnf ユーザ名@MySQL 5.0用の作業サーバIP:/tmp/
$ sudo scp -rp /var/lib/mysql ユーザ名@MySQL 5.0用の作業サーバIP:/tmp/mysql
$ sudo /etc/init.d/mysql start
$ mysql -uroot -p -e "SHOW SLAVE STATUS\G"
MySQL 5.0用の作業サーバへデータを投入する(MySQL 5.0作業マシンでの作業)
プロダクション環境サーバからのコピーが完了したら、MySQL 5.0作業マシンでの作業を始めます。必要ファイルをそれぞれ配置した後、MySQLサービスを開始します。
/etc/init.d/mysql stop
$ sudo mv /var/lib/mysql /var/lib/mysql-default
$ sudo mv /tmp/mysql /var/lib/mysql
$ sudo chown -R mysql:mysql /var/lib/mysql
$ sudo mv /tmp/my.cnf /etc/my.cnf
$ sudo vi /etc/my.cnf
$ sudo /etc/init.d/mysql start
$ mysql -uroot -p -e "SHOW SLAVE STATUS\G" | grep Running
Tritonn依存のないスキーマに変更する
Tritonn依存のスキーマがない状態になってはじめて、MySQL 5.6用の作業サーバへデータの流し込みができるようになります。ここでは、FULLTEXTインデックスを一旦除去するためのDROP INDEX文を作成します。
それでは、MySQL 5.0用の作業サーバにて、mysqldumpコマンドを利用してスキーマを取り出します。なお、このファイルは後ほどスキーマをmroonga対応にする際にも利用しますので、消さずに保管しましょう。
$ mysqldump -uroot -p --no-data --triggers \
--routines データベース名 > /path/to/データベース名.sql
書き出したSQLファイルの中に、"FULLTEXT"という行を持つテーブルをリストアップしましょう。今回はそれらテーブルを対象に、ALTER TABLEクエリを作成していきます。
このスキーマで作られたテーブルを例に説明します。
CREATE TABLE search_fulltext (
id INT ,
content TEXT ,
FULLTEXT INDEX ft USING MECAB , NORMALIZE ( content )
) ENGINE = MyISAM DEFAULT CHARSET utf8 ;
ft
という名前でフルテキストインデックスが作られているため、次のクエリでTritonn依存のインデックスを削除できます。同様にその他のテーブルに関しても確認を行い、MySQL 5.0用の作業サーバにてそれぞれ実行します。
ALTER TABLE search_fulltext DROP INDEX ft ;
MySQL 5.0のSQLダンプを作成
先ほどの作業により、MySQL 5.0用の作業サーバにあるデータからTritonn依存のスキーマはなくなりました。ようやく、MySQL 5.0用の作業サーバよりmysqldumpコマンドを用いてデータベース毎に吸い出すことができます。
早速MySQL 5.6用の作業サーバにて、次のようにmysqldumpを行います。
$ mysqldump -h MySQL5.0サーバのIP -u root -p --quick --no-data \
--triggers --routines --order-by-primary --no-autocommit \
--set-gtid-purged=OFF データベース名 > /path/to/データベース名.sql
なお、MySQL 5.6からの新しいオプション--set-gtid-purged=OFF
は、MySQL 5.6以下のGTID非対応のMySQLからデータをダンプする際に必要になるオプションです。
SQLダンプをMySQL 5.6へインポート
先ほどの作業で吸い出したSQLダンプデータを、MySQL 5.6用の作業サーバへ流し込みます。
$ mysql -uroot -p -e "CREATE DATABSE データベース名"
$ mysql -uroot -p DB < /path/to/データベース名.sql
MySQL 5.6にてレプリケーションを開始
“MySQL 5.0用の作業サーバへデータを投入する(プロダクション環境サーバでの作業)"にてメモを行った、Master_Log_File
と Exec_Master_Log_Pos
を利用し、CHANGE MASTER構文を作ります。
CHANGE MASTER TO
MASTER_HOST = 'レプリケーションマスタのIP' ,
MASTER_USER = 'レプリケーションユーザ' ,
MASTER_PASSWORD = 'レプリケーションパスワード' ,
MASTER_LOG_FILE = 'Master_Log_Fileの値' ,
MASTER_LOG_POS = Exec_Master_Log_Pos の値;
CHANGE MASTER文の準備ができたらいよいよ、レプリケーションの開始です。
SHOW SLAVE STATUS \ G
START SLAVE ;
SHOW SLAVE STATUS \ G
これで無事、MySQL 5.0の下にMySQL 5.6をスレーブとして追加する、MySQL 5.0と5.6の混在レプリケーションが完了しました。
今回はここまでとし、この続きは次回となります。
まとめと次回予告
次回はいよいよ本プロジェクトの大詰めとなります。 次のトピックをお届けする予定です。乞うご期待!
Tritonnからmroongaへのスキーマ書き換えガイド
Tritonnからmroongaへの全文検索クエリ書き換えガイド
CentOS 6+MySQL 5.6環境で動くmroongaのrpmパッケージ構築方法
groongaの利用事例を寄稿しませんか
連載の目的は「読者の皆さんがgroongaを使いたくなる!」ことです。そこで、すでにgroongaを使っており、groongaの利用事例を本連載で紹介していただける人を募集します。募集要項 を参考にご連絡ください。お待ちしています!