エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

DBテーブルのカラムを削除するときにやること・考えること

突然ですが皆様は、稼働済みサービスのDBからテーブルカラムを削除されたりすること、ありますでしょうか? 基本的に削除はまずやらないのではと思います。えっやらないの? と思われた方もいらっしゃるかもしれませんが、きっとこの記事を読めばなぜ多くの方がカラム削除を避けるのかわかることでしょう。

とはいえ、そうして使わないカラムがテーブルに溜まっていくとやがて新規加入メンバーがコードにキャッチアップする妨げとなるレベルにまで溜まってきたりします。いつかは大掃除のときがくるわけです。DBは寿命長いですからね。そうしたときに実際どのような手順でカラムを削除するのか見ていきましょう。エムスリーエンジニアリンググループUnit1(製薬プロモーション)・Unit9(治験臨床研究支援)チームエンジニアの三浦 [記事一覧 ]が、最近実際にやった作業から知見をお届けします。

長いので3行で

  1. そのカラムへのアプリケーション外からのアクセスをなくす
  2. カラムをNULL許容にする
  3. アプリケーション側でそのカラムへのアクセスをなくし、リリース
  4. カラムをDROP

すみません4行になってしまいました。でもこれ以上は減らせません。

考え方

最も大事な点は稼働中のサービスがリリース前後で無用にエラーを起こさないようにということになるのですが、それに加えて、開発・QA環境のことですね。別にフィーチャーブランチを切って開発やQAの作業をしている人々が詰まないことも重要な観点となります。そして忘れてはいけない、データ分析をしている人々への目配りが三つ目ですね。

さてまず、カラム削除をアプリケーションとDBどちらから先にやるかという議論ができます。両者を厳密に「同時」にリリースはできませんからね。どちらを先にしましょう、もし先にDBからカラムを削除すると、アプリケーションがエラーを出し始めるのは容易に想像がつきます。では、アプリケーションが先か。

先にアプリケーション側からそのカラムへの参照をなくしてリリースすればあとは落ち着いてDBからカラムを削除できる、はい、その発想でほぼ間違いありません。ただ、それだけの順番で行くとアプリケーション側リリースと同時に大量のアラートが飛び始める可能性があります。何がいけなかったでしょう。

 ︙

INSERT文が落ちるようになったのです。アプリケーション側でカラムをまったく参照しなくなれば、INSERTするのに必要なカラムデータも足りず軒並みエラーというわけ。つまりアプリケーション側からカラム参照を削除するのに先立って、「それなしでもINSERTが通る」ようにしておかなくてはならなかったのです。

この、INSERT文を通るように→アプリケーション側から削除→DBから削除の3段階をサービス本体での修正とすると、これにアプリケーション外からのアクセスへの考慮を入れて前述の4段階となります。

1. そのカラムへのアプリケーション外からのアクセスをなくす

事業として提供しているサービスならば、多かれ少なかれその利用実績・実態をデータ分析したり集計したりするニーズがあるはずです。すなわち、アプリケーションDBを分析用DBに取り込んで加工しているチームがあるはずです。アプリケーション本体の開発に集中しているとついそうしたチームへの目配りを忘れてしまい、新機能リリースと同時にデータ分析チーム方面から悲鳴が上がってしまったなんて経験をお持ちの方も少なくないのでは。

ですから定石としていつもステップを踏むようにしましょう、DBスキーマ変更の最初の一手はデータ分析チーム相談。特に何かを削除するような確実にエラーを起こす変更ならば絶対にはずせない事項となります。

データ分析チームもその下流での影響がすぐに読み切れるとは限らず、データマートからも無邪気にそのカラムを消してしまえるとは限りません。まずは保守的に、次のように取り込みSQLで無意味データを取り込むように変更などすることになるでしょう。

 SELECT
   id,
   tenant_id,
   name,
-  billing_start_date,
+  DATE'1970-01-01' AS billing_start_date,
-  billing_end_date
+  DATE'2200-12-31' AS billing_end_date
 FROM
    users

NULL非許容だったbilling_start_date, billing_end_dateを削除するというストーリーですが、下流影響を防ぐためNULLではなく固定値を読み込むようにする変更をしている例です*1。

2. カラムをNULL許容にする

そのカラムを指定しなくてもINSERT文が通るようにするということです。ということはNULL許容にするのでなくDEFAULTを設定するというのも別解になります。ただこれから消すカラムに値を持たせる必要もそうありませんから、NULL許容にしておくので十分でしょう。本来NULL非許容でデータマートにも必ず値が取り込まれてきたのだとしても、1.の段階でちゃんとダミー値を取ってくれるようになりましたから心置きなくNULL許容にできますね。

カラムがもともとNULL許容だったらこのステップはパスです。おめでとうございます。

3. アプリケーション側でそのカラムへのアクセスをなくし、リリース

アプリケーションから先にカラム参照をなくしましょうというのは総論でお話しましたが、ここであえてさらに「いったんリリースしてしまいましょう」というのも付け加えます。つまり同日にDBからのカラム削除までやってしまわずに別の機会にしましょうということと。その理由が、他のメンバーの開発・QAを止めないようにという観点です。

アプリケーションからもDBからもカラムを削除するという修正、これをリリースする前にQAするには、QA環境でDBからカラム削除することになりますよね。すると、他のフィーチャーブランチがQA環境でエラーを出してしまうようになります。これを防ごうとするとどのような手順を踏まないといけないかというと

  1. アプリケーションからカラム参照を削除する修正を書く→QA→リリース
    (この間、DBスキーマに変更は加わらないので他のフィーチャーブランチも問題なく動く)
  2. 各フィーチャーブランチで本流の最新をマージなりリベースなりしてもらう
    (各ブランチとも、DBからカラムがいつ消えても動く状態になった!)
  3. DBからカラムを削除するマイグレーションをQA→リリース
    (QAする段階でまずQA用DBからカラムが消えるが、各フィーチャーブランチは問題ない)

二人以上のエンジニアがいるなら、アプリケーションからとDBからカラムを削除するのは別のリリース機会に分けたほうがいいとなります。最初に書いた4手順のうちこの3番目で、文末の「リリース」というのにはチームの皆に本流マージをしてもらうというステップも含まれていたことになりますね。

4. カラムをDROP

もはやあなたを阻むものはありません。何年もかけて溜まったいらないカラム、すっきり削除しましょう。

ALTER TABLE users DROP COLUMN billing_start_date;
ALTER TABLE users DROP COLUMN billing_end_date;

おつかれさまでした。

他のケース:不要なテーブルの削除

テーブル削除の場合には、INSERT文が通らなくなる問題がありません。よって少し手順は簡単、今度は本当に3行で書けます。

  1. そのカラムへのアプリケーション外からのアクセスをなくす
  2. アプリケーション側でそのテーブルへのアクセスをなくし、リリース
  3. テーブルをDROP

他のケース:カラムの改名

名前が実態を表さなくなってしまったので改名したいというニーズがたまに生じるのはその通りです。しかし⋯⋯改名は難しいです! もしどうしてもやりたいとしても、新しいカラムを作って使うようにして古いカラムは使うのをやめる(そしていつか大掃除のときに⋯⋯)とするしかないでしょう。もちろんそれだって簡単ではなく、

  1. 新しいカラムをNULL許容で作る
  2. アプリケーションの更新系で両方のカラムに書き込むようにし、リリース(上の例と同じく、リリースとは各フィーチャーブランチにも取り込むことまでを意味します)
  3. 古いカラムから新しいカラムへデータをコピーするDMLをDBで流す
  4. この時点で、NULL非許容にする必要性があればそうできる
  5. アプリケーション外からのアクセスを新しいカラムへ変更
  6. アプリケーションの参照系を新しいカラムへ変更し、リリース
  7. アプリケーションの更新系で古いカラムへの書き込みはやめる(NULL非許容なら無意味データを書き込むようにする)

かような一大作業となります。アプリケーションプログラム内で変数名やフィールド名を変えるリファクタリングとは桁違いの大変さです。

まとめ

おまけも含めてこのようなやることリストをご覧に入れたのですが、スキーマ変更には限りなくパターンがありますから一記事では本来とても網羅できるものではありません。そこでそのときどきでこうした実施プランを立てられるよう、ここまででプランを立てるのに考えていたことは何だったのかを振り返ってみましょう。

急に人間臭い話になってしまいますが、他者への目配りが出発点となっていたと思います。データ分析チームを、他に開発・QAしている人を、また参照系は更新系を、更新系は参照系をといったように他の人達、他のユースケースたちが詰まないかを各ステップで考えることで悲鳴の上がらないスキーマ変更を構成しようとしてきたのです。

もちろん、人間の想像力には限りがありますから、想像できなかったところまでは目が配れません。そうして悲鳴が上がることになってしまったら振り返りをして、そうすればもっと広く目が配れるようになります。次のスキーマ変更はきっともっとうまくいきます。

We are hiring

影響範囲に目を配って、それでも悲鳴を上げさせてしまったら振り返ってもっと目を広く配れるようにする⋯⋯このサイクルはスキーマ変更作業に限らないこと、すべての開発分野でエンジニアを成長させてくれることです。私達エムスリーのエンジニアはすべきことを自分とチームで考えて開発していますので、それだけ影響範囲を考えたりも、ときには影響を読み落としてしまったりも日々しています。そういう成長サイクルの回っている環境、ご興味あったらこちらのページからどうぞ。応募を前提にしないカジュアル面談もやっています。

jobs.m3.com

*1:遠い未来という意図で2200年というわりあい近い年月を採用しているのが気になる方もいらっしゃるかもしれません。しかしデータ分析分野だとよく使われるPythonライブラリのpandasが日付として最大2262-04-11までしか扱えないという問題があり、それに引っかからない切りの良いい年号というとこのあたりになるなどといった事情があります。175年後ですが、175年前はまだ江戸時代ですからこれでも相当遠い未来ですよね