第4章 エンコーディングと進化
要約
大規模システムのデプロイにおいて進化性を高める手法として、スムーズな動作を確認しながら全体をアップグレードするローリングアップグレードが挙げられます。ダウンタイムがなく、リリースが容易になり進化性が向上します。
データ変更に伴う互換性管理には前方互換性と後方互換性が存在し、Apache Thrift、Protocol Buffers、Apache Avroなどもあり、それぞれフィールドのタグが存在したり、初期値を設定する必要があります。
前の章
前の章である第3章 ストレージと抽出はこちらです。
大規模システムのデプロイのための進化性
ローリングアップグレード
アーキテクチャやアプリケーションにおいても、新しいバージョンのデプロイを少しずつ行い、新しいバージョンがスムーズに動作しているかをチェックしながら、全てに対してアップグレードを行っていくやり方(サーバーサイドアプリケーションのみ)です。
このメリットは、ダウンタイムなしに新しいバージョンをデプロイできるのでリリースがしやすくなり進化性が高まります。
アップグレードによるデータの変更
ほとんどの場合、アプリケーションの機能に変更を加えるなら、アプリケーションが保存するデータにも同様に変更を加える必要があります。
例えば、新しいフィールドやレコードの種類を追加したり、既存のデータを新たな方法で提示したりする必要が出てきます。
データの変更のための互換性
システムでは新旧のバージョンのコードと新旧のデータフォーマットが共存するかもしれません。システムが動作し続けるためには互換性を管理する必要があります。
例えば、ユーザがアプリをアップデートせずに、旧バージョンのアプリと新バージョンのアプリを使うユーザが混在します。
互換性の種類
互換性には2種類があり、前方互換性と後方互換性があります。
前方互換性は新しいコードによって書かれたデータを古いコードが読めること。
後方互換性は古いコードによって書かれたデータを新しいコードが読めること。
(私の個人的な意見としては、データ分析基盤において可能であるならば、前方互換性は実装しない方が良いと思っています。
なぜなら、本来はフィールドに入っている値が失われたり、履歴管理を行っていないテーブルはnullでアップデートされてしまう可能性があるからです。)
様々なバイナリフォーマット
JSON, XML, CSV
これらのフォーマットは非常に幅広い分野で利用されています。
しかし、エンコーディングに関して曖昧さがあるという問題もあります。
XMLとCSVでは数値と数字だけから構成される文字列を区別することができません。
また、JSONは文字列と数値を区別できますが、整数と浮動少数点は区別できません。
JSON, XML, CSVの問題
CSVにはスキーマがないので、それぞれの列や行の意味の区別をCSVそのものではできません。よって、アプリケーションの変更によって新しい行や列が追加されるのであれば、アプリケーションで管理をする必要があります。
JSON, XMLを扱うのであれば、それぞれMessagePackやWBXML等を使用することになるでしょう。
Apache Thrift、Protocol Buffers
Apache ThriftはFacebook、Protocol BuffersはGoogleで開発されたライブラリになります。
特徴は下記の2つになります。
エンコードするにはスキーマを必要
それぞれのフィールドにタグ番号をつける
下記がスキーマ定義の例になります。
特徴の1つであるフィールドのタグ番号はスキーマ定義の1,2,3に当たります。
message Person {
required string user_name = 1;
optional int64 favorite_number = 2;
repeated string interests = 3;
}
Apache Thrift、Protocol Buffersの前方・後方互換性
まずは前方互換性です。
古いコード(Subscriber)が新しいコードによって書かれた(Publishされた)データを読み取ろうとした場合、古いコードは認識できないタグ番号を持つ新しいフィールドを単純に無視していきます。つまり既知のフィールドのみデータを扱います。(これは実装方法にもよると私は思います。)
次に後方互換性です。
それぞれのフィールドがユニークなタグ番号を持っていれば、常に古いデータを読むことができます。
ただし、注意点として、追加するすべてのフィールドを必須としないか、もしくは初期値を指定するかをしなければいけません。
最後にデータ型の変更についてです。
データ型を変更する際は、値の精度が低くなったり切り捨てられてしまったりするリスクがあります。
例えば、32ビットの整数を64ビットの整数に変更する時、64ビットの値が32ビットに収まらなかった場合に、その値は切られてしまいます。
Apache Avro
Apache Avroもまた、 バイナリのエンコーディングフォーマットであります。Apache ThriftのフォーマットがHadoopのユースケースにうまく適合しなかったことを受けて、プロジェクトが立ち上がりました。
Apache Avroの特徴としては下記の2点があります。
タグ番号がない
ライターとリーダのスキーマは同じである必要は無く、互換性だけあれば良い
Apache Avroの前方・後方互換性
前方・後方互換性については、Apache Thrift、Protocol Buffersと同様です。
前方互換性については、古いコード(Subscriber)が新しいコードによって書かれた(Publishされた)データを読み取ろうとした場合、古いコードは認識できないタグ番号を持つ新しいフィールドを単純に無視していきます。
後方互換性も追加したそれぞれのフィールドが初期値を指定すると実現できます。
それぞれのデータフォーマットを資料にまとめたものが下記になります。スキーマを比較することで特徴を非常に分かりやすくまとめています。
まとめ
以上、エンコーディングと進化について解説しました。
データ基盤という巨大なシステムが常に起動するためには、アプリケーションだけではなく、データの進化性を意識しなくてはいけません。
特に連携方法が煩雑だったり、リアルタイム連携が複数あると様々なスキーマバージョンでデータが混在することになります。
また、マイクロサービスのAPIやデータ基盤のスキーマ(やカラム)変更では、どのバージョンでも稼働し続ける設計ができると良いと思います。
ご興味あれば、下記のような本も参考にしてください。
エンコーディングと進化について特に重要な点を解説しました。ただし、非常に量が多いため解説していない部分が多々あります。詳細は本書を手にとってみて下さい。