データベースやSQLに関する知識をもっと身に付けたいなと思っていて書籍を探していたところ、下記のような本を見つけました。
現場で起こりがちなRDBのアンチパターンが実例をもとに紹介されており、全体的にとても勉強になりました。
今回は特に印象的だった章を掻い摘んで感想を述べていきたいなと思っています。
第1章 データベースの迷宮
この章ではデータベースの不適切な名前付けや、構造が紐解けない設計について説明がされていました。
どんなアンチパターンが紹介されているのかと軽い気持ちで読み始めたのですが、初っ端から思い当たる節が多すぎて、思わず頭を抱えてしまいそうになりました。
エンジニアとしてのキャリアをスタートさせたばかりの頃、社員数の少ないベンチャー企業でとある新規サービスの開発を担当することになりました。
最初は3人ほどで開発を行っていたのですが、紆余曲折あり、なんと最終的に自分一人で開発を行うことに。もちろんDB設計も私がやらないといけないのですが、その際、当然の如くDBの知識はほとんどありません。
そのような状態でDBを設計したものですから、「memo1」、「memo2」、「memo3」……と何が入っているかよくわからないカラムが量産されたり、「hoge_nema」などタイピングミスのあるカラムがあることに後から気がついたりと、散々なものになってしまいました。
数年後、サービスの改修をすることになった際、これは何を保存したカラムなのか開発者である自分自身すらわからなくなってしまたのですが、サービスを新規開発をしていた時点でこのシステムは破綻していたのでしょう。
結局このサービスは私がこの会社を退職してすぐに終了したのですが、後任者に引き継がれていたらどうなっていたかと、想像するだけでゾッとしてしまいます。
今だったらそんなこと絶対しないのにと思うのですが、そう反省できるのはこのような経験があったからこそなので、今後も同様の失敗を繰り返さないようにと、最初の章から身に包まされるような思いとなりました。
第3章 やり過ぎたJOIN
JOINの仕組みを理解せず、不用意にたくさん使用するとDBに負荷がかかるよ、というアンチパターンを紹介した章。
元々JOINを何重にも使用しており、速度改善の際にもっとシンプルにSQLを組めるのでは?と多段JOINを解決したところ、明らかに該当ページの読み込みが早くなったという経験を過去にしており、この話も実感を持って読み進めました。
JOINは不用意に使い過ぎないほうがいいよというのは、DBの高速化について調査をしていると必ずと言っていいほど出てくる話ですが、実際のところ、なんでだろうと思っていたところではありました。
本書ではJOINのアルゴリズムが紹介されていました。RDBでは、外部表を上から一行ずつ処理を行い、外部表と紐づいた内部表も上から一行ずつループする形で参照していくNLJ、外部表と内部表を一度に全件読み込んで処理をするHash Join、内部表と内部表を全件ソートしてから上から順に比較するSort Merge Joinの主に3つのアルゴリズムが使用されています。
例えば何万件もあるテーブルのデータをJOINして、それを上から全件ループして処理するとなると、そりゃあ莫大な時間が掛かるよな……と、納得せざるを得ませんでした。
第6章 ソートの依存
RDBMSの機能の一つであるソート機能・ORDER BYの使い方を間違ってしまったことで、速度遅延の原因になってしまったというアンチパターンを紹介した章。
システム開発ではよく「⚪︎⚪︎を昇順(または降順)で表示させてほしい」とお願いされる場面が多く、そうなるとORDER BYを使用してソートするというのが一般的な実装方法となってきます。
しかし、このORDER BY、後々にデータが増えてきたときにDBの動きが重たくなる一要因になっちゃうんですよね……。これも本当に現場でも実感しました。
ORDER BYでパフォーマンスが下がる原因として、RDBの元となっている考え方であるリレーショナルモデルがあるそうです。
リレーショナルモデルは集合を扱うデータモデルのことで、この集合には「重複がない」「実在する要素しかない(NULLがない)」「要素に順序がない」という性質があります。
RDBMSはこのリレーショナルモデルの領域を超えて幅広くデータを扱えるようにしているのですが、やはりリレーショナルモデルから外れてしまう処理を扱うのが苦手。ORDER BYはまさに順序を扱っており、RDBの苦手分野なのです。
更に、RDBMSにはクエリを実行するにあたっての評価順序があり、ORDER BYは全てのデータを取り出してから並び替えの処理が行われます。データを取り出してから並び替えが行われるため、高コストな処理になってしまうのです。
並び替えの処理が苦手で、実行順序も遅いとなると、ORDER BYを使用すると処理が遅くなってしまう、というのは納得がいきます。
しかし、だからといってORDER BYを使用しないといけない場面は必ず出てくるため、なるべくデータを小さくしたり、INDEXを使用したり、その時々に合わせた方法でしっかりと検討していくしかないよなぁ、と思いました。
第10章 転んだ後のバックアップ
バックアップの間違った運用方法のアンチパターンについてまとめた章。
いつシステム障害が起こっても復旧できるようにバックアップの体制はちゃんと整えたほうがいいとわかっておきながら、面倒臭さや油断の方が先行してしまい、どうしても疎かにしてしまいがちです。
前職のサービスではバックアップに関する部分も私が考えていたのですが、本書のアンチパターンにもあるように、mysqldumpを保存しとけば何とかなるだろうと、ひたすら定期的にdumpを取り続けるということをしていました。
幸い、運用している最中に大きな障害が発生することはなかったのですが、もし何か大事故が起こっていたとしたら冷や汗をかいてしまいます。
システムをリリースする前から、どのようにバックアップを行うのかや、どのようにデータを復旧していくのかの設計をしっかりと固め、チーム内で定期的にリストアのロールプレイングを行う必要性を感じました。
また、それなりのシステムはAWSなどのクラウドサービスを必ずと言っていいほど使用しており、高いコストを割いてでもクラウドサービスを使用する利便性はここにもあるのだなということを改めて実感しました。
第12章 監視されないデータベース
監視の仕組みを整えていなかったことにより、システムが落ちても問題が特定できずに復旧ができないアンチパターンをまとめた章です。
こちらについては、RDBMSのみならず、システム全体に関わってくる話な気がします。
時々インターネットで、保守運用担当の社員が何の仕事をやっているのかわからず、ずっと暇そうにしているように見えたため解雇してしまったところ、途端にシステムが落ちるようになってしまい、専門家がいないため何も手の施しようがなくなってしまった、という話が寓話の如く流れてきます。
私が実際に体験した話ではないのですが、監視している人がいなくなったことで、システムがまともに運用できなくなってしまうことは容易に想像できます。
それなりに大きいシステムであればインフラ担当のエンジニアがおり、私も現職になってからそのようなシステムに携わることも多くなっていますが、実装者側であってもこの監視の問題については考えていかないとなと思いました。
第17章 複雑なクエリ
長大になってしまった複雑なクエリにより生まれるアンチパターンについてまとめた章。
複雑なクエリの発端として、「無知ゆえの豪腕」と「腐ったテーブルの腐ったクエリ」の2つが挙げられていますが、私はどちらも経験があります。
「無知ゆえの豪腕」により長ったらしくてよくわからないスロークエリについては、まさに私が前職で生み出してしまったものそのものです。
ほとんどRDBMSやSQLに関する知識が備わっていないまま、手探りで調べながら書いているため、とりあえず希望の動きが実現できればいいと、複雑なコードを書いてしまっていました。
後から追加機能の改修を行なったり、リファクタリングを行なったりする際、どこで何をしているのかわからなくなり、まるで絡まってしまった糸を解くような気持ちに。数年前の私、何やっていたんだと頭を抱えてしまうことが何度もありました。
「腐ったテーブルの腐ったクエリ」に関しても見覚えがあり、元々のテーブル設計がおかしいことにより、長ったらしいSQLコードを書かないと希望のデータが取り出せないという事例が発生しました。
スキルアップやコードレビューの大切さや、テーブル設計をしっかりと固めておくことの重要さを改めて実感しますが、もし今後、このような複雑なクエリを避けられない事態になっても冷静に判断していきたいです。