/note/tech

楽観的ロックでいいじゃん!

要約:

■ 1. 排他制御の2つの方法と歴史的背景

  • データベースのトランザクション処理を実行する場合対象となる行の排他制御が必要である
  • 排他制御は同時実行処理において必要不可欠だが使い方を誤ると簡単なはずの処理が難しくなる可能性がある
  • リソースをロックする方法は2種類ある:
    • 悲観的ロック
    • 楽観的ロック
  • スタンドアロンでプログラムが動いていた時代は1ユーザ・1プログラム・1データのみが存在するため排他制御を考えなくてもアプリケーションの開発は行えた
  • コンピュータがネットワークでつながるようになってデータが複数のユーザからアクセスされるようになると排他制御を無視してプログラムを書くことはできなくなった
  • DBMSが利用できるようになってファイルレベルでの同時実行制御からデータベースレベル・テーブルレベル・ページレベル・行レベルといったロックのメカニズムが提供された
  • 現在はHTTPという不安定なプロトコルの上でのトランザクションが求められている

■ 2. 悲観的ロックと楽観的ロックの定義

  • 悲観的ロック:
    • ステートフルなロック
    • 更新したい対象のリソースを照会して取得した直後から更新が終わるまでロックを維持する
    • ロック時間は長時間でロックは独占的
  • 楽観的ロック:
    • ほぼステートレスなロック
    • 更新したい対象のリソースを照会してもロックはかけず本当に更新が必要になった段階でその対象リソースをロックする
    • ロック時間は短時間でロックは非独占的

■ 3. 悲観的ロックのメリットとデメリット

  • 悲観的ロックは「俺様が更新するリソースは全部俺様のものだ他人にはアクセスさせないぞ」というものである
  • DBMS上でロックを実行したユーザアカウントのコンテキストでロックが解放されるまで排他制御が実行され続ける
  • メリット:
    • ロックを取得したユーザが見ているリソースが他者から変更されないことを保障する
  • デメリット:
    • ロックが維持されている間他のユーザはそのリソースにアクセスできない
    • ロックを維持することによりデッドロックを引き起こしやすくなる可能性が高い
  • この記事で提示したいのは「そのロック本当に悲観的じゃなきゃいけないの?」という疑問である

■ 4. 楽観的ロックで十分な理由1:認証と認可

  • ユーザの認証と認可をきちんと処理していれば更新の競合はほとんどありえない
  • データベースを設計する際に考えなければならないのは「ユーザの認証」と「ユーザの権限」である
  • 悪い例は何でもできる特権を持ったユーザアカウントですべてのデータベースオブジェクトを作りアプリケーションからのアクセスもそのアカウントで実行してしまうこと
  • 権限レベルの段階:
    • Level0:アクセス許可なし
    • Level1:読み取り専用
    • Level2:読み取り・書き込み可能
    • Level3:特定データベースの変更・構成可能
    • Level4:全権限の所有
  • 理想的にはデータベース設計者がデータベースを完成させたら権限をDBAに委譲してデータベース設計者からすべての権限を奪うのが望ましい
  • Level0からLevel2までの範囲で権限を設定していれば無駄なトランザクションの発生を抑えられる

■ 5. 楽観的ロックで十分な理由2:業務プロセスとワークフロー

  • 業務上同一行を複数人で同時に更新するプロセスはほとんど考えられない
  • 業務システムにおいて情報が更新されるためには何らかのビジネスプロセスと対応している必要がある
  • 例えば人事情報の変更を考えた場合人事部に変更届を提出するのが普通である
  • 何らかの届けが提出されそれが認知されて処理されるにあたっては「審査」というワークフローを通る必要がある
  • 審査が通るまではトランザクション処理は実行されることがない
  • このような「変更」-「審査」といった業務フローの場合同じ行が複数の人から同時に更新されることは起こりえない
  • 業務における「変更」には必ず権限を持つ人の「承認・審査」が伴うはずである
  • この制約がある限り「変更」は即座には実行されず「変更要求」-「承認・審査」-「変更実行あるいは変更の却下」の流れになる

■ 6. 楽観的ロックで十分な理由3:対照表による履歴管理

  • 動的に変化するデータを対照表の形で作成してしまえば追加のみなのでロックは必要ない
  • 例えば小売店における商品の売価を考える
  • 商品というエンティティに売価を入力して更新するやり方は後に営業分析する際に売価の動きを追跡することが難しくなる
  • 売価を対照表で管理すると商品ID・店舗ID・発生日時・イベント・売価などで管理できる
  • このモデルに従うと商品の売価においては変更が生じてもデータベース側では「行の追加」しか発生しない
  • 変更イベントが発生しても商品の売価という対照表の行を更新するということは起こりえない
  • したがって楽観的ロックすら必要がない

■ 7. 楽観的ロックで十分な理由4:データの所有権管理

  • データの所有権を管理すれば更新の競合はほとんどありえない
  • アプリケーションによって行に所有権を設定し所有権のある行だけを処理の対象とすることを考える
  • 所有者IDをUPDATEステートメントのWHERE句に含めることで他の所有者の行を更新することがなくなる
  • 行の所有者を明確にしているので悲観的ロックにすることなく楽観的ロックで十分になる
  • しかし所有者の変更を追跡しようとすると多対多のモデルになるので結局は対照表の構造に帰着する
  • したがって理由3の繰り返しになり行を更新するという話がなくなる

■ 8. アーキテクチャの変遷とトランザクション軽量化の重要性

  • 時代はメインフレームによる集中処理からクライアントサーバの分散型・Webシステムにおける集中処理型・スマートクライアントによる分散型と集中・分散を繰り返している
  • どのようなアーキテクチャが採用されるにせよコンピュータのメモリは有限でありデータをどこかの2次記憶装置に永続化する必要がある
  • 工夫次第でトランザクションを軽量なものにすることができる
  • 最終的に一番負荷がかかるのはデータベースが稼動しているサーバである
  • スケーラビリティやパフォーマンスを求めるのであればいかにサーバリソースを消費しないで同時実行性を高めるかということにつきる

■ 9. 実体験に基づく楽観的ロックの優位性

  • 12-13年前にCA-ClipperというXbaseのシステムで悲観的ロックモデルによりアプリケーションを作成した際にこのロックモデルの欠点がよくわかった
  • ネットワークのトラフィックの増大・アプリケーションパフォーマンスの低下を招くことが低性能のPCで証明された
  • 結局楽観的ロックに切り替えられるようクライアントでトランザクション用のキューを作りサーバ側にバッチアップデート要求を出すような作りにした
  • MS-DOSのファイルシステムレベルでの排他制御も経験したがロック時間が長くなればパフォーマンスが低下することがわかった
  • ロック時間は短いほうがいい
  • SQLを利用するDBMSにおいても結局理屈は変わらない
  • ユーザのロールモデル・業務上のワークフロー・データベースの設計方法を工夫するだけでも「悲観的ロック」から逃れることができる
  • SOAのアプローチになってサービスとの疎結合が求められると「楽観的ロック」中心のアプローチでないとサービスの開発ができない
  • 永続化処理を軽量にすることが今後も求められる

MEMO: