ちょっと硬派なコンピュータフリークのBlogです。

カスタム検索

2013-11-17

データベースアプリケーション開発を炎上させる負のスパイラル

毎度おなじみ、はてブのホットエントリに「SIをダメにする負のスパイラル」というタイトルのまとめが掲載された。きしだ氏とはかなり視点は違うものの、開発現場の問題点については少し思うところがあるので意見を書いてみようと思う。と言っても、以下の話の内容はデータベースアプリケーションに限定した話であり、またSIerだけに限った話ではないのでその点はご容赦頂きたい。もちろんSIer各位の案件はデータベースは必須なので、本エントリで触れる問題点には該当するだろう。

Q.なぜ炎上するのか? A.正しいデータベース設計ができていないから

結論から言おう。データベースアプリケーションの開発が炎上するのは正しいデータベース設計ができていないからだ。ここでいう「正しい」とは、論理的に証明できる正しさという意味ではない。「本来こうするべき」といった意味で捉えて欲しい。

「炎上」というのは、例えばテストが通らない、バグが直らない、仕様がコロコロ変わるといった理由で工期が伸びて納期までに終わらないというような状況だとしよう。全てが全てデータベース設計に原因を帰着させるのは無理があるが、技術的な面だけにフォーカスすれば、開発が炎上する最大の要因はデータベース設計に問題があるからだ!というのが私の意見である。

データベース設計理論

幸いにして、リレーショナルデータベース(あるいはリレーショナルモデル)では、どのようにデータベース設計を行うべきかという点については王道の理論が存在する。そう、「正規化」と「直交性」だ。これら2つの理論を実践し、データベースを「正しく」設計することが、データベースアプリケーションを滞りなく開発する上で重要なポイントとなる。

最近はO/Rマッパーが使われることが多くなったが、SQLを直接書かないからといってデータベース設計がおろそかであって良いということはない。行単位でデータを扱う構造である以上、データベース設計の質によってアプリケーションの開発効率は左右されてしまうことになる。これはリレーショナルデータベースを使わなくても、つまりNoSQLと呼ばれるカテゴリのデータベースを使った場合も同様である。

SI案件のアーキテクトなどが「このプロジェクトではテーブルは正規化しない」などと言ったら間違いなくその案件は地雷であると思って間違いないだろう。炎上する前に荷物をまとめて逃げることをおすすめする。

アンチパターンにはまると工数が指数関数的に増える

データベース設計を間違ってしまうと、開発の手間が飛躍的に増える。書籍、「SQLアンチパターン」には様々なアンチパターンが収録されているが、その多くはデータベース設計が正しくないことに起因する問題である。

なぜデータベース設計に問題があるとロジックをうまく書けないのか。それはデータベース設計(スキーマ)がロジックと密接に結びついているからである。ある操作をするには、その操作に適したデータ型が必要となる。例えば、数値演算をするときに変数のデータ型として文字列を選択するような人はいるだろうか?いや、いるはずはない。何故ならそのような選択は馬鹿げているからだ。整数の四則演算をしたければ、整数型を用いるべきだろう。文字列を使って数値演算を表現するのはうまくいかない(あるいは非常に手間がかかる)のと同じように、データベース設計が適切でなければ、まともなクエリは書けないのである。

つまるところ、道具は正しく使うべきだということだ。料理をするとき、スプーンやフォークで魚を捌こうとしても上手く行かない。魚を捌くにはやはり包丁が適している。反対に、包丁で調理済みの魚を食べようとしても上手く食べられない。(怪我をしてしまうだろう。)道具は必要に応じて使い分ける必要があるのと同じく、データベースはロジックに応じて設計する必要があるのである。

どこまで正規化すべきか

IT業界では、なぜか正規化は極めて蔑ろにされているように思う。「正規化なんか必要ない」「非正規化したほうが性能が高い」と言った間違った言説が跋扈してはばからず、多くの人がそのような間違った考えを信じてしまっているようである。これはデータベース業界だけでなく、IT業界全体にとって大きなマイナスである。

なぜ人は正規化を蔑ろにするのか。そのことについて近年私の中でひとつの大きな発見があった。「正しいデータベース設計をするぞ!」と意欲的に取り組んだとしても、多くの人が挫折を経験するようになる罠が、リレーショナルデータベースには存在するのである。それは、「全てのテーブルを正規化できるわけではない」という点である。言い換えると、テーブルには正規化できるものと出来ないものがあるということだ。この点を見逃してしまうと、本来は正規化できない、あるいは正規化するに値しないようなテーブルを正規化しようとしてしまうことになるが、それは当然うまくいかない。すると「正規化理論なんて実践では適用できない」「正規化など意味がない」と言った考えにとりつかれてしまうことになる。

ところで、正規化できるテーブルとそうでないものの違いは何であろうか。実は、正規化できるテーブルというのは、そこに含まれるデータが「事実の集合」として表現できるものである。この「集合」というのがポイントなのだが、集合の要素には重複がなく、要素同士に順序がなく、NULLが含まれていない。そのように「集合」としてデータを表現できるものだけが正規化の対象になる。

ところが、世の中には集合で表現できないデータ構造であふれている。例えばグラフや履歴(時系列)データというものは、集合というデータ構造では表現出来ない。これを無理に集合に当てはめ、正規化しようとしても上手く行くはずがないので、時間の無駄である。これらのデータ構造については、ここで解説できるほど短くまとめられないので、詳しくはWeb+DB Pressの連載を読んで欲しいと思う。Vol.75で履歴データを、Vol.76でグラフをそれぞれ扱っている。他にも全文検索や正規表現、図形データと言ったものなども集合とは無縁である。

従って、データベース設計においては、正規化できる部分だけをきっちり正規化すれば良いのである。「全てのテーブルは正規化すべき」というような原理的な考えでは上手く行かないし、「正規化など一切しなくても良い」という思考停止でも上手くは行かない。どのテーブルが正規化できるかという見極めが重要なのであるが、これができる人というのはほとんど居ないように思う。その結果、データベースアプリケーション開発は炎上し、正規化がますます蔑ろにされ、負のスパイラルに陥っているように見える。

NULL

正規化の対象となるテーブルでは、NULLはご法度である。NULLを出現させない最良の方法は、テーブルを分けるということだ。よく、「最初は値が分からないが、あとから入力される」という理由で、あるカラムをNULLにするような設計をするのを見かけるが、それは間違いである。値が分かっていないなら、その時点でそのデータは必要ないのである。必要になった時点で、専用のテーブルにデータを記録すれば良い。まだデータが必要ない段階で、未来のデータ用の領域を取っておこうとするのは、リレーショナルデータベースでは誤りなのだ。

どうしてもあるカラムをNULLにしておきたい(例えばSNSのプロフィール情報のようにオマケ的なデータ)ならば、NULLになる可能性のあるカラムは検索条件(WHERE句やJOINの条件式など)には含めないことだ。そのようなケースだけであれば、開発効率への影響はほとんどないだろう。

なお、リレーショナルモデルに適合できない(正規化できない種類の)テーブルでは、NULLがあっても構わない。すべてのテーブルを正規化できないのと同じで、すべてのテーブルからNULLを排除できるわけではない。SQLはリレーショナルモデルをベースに設計された言語ではあるが、リレーショナルモデルに即したデータだけでなく、それ以外のデータも両方扱えるようになっている。両方を扱えるからSQLは万能であり、長く使われ続けてきたのであるが、皮肉にもそれが大きな落とし穴を作っている要因になってしまっているのである。

リレーショナルモデルの範疇でない問題をSQLで扱うならば、NULLを排除するべき理由は全くない。にも関わらずNULLを排除しようと躍起になると、正規化のときと同じように「やっぱりNULLを排除するなんて現実的じゃないよね」という諦めの境地に達してしまうことだろう。

開発者がDBを設計する

これは多くのSIerが抱えている問題だと思うのだが、データベースの設計が上流工程とやらで出来上がってしまっており、以降の開発作業ではデータベース設計をいじることができない場合がある。これは文字通り本末転倒の事態であると言える。何故なら、本来はアプリケーションが必要とするロジックに合わせてデータベースを設計するべきであり、データベースに合わせてロジックを記述するべきではないからである。実際にロジックを実装しながら、データベースの設計の問題に気づくことは多いだろう。だが、最初に設計を決め打ちしてしまうと、そのような間違いが修正されずに放置されることになる。

間違った手順で開発を行えば、炎上するのは必至。コーラを飲むとゲップが出るのと同じぐらい当然のことである。

きしだ氏も『開発できない「設計書」が出来上がる』と仰ってるが、まさしく「開発できないデータベースができあがる」要因がSIerの開発手順に含まれているように思う。

仕様変更

アプリケーションを実装している途中で、顧客の要求によって仕様が変更されてしまう、あるいは新たな機能が追加されてしまうといったことはよくあるだろう。そして、「急な変更だからデータベースはそのままにして、ロジックで何とかしてしまおう」という判断をするのもよくある話である。

だが、この判断は間違っている。本来は必要なロジックが変わればデータベース設計も見直しをするべきであり、ロジックとかけ離れた設計のデータベースを使い続けることは、開発効率を低下させ、ソースコードのメンテナンス性を低下させ、テストの手間を増やし、バグの原因となり、工期を遅らせてしまうことになる。まさに負のスパイラルだ。

アプリケーションの仕様変更や機能追加が生じたら、まずはデータベース設計の見直しをしよう。遠回りに感じるかも知れないが、小手先でごまかそうとすると、返って大きなコストを支払うことになってしまう。

マイグレーション

「データベース設計を見なおせとか気軽に言ってくれるよなー」と思ったそこのアナタ!!そんなアナタにオススメしたいのが、マイグレーションだ。マイグレーションと言ってもデータベースの移行作業(アップグレードや他の製品への乗り換え)のことではなく、データベースの変更管理のことである。

最近はソースコードのバージョン管理の重要性は広く理解されるようになっているが、データベースのバージョン管理についてはまだ認識が甘いように思う。ソースコードをバージョン管理することで得られたことはとても大きい。いざとなれば巻き戻せば良いので、恐れることなくどんどんコミットすることができるようになったのではないだろうか。データベースの場合も全く同じで、バージョン管理をしておけば何か問題が生じたときに元に戻すことが可能であるため、過剰に恐れることなくデータベースの再設計が可能となる。

最近、「DBスキーマもバージョン管理したい!」というタイトルのとても良いスライドが発表されたので、ここで紹介させてもらおう。(PostgreSQLカンファレンスのLTのもののようだ。)既に11000view以上のアクセスがあるので、目にされた方も多いと思うが、もしまだ見てないようなら是非一読して欲しい。

ところで、データベースのバージョン管理はソースコードのバージョン管理にはない難しさがある。データベースの実体はひとつなので、データベースをフォークすることできない。また、データベースにはデータが既に入っているので、バージョン管理とバックアップを使い分ける必要がある。また、データが大きければ変更作業(ALTER TABLE等)には時間がかかってしまうだろう。従ってソースコードほどは気軽にバージョン管理することはできないが、変更作業をコントロール下に置くというのは非常に重要なことであると言える。

gitを始めとするバージョン管理ソフトウェアが普及したことによって、反復的な開発手法が可能となった。だが、ソースコードだけバージョン管理をして、データベースはバージョン管理しないというのでは片手落ちである。プログラムのデプロイとデータベースのマイグレーションはセットで考えよう。

マイグレーションは強力な保険である。過剰に失敗を恐れることなく、どんどんリファクタリングしてアプリケーションのロジックに合ったデータベース設計にすると良いだろう。

なお、データベースのリファクタリングの話については、そのまんまだが「データベースリファクタリング」というタイトルの書籍で詳細に解説されている。だが、この書籍の解説ははっきり言って気に入らない。何故なら「どのように○○という種類の変更を行うか」ということの説明に終始しており、どのように設計を改善すべきかという点については一切触れられていないからだ。また、リレーショナルモデルに対する考慮にも欠けているように感じられる。(特に関連テーブルという概念を用いた解説があるのは大きなマイナスポイントだ。)そのため、Web+DB Press Vol.77で、データベースのリファクタリングについて解説したので、もし興味があればそちらを参照して頂きたいと思う。なお、書籍「データベースリファクタリング」は網羅的であり、読んでも損ではないだろう。ただしリレーショナルモデルに対する考慮が欠けている点には要注意である。

まとめ

データベースアプリケーション開発において、データベース設計は極めて重要である。データベース設計に問題があると、開発効率やメンテナンス性に大きな悪影響を及ぼしてしまうことになる。

幸いにして、リレーショナルデータベースでは正規化や直交性といった設計理論が存在しており、それらを実践すればまず間違いはない。問題は、SQLはリレーショナルモデル以外のデータも表現することができてしまうという点であり、そういったデータに対してリレーショナルモデル用の設計理論を適用しようとしても上手く行かない。どのデータ(テーブル)が正規化でき、どのデータが正規化できないかを見極めるには、リレーショナルモデルについてもっと詳しく知る必要があるだろう。

このような理由から、データベース設計は、上流工程でもDBAでもなく、本来は開発者がするべき作業である。遅いクエリを見つけてインデックスをつけるといった判断や、マイグレーションを用いた変更作業そのものであればDBAでもできるかも知れないが、どのように変更するかはアプリケーションのロジックに基づいて判断する必要があるため、開発者にしか判断することはできないだろう。当然ながら、開発者は「どのように変更すべきか」を判断できるだけの知識を持っている必要がある。

このテーマについてもっと詳しく知りたい人は、本文でも触れたがぜひWeb+DB Pressの連載を読んで頂きたい。

また、月末に予定しているデータベースエンジニアのための技術勉強会 第3回(エンバカデロ社主催)でも、データベース設計について詳しく解説する予定である。興味があればぜひ聞きに来ていただきたい。

0 コメント:

コメントを投稿