こんにちは。 エキサイト株式会社の三浦です。
長い間サービスを運用していると、やがてテーブルの構造を変えたいと思うときが来ることがあります。
「Indexを追加する」程度であればそこまで問題ではありませんが、「特定のカラムの照合順序を変更する」などをする場合、そのテーブルへの書き込みがブロックされてしまいます。 一時的にサービスを停止できるのであればそれが最も楽ですが、多くの場合、可能な限りサービスは停止せずに変更したいのではないでしょうか。
今回は、実際に私が携わっているサービスで行った、サービス無停止でのテーブル変更の方法を紹介します。
テーブル変更の経緯
私が携わっているサービスはかなり息の長いサービスであり、その分テーブルも長い間使われて続けています。
その結果、過去に使っていたが現在では使っていないカラムがあったり、不適切な照合順序がついているカラムがあったり、本来必要な制約がついていなかったりと、様々な問題が山積していました。
そこで今回、そういった問題を解消するためにテーブル構造を変更することになったのですが、ここでいくつか問題が浮上しました。
カラムの照合順序の変更中は、テーブルへの書き込みができない
カラムの照合順序を変更すると、その変更中はテーブルへの書き込みがブロックされ、待機状態になります。 変更がほぼ一瞬で済むレベルであれば良いかもしれませんが、今回のテーブルはレコード数が多く、変更に数分掛かってしまうため、その間書き込み不可状態にしておくのは現実的ではありませんでした。
カラム削除後にエラーが起きる可能性がある
変更したいテーブルには「過去に使っていたが現在では使っていないカラム」があり、それを削除することも今回の目的でした。 ですが、厳密には使っていないわけではなく、「使うべきではなく、別テーブルとのJOINで取得するべきカラムであるため削除しても問題ないが、一部では使ってしまっているカラム」であり、そのためそれを使用しないようにするためのアプリケーションコード側の修正が漏れてしまった場合、カラム削除時にエラーが起きてしまう可能性がありました。
一時的にサービスを停止してテーブルを変更し、そのテーブルを使用している箇所が問題ないかテストを行い、問題なければサービス再開、ができるのであればよかったのかもしれませんが、サービスを停止する場合は当然以下のようなことを考えなければなりません。
- ユーザ側にサービスを停止する旨を事前に通達する必要がある
- サービス停止中はユーザがアクセスできないため、ユーザの利便性としても、サービス側の収益としても問題となる
- 万が一変更中に何かが起きた場合、停止時間が長引き、上記の問題が拡大する
そのため、可能な限りサービス無停止でテーブルの変更を行う必要がありました。
テーブル変更の方法
色々と考えた結果、 blue / green の手法を取ることとしました。
すなわち、
- 該当テーブルに対する書き込み処理のみを停止する
- 今回のサービスは、ユーザが行うのは読み込みのみで、書き込みは管理側のみで行うため、書き込み停止によるユーザ側への影響はほとんど存在しない
- 該当テーブルのコピーテーブルを作成する
- コピーテーブルに対して、構造の変更等を実行する
- コピー元テーブルとコピー先テーブルの名前を入れ替える
- 問題なければ書き込み処理を再開する
- 全体的に問題なければ、コピー元テーブルを削除する
という方法です。
これであれば、先に挙げた「カラムの照合順序の変更中は、テーブルへの書き込みができない」の問題は気にする必要がありませんし、万が一テーブル名を入れ替えた後に問題が発生しても名前を戻せばいいだけなので、「カラム削除後にエラーが起きる可能性がある」の問題もリスクを最小限に抑えることができます。
ちなみに、今回は書き込みを管理側のみで行うタイプのサービスであり、かつ書き込み停止がそこまで難しくなかったのでこの方法にしていますが、例えばユーザ側でも書き込みがあったり、書き込み停止がかなり難しい場合は、一時的にコピー元・先の両方のテーブルにダブルライトする方法をとっても良いでしょう。
最後に
テーブル変更は、テーブルへのアクセスに制限が掛かったり、不可逆性があったりして、アプリケーションコードの変更ほど簡単には行うことができません。
可能な限り、最初に作る段階でちゃんとしたものを作るべきですが、サービスやエンジニアリングの変化で、どうしても変えなければいけない日が来ることもあります。
今回の blue / green の手法は、常に使えるわけではないとは思いますが、テーブル変更の際の問題点をある程度排除してくれる物となっていますので、もしテーブル変更が必要な際はぜひ考えてみてもらえると幸いです。