失敗から学ぶRDBの正しい歩き方 Software Design plus
トリガーを利用して変更前のデータと同じになるようにする 例)古いカラム名の「delete_falg」のINSERTやUPDATEのactionに対してトリガーを定義し、新しいカラム名のdelete_flagを同じデータにする
後からデータを遡りたいときに事実が失われている設計をしてしまう」
パフォーマンスの劣化を考えて、あえて履歴を保存しない設計を取るケースもあります。その場合に大切なこととして、もしRDBの責務内で履歴を持たない場合は、次のように別手段で必ず持たせるようにしましょう。
RDBのレプリケーションには、遅延レプリケーションという機能があります。この機能は指定した時間分、スレーブDBに対して、マスタDBからのレプリケーションを遅延させることができます。たとえば1日遅れのスレーブDBを作ったり、2時間遅れのスレーブDBを作ったりすることができます。遅延レプリケーションはDBに柔軟な設計を与えてくれ、おもな目的として「マスタDB上で行われた誤った作業から保護する」「システムのデバッグ時の再現手法として使う」場合に利用され
Viewを活用することで複雑なクエリも防げます
SQLの結果を実体のあるViewにする機能としてマテリアライズド・ビューがあります。マテリアライズド・ビューはクエリの結果のテーブルを作ることと一緒ですが、再作成のときにテーブルの作りなおしが不要で、共有ロックのリフレッシュのみで良い、などのメリットがあります。SQL
商用DB
INDEXを利用するためには次の2つの条件が必要です。 ①検索結果がテーブル全体の20%未満 一般的な実務レベルでは10%未満を指標にするのが良い ②検索対象のテーブルが十分に大きい 数万~数十万行が目安。1,000行程度のテーブルの場合はINDEXを参照するよりもテーブルスキャンが効率的なケースが多いため、INDEXは利用されにくい。たとえば都道府県マスタのように47行しかないテーブルの検索では、INDEXが利用されないケースが大半 ここ
・INDEXが利用されない例 SELECT * FROM users WHERE age * 10 > 100; この例では、INDEXをage列に対して設定している場合でもINDEXは使われません。検索の対象は age * 10 の計算結果となるため、すべての行に対して計算・比較する必要が出てくるからです。この
部分一致でINDEXを利用したい場合は、全文検索インデックスなどを利用する必要があります。
インデックスショットガン
とりあえず削除フラグ」と呼ばれ、現場でもよく見られるアンチパターンです。削除フラグは次のようなRDBの問題を多く含んでいます。 ・クエリの複雑化 ・UNIQUE制約が使えない ・カーディナリティが低くなる
Viewにアクセスする場合は、Viewで定義されたクエリが再実行されますので、高速化にはつながりません。高速化を意識するなら、PostgreSQLならばマテリアライズド・ビュー、MySQLならサマリーテーブルを生成するのも良いでしょう。
PostgreSQL、MySQLの標準的なINDEXの実装であるBTree INDEXは、データを「ソート済み」の状態で保存しています。勘の良い読者のみなさんは気づきましたね? そうです、対象のソート結果とINDEXが同じならば、INDEXから取り出せば良いのです( 図6.3)。これをORDER BY句狙いのINDEXと言います。
ORDER BYでINDEXを使うことを始め、RDBMSのソートについてお話しました。では実際に業務でうまく活用するためにどうするか?と言われた場合の答えは「しっかりと実行計画を見る」ということです。
PostgreSQLもMySQLも、ソートの処理はメモリ上で実行できる場合にはクイックソートを利用しますが、それ以上のサイズになると、ソート結果をファイルに書き出す「外部ソート」になります。ORDER BYの外部ソートは処理が遅くなる典型的なパターンの1つで、次の2つの問題があります。 ・データ量が成長することで当初は問題がなかったクエリがある日突然外部ソートになる ・クイックソートか外部ソートかは実行されるまでわからない
トランザクションが必要な課金系のデータはRDBMSに保存し、処理の結果はRedisに保存して参照はRedisから行うといった構成はよく取られます。
意味を持たせたIDを「意味を含んだID」、あるいは「論理ID」「スマートカラム」と言ったりします。IDとはidentificationのことですから、識別できる一意の値以上の意味を持たせてはいけません。
似たような属性のデータの場合、1つのテーブルに保存してしまいがちですが、パフォーマンスの面を考えても、テーブルを分けたほうが良いケースが圧倒的に多いです。
EAV(
Polymorphic Associations(
トリガーを使っても良い場面 設計における隠された状態と違うのは、制約を犠牲にしないという点です。そこで筆者は、アプリ側で機能を実装するのと比較したうえで次のようなメリットがある場合のみ、トリガーの採用を検討しています。 ①パフォーマンス的メリット ②アプリ側の実装が大幅に削減できる ③既存のアプリの振る舞いを維持したまま、仕様を変更できる ①の
「なんでもJSON」の危険性
Web APIの戻り値 TwitterのWeb APIと連携するため、 図8.2 のように、メインで使うテーブル(twitter_account)とサブのテーブル(twitter_account_detail)を分けつつ、履歴も残す設計を
ALTER文による型変換は、一番強いロックであるACCESS EXCLUSIVEを取る
MySQLは、外部キー制約の子を更新しても親テーブルに共有ロックを取るため、デッドロックの温床になります。この問題への正しい対応は排他ロックを取ることですが、排他ロックは正しく順番を待たせるため、たびたびパフォーマンスのボトルネックになります。
普遍的な値にENUMを使うことは、表記揺れを防ぐ強い制約になります。しかしDOMAINの例と同様に、データ型の変更にはALTER文が必要で、仕様変更にはとても弱く、また制約されている文字列を知る方法が限られています。
状態を持つCHECK制約