基盤チーム所属の沖中( @okinaka )です。
「リファクタリング」という言葉、エンジニアのみなさんならご存知でしょう。 システムの振る舞いを変えずに内部を改善することを指す言葉です。
一般的に、コードの修正を指すことがほとんどですが、今回はデータベース設計のリファクタリングについてお話ししたいと思います。
絶版になってしまいましたが、データベース・リファクタリング という書籍に様々な手法が紹介されていて参考になります。英語で良ければ 原書 はまだ入手可能ですね。

- 作者:スコット W アンブラー,ピラモド・サダラージ
- 発売日: 2008/03/26
- メディア: 単行本
商品並び替えの問題
弊社が運営する「BASE 」というサービスは、無料でかんたんにネットショップを開設できるサービスです。Webサービスの他に、購入者向けショッピングアプリ「BASE」、ショップオーナー向けのショップ運営管理アプリ「BASE Creator」、外部システムとの連携のための BASE API なども提供しています。
ショップに商品を陳列する際の並び順は重要な情報です。並び順の情報は、 システムのいたるところで参照されています。
商品の並び順を管理している商品テーブルは、在庫管理などにも用いられるため頻繁に更新がかかるテーブルでした。
商品の並び順は、ショップごとに先頭を「1」として降順に番号がふられています。例えば、新規登録した商品を一番先頭に持っていきたい場合、他の商品の並び順も全て更新してあげる必要があります。
多くの商品をかかえるショップが管理画面などで商品を追加すると、大量の商品の並び順を更新する操作が行われるため、1つのテーブルに書き込みが集中してしまい、負荷が問題になっていました。
リファクタリングの方針
データベース・リファクタリング本で言うところの「テーブルの分割」を行いました。 並び順のデータを別テーブルで管理することで、書き込みを分散することができます。代わりにテーブルを JOIN するようにソースコードの修正が必要になります。
リファクタリングの流れ
以下の順で実施しました。
- 前準備
- 商品の並び順を管理する新しいテーブルを追加
- 商品テーブルと並び順のテーブルの値を同期
- 既存の並び順を新しいテーブルにインポート
- 修正したソースコードのリリース(複数回)
- 並び順を参照するコードを修正
- 並び順を更新するコードを修正
- 後処理
- 商品テーブルと並び順テーブルの同期を停止
移行期間中はトリガーで同期
ソースコードの修正は一人で作業するには広範囲で、全てを把握できているわけではありませんでした。 このまま一気に差し替えはリスクがあると判断し、サービス稼働中に無停止で段階的にリリースするために移行期間を設けました。
移行期間中は、商品テーブルと並び順のテーブルの値を同期する必要があり、 今回は、データベースのトリガーを利用することにしました。
トリガーとは、テーブルに対して INSERT
/UPDATE
/DELETE
などを契機に処理を実行できるデータベースの機能です。環境や設定によっては利用できないこともあるのですが、幸い BASE のデータベースである Aurora は MySQL 互換ですので、利用することが可能でした。
以下にトリガーの例を示します。 商品テーブル (items) が INSERT された際に、並び順テーブル (item_list_orders) に内容を反映させています。
DELIMITER | CREATE TRIGGER item_list_orders_insert AFTER INSERT ON items FOR EACH ROW BEGIN IF NEW.list_order IS NOT NULL THEN INSERT INTO item_list_orders (item_id, list_order) VALUES (NEW.id, NEW.list_order); END IF; END; | DELIMITER ;
実際にやってみて
データベースのエキスパートの方にも協力いただく必要はあったのですが、コードの修正作業は一人でできました。
移行期間を設けたことで、1つのリリースを影響範囲を極限まで絞り込んだことで動作確認しやすくなりました。 修正を一気にリリースしたい誘惑に抗いつつ、地道に作業をすすめていきました。
また、リファクタリングに欠かせない自動テストで動作に問題がないことを常に確認できました。
おかげさまで、大きなトラブルもなくリリースできました 🎉 (懸案だった更新のスループットも大幅に改善されたとのこと)
おわりに
データベースのリファクタリングは、正しい手順に沿って行えば、時間はかかりますが技術的には難しいことはありませんでした。 ミスした際の影響範囲の大きさから敬遠されがちですが、積極的にやる価値はあるのではないかと思いました。