勉強日記

チラ裏

理論から学ぶデータベース実践入門 ch13 リファクタリングの最適解

gihyo.jp


まとめ

リファクタリング

  • DBリファクタリング: 1つ以上のテーブルの設計を変更する
  • 定義変更自体はALTERコマンド一撃
  • 現実、そんなに簡単な話ではない
    • アプリケーションはリリース済、運用中
      • テーブルにデータ格納済
      • テーブルアクセス依存の処理がある

DBのリファクタリングは大変

  • 影響範囲甚大
    • クエリの書き換え
    • 各種制約の確認
    • ビューの修正
    • トリガーの修正
    • ストアドプロシージャの修正

マルチアプリケーションにおけるDB環境

  • 共通のDBを複数のアプリケーションから参照するのはよくあること

なぜリファクタリングが必要なのか

  • アプリケーションが変化する以上、本質的に必要
  • しかし大変なので「やらない理由」を探して放置しがち
    • 負債は貯まるばかり
  • 負債を返済するにはリファクタリングするほかない

リファクタリングの手順

  • アプリケーションへの影響を抑えつつ、アプリケーションのペースに合わせた移行をするための手順
    1. 作業前後のバックアップ
    2. スキーマの変更
    3. データの移行
    4. アプリケーション移行のためのトリガーの作成
    5. アプリケーションのデプロイ
    6. 移行のためのトリガーの削除
    7. 移行のためのカラムの削除

スキーマの移行期間

  • 移行期間を設けるケース
  • 古いロジックのアプリケーションが一定期間残る
  • そのため、元のスキーマと変更後のスキーマが共存する必要がある

反復的なリファクタリング

回帰テスト

  • 十分なテストケースが存在していることが前提
    • アプリケーションの機能が損われたらリファクタリングではない、破壊しているだけ
    • 機能が損われていないことを確認する方法はテストしかない

ベンチマークテスト

  • リファクタリングの結果、パフォーマンスが著しく低下するケースは珍しくない
  • パフォーマンスが低下していないことを確認する方法はベンチマークテストしかない
  • 本番に忠実な環境を用意すること

マイグレーション利用のススメ

  • スキーマのバージョン管理
    • RoRのActive Recordで導入されたしくみ
    • 【補】Laravelでもおなじみ
    • 制限: バージョン分岐ができない
  • 戻す場合のロジックもテストすること
  • バックアップじゃダメなの?
    • ダメ
    • データも巻き戻ってしまう

トリガーを使って2つのテーブル間で同期を取る

  • 移行期間中、新旧テーブルのデータの整合性を確保するために使う
    • 循環に注意
    • 移行が完了したらトリガーを削除する
    • トリガーのテストも忘れずに

リファクタリングの種類

インデックスの追加・削除

  • 軽微
    • クエリ書き換え生じない
  • テーブルが持つ論理的な意味が変更されうることに注意
    • 主キーやユニークインデックスによる一意性制約の導入/削除
  • ベンチマークテストで性能劣化のなきことを確認せよ

カラム名の変更

  • 過渡
    • 異なる名前で同じデータを持ったカラムを2つ持たせる
    • 行単位のトリガーで同期
      • MySQLなどの製品でサポート

NOT NULL制約の導入

  • 1NFにするために後からでもNOT NULLをつけることは大切
    • あるいはリレーション分割
  • NOT NULLをつけるにあたり、既存のデータにNULLが含まれていたら若干の調整が必要
    • NULLだった個所の値がデフォルト値でもNULLでも動くようにクエリを書き換える

主キーの定義変更

  • 変更が必要になるケース
    • 主キーが既約ではない場合
    • 主キーから別のカラムへの関数従属性がなくなってしまった
    • サロゲートキーからナチュラルキーへの変更、あるいはその逆

無損失分解

  • 移行期間が必要な場合は面倒
    1. 元のテーブルを残しつつ新しいテーブルを作成
    2. 双方のテーブルでデータの同期
      • トリガー
      • カスケード

テーブルの垂直分割と統合

  • 複数のテーブルで直交性が満たされていない場合、テーブルの統合が必要

コラム: 関連テーブルの実態

  • 1:N -> M:N
  • 2段階のリファクタリングのショートカットと考えられる
    1. 主キーを変更して行持ち
    2. 第2正規化

リファクタリングのためのベストプラクティス

正規化と直交性

カラムではなくテーブルを追加する

  • カラムを追加する前に熟考せよ:
    • カラムの初期値はあるか
    • 主キーに対して関数従属しているか
  • これらを満たすならカラムを追加して問題ない
  • テーブルを追加するというアプローチも
    • メリット
      • 既存のテーブルを変更しなくてよい
    • デメリット
      • JOIN増える
        • 内部表に対して主キーでアクセスできる実行計画ならば、パフォーマンスペナルティは存外小さい

SELECT * を使わない

  • 1NF崩れる
    • 順序を持ち込むことにほかならないため

アプリケーションを疎結合

  • データへアクセスするロジックを共通化する
    • アプリケーション側の修正個所が少なくなるよう