賢いデータ移行のやり方

どうも、最近ビットコインに給料ブチ込みまくって金欠になってる人です

今日はシステム更改に伴うデータ移行のやり方についていくつか紹介していこうと思います

#データ移行とは
システム更改を行うときは上物のソフトウェアごと取り換えるパターンが多いです
システム更改を行う理由は様々ですが、ほぼ必ず付いてくるのがデータ移行です
ですが、新システム開発に気を取られてデータ移行がおろそかになることが結構あるようです
移行方法を考えておかないと新システムにも悪影響を及ぼすことがあります

では早速、どんな移行パターンがあるのか見てみましょう

#Export/Importパターン
ほとんどのRDBMSはデータのexport、importをサポートしています(あまりにも古いバージョンだとサポートしていないこともたまにあります)
RDBMSによってコマンドや、吐き出すデータの形は違いますが、できることはほぼ同じです
RDBMSのバージョンが大きく変わったり、他のRDBMSに移行しようとしたときに良く使われるパターンです

移行元と移行先のテーブル定義が同じ場合にはこのパターンを用いるだけで問題ないです
ただし、移行元に不正なデータが入っている場合はimport時にエラーが出る可能性があります
長く使っているシステムだとint型にnullが入っていることがあったりしますｗ

ですが新システムを作ろうとした時に旧システムと同じテーブル定義になるのはあり得ないと言って良いでしょう
何故なら、旧システムのDBの再設計を行うことが新システムの役割でもあるからです

###DBの再設計について
もう少し掘り下げてみましょう
まず、DBには既存のデータが保存されています
ここには社員IDや店舗コードと言ったコード体系が存在しています
このコード体系が複雑化していたり、命名規則が似たり寄ったりになっていてどれがどれだかわからない、ということが多々あります
新システムではこれらの問題を解決することが求められます

逆に、コード体系やテーブル定義の再設計を行わなくて良いのであればデータ移行は不要どころか、システムの載せ替え自体考え直したほうが良いかもしれません
この場合は、旧システムのAPIを呼び出す形で新システムを構築してしまえば良い訳です
旧システムがCORBAを使っていたとしたら、CORBAをRestful APIへと変換させる層を作り、その変換層経由でアクセスを行ってしまえばOKということです
このやり方であれば、後々コード体系を再設計したいと思った時に変換層を通じてデータの移行を行うことができるようになります
変換層の更に変換層を作って、自分の欲しいデータを抽出できるようにすることも可能なため、実は結構現実的な手法です

ちょっと話がズレましたが、新システムではコード体系やテーブル定義を再設計することがほとんどのため、Export/Importパターンのみを使ってデータ移行することは難しいでしょう
importの後にalter文をいくつも発行する…ということもできますが、これはあまりお勧めできません

#Export/Import/Alterパターン
同じテーブル定義で移行した後、alter文をいくつも発行することでデータ移行をしよう…というパターンです
移行時のリスクヘッジが出来ているような感じはしますが、実はかなりリスクが高い方法です

まず、alter文をひとつ間違えたらシステムがぶっ壊れます
更に、alter文をちょっと発行するだけで欲しいテーブル定義になれば良いですが、コード体系の再設計を行うような場合は到底そんな簡単にはいきません
現実的に考えると、複数段階に分けて徐々に欲しいテーブル定義に変更しなければなりません
![データ移行整形.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529608/24e00dcc-3a39-f5dc-fcbd-a69eba2749dd.png)

このようなリスクの高い事をするには勿論テストコードが必要になりますが、旧システムと新システムでテーブル定義があまりにも違う場合はテストコードを書いたとしてもミスがめちゃくちゃ発生しやすくなります
このような複雑なやり方でデータ移行を行うのはリスクが異常に高くなるためおすすめしません

旧システムからデータをexportし、ちょっと編集してそれっぽい形でimportする…という方法は言語道断、今すぐ止めるべきです
そのやり方はExport/Import/Alterパターンよりも更にリスクが高いです

#DB-DBパターン
ここまで紹介したExport/Importパターン、Export/Import/AlterパターンはいずれもDBからデータを直接抽出し、DBに直接データを入れ込む、ということを行ってきました
このようなDBを直触りする移行方法をここではDB-DBパターンと呼びます

DB-DBパターンは、これまで紹介した通り、バージョンアップやRDBMSの変更時ではシンプルで良いやり方ですが、コード体系やテーブル定義の再設計を含むシステム更改には向いていません

ではどのようなやり方が良いのでしょうか

#API-APIパターン
DBを直触りせずに、旧システムのAPIと新システムのAPIを経由してデータ移行をするパターンです
先ほど少し紹介した変換層を用いる方法もこのパターンになります
ここからはAPI-APIパターンにはどのようなものがあるかを見ていきましょう

#Conversion Layerパターン
変換層を用いるパターンです
先ほど紹介した変換層を用いる方法ではコード体系、テーブル定義の再設計を行わないことを前提としていましたが、再設計を行う場合でも使える非常に強力なパターンです
![Convaersion Layer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529608/9f62b0d6-7642-f235-b13d-eb361112d195.png)
(矢印はデータの流れをイメージしたものです)

旧Applicationと新Applicationの間に専用の変換層を挿入します
これはデータ移行以外でも見られる方法ですが、データ移行する時であれば汎用性は無視して、移行専用の変換層を作ってしまいましょう
一括送受信のようなことが出来るようにしておけば移行時間の短縮に繋がります

API-APIパターンではDBを直触りしないため、旧システムと新システムのテーブル定義の誤差を気にする必要がなくなります
Applicationを仲介することによって、データの移行だけではなく、意味の再定義も行うことが出来るようになります

###意味の再定義について
もう少し掘り下げてみましょう
DB-DBパターンだと、旧システムのデータを旧システムの意味ごと移行してしまいます

例えば、旧システムの値引きカラムに「30」が入っていたとしましょう
そのままデータを新システムに移行したのですが、問題が発生しました
旧システムでは「30円」という意味でしたが、新システムでは「30%」という意味で扱ってしまいました、、、

こんなの気付くだろ！と思うかもしれません
確かに、このような単純な場合はカラム名で判別できるかもしれません(値引率、値引額など)
しかし、もっと複雑で名前が似ていたらどうでしょうか
このように意味を間違えて移行してしまうことがあります

他にも、複数のコードの組み合わせが別のコードの素になっているような場合では、新システムでも同じルールに従うか、全てを入れ替える必要が出てきてしまいます
コード体系を新しくするだけであればExport/Import/Alterパターンでも良いと思ってしまうかもしれませんが、API-APIパターンのほうがテストコードが書きやすいです
更にAPI-APIパターンでは複数の入力チェックが行われるため、間違いに気付きやすい、という利点もあります

一番大きい利点は、**意味の変換**ができることです
旧システムの意味付けに左右されずに、新システムで意味の再定義を行うことができるようになります
当たり前と思うかもしれませんが、DB-DBパターンではこの意味の変換ができていません
そのため、コード体系やテーブル定義の再設計を行う場合はAPI-APIパターンを使ったほうが良いと言えます

#Plain Textパターン
旧システムでファイルの書き出しを行い、新システムがそのファイルを読み込む、というパターンです
Conversion LayerパターンではWebAPIを使うようなイメージでしたが、このパターンではプレーンテキストがInterface代わりとなるため、通信のやり取り等について考える必要が無くなります
![plaintextパターン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529608/882d3ce8-8ded-5248-37bb-b9460d290af5.png)

CSVやTSV、JSONのような単純な形にするほうが書き出し、読み込みのどちらも楽になるので良いと思います

ここでの注意点は、**テーブル定義をそのまま書き出している訳では無い**、ということです
旧システムのコード体系やテーブル定義を気にしない別の形でプレーンテキストを扱わなければいけません
この場合でも意味の変換は必要となるため、書き出し部分、または読み込み部分に変換モジュールを差し込む必要があります

Plain Textパターンの利点は、常にオンラインである必要が無いことにあります
旧システムは好きなタイミングで書き出しを行うことができ、新システムは好きなタイミングで読み込みを行うことができます

###旧システムがGraphQLのサービスを提供している場合
この場合、データ移行が楽にできることもありますが、逆に大変になってしまうこともあります
旧システムが公開しているリソースが新システムと互換性が無いような場合には、改めて変換層を差し込む必要が出てきます
ただし、Conversion Layerパターンと違って旧システム側をいじる必要はありません
この部分がGraphQLの利点ですね

#その他のやり方
上記で説明していたパターンを組み合わせてデータ移行することも可能です
基本的にはAPI-APIパターンを用いて、データ量の多いテーブルだけDB-DBパターンを用いる、というやり方も選択肢に上がってくると思います

しかし、このようなやり方はおすすめできません
何故なら、双方の利点を潰しあってしまう可能性があるからです

データの量によってやり方を変えるのではなく、その用途によってやり方を変えるべきです

RDBMSのバージョンアップや他のRDBMSに移行しようとしている時はDB-DBパターンを使い、コード体系やテーブル定義の再設計を行うような場合はAPI-APIパターンを使ってください

システム更改時のデータ移行はかなりリスクが高いことを忘れないでください
複雑な部分を変換層や変換モジュールに集めるようにしましょう
複数のパターンを組み合わせてしまうと、その複雑な部分が色々な場所に散らかってしまいます

#まとめ
どうだったでしょうか

データ移行は失敗することが許されず、失敗しても気付きにくく、リスクも非常に高いものです
その割に、データ移行のやり方などの情報が不足しているように感じます

安全にデータ移行を行うために、この記事が少しでも参考になればと思います

もし他のパターンを知っている方が居たら教えてください