ウィリアムのいたずらの、まちあるき、たべあるき

ウィリアムのいたずらが、街歩き、食べ物、音楽等の個人的見解を主に書くブログです(たま~にコンピューター関係も)

トランザクションの矛盾回避-なぜ昔DBのテーブル名が数字だったか-

2015-11-24 15:52:42 | Weblog
まえに、

Sparkは来るのか来ないのか、Hadoopから乗り換えたほうがいいか、聞いてきた
http://blog.goo.ne.jp/xmldtp/e/789ccb053f6b7fa42efb5e21f9ad9a3c

で、

途中でてくるOCC(optimistic concurrency control:楽観的並行制御、楽観的排他制御、楽観的ロック)の話は、また別に書く


と書いた件について




■まず、何が問題?:わざと難しい問題設定

この話をするまえに、なにが問題か、確認しよう。
わざと難しい問題設定にする。

2つの関連あるテーブル操作のお話
たとえば、入庫テーブル(商品が入ってきた)と出庫テーブルと
あったとする。

このとき、入庫数の合計-出庫数の合計 = 在庫数 >=0である。

1.今、5個入庫していて、出庫0だとする

2.Aさんは5個出庫したいが、在庫がなくなるとこまるので
  1)5個出庫
  2)5個発注
  したとする

3.このとき、「なにもロックしていないと」5個出庫した時点で、
 Bさんが読み込むと、Bさんは在庫0と思い、発注をかけてしまう
 仮に5個かけたとしましょうか・・・

 この結果、Bさんに読み取られていることを気づかないAさんは、
 2)の5個発注を行い、在庫0と思ったBさんも5個発注する。
 →在庫10個となり、在庫が積みあがる。

 たぶん、Write-Skewとかの中で、これが一番難しいケースだと思う。
 なので、今回は、これで説明する

 ちなみに、上記エントリで言ってたOCCのwrite-skew問題は、

Making Snapshot Isolation Serializable 再考
http://d.hatena.ne.jp/okachimachiorz/20130331/1364709005

の「○Write-Skew」のところ「X+Y > 0 という制約条件」・・・とかいう話



■なにが難しいのか

★悲観的ロックはかけられない
 まず、「排他制御すればいいじゃん。Aが書き終わるまで」
 というかもしれない。
 いわゆる、悲観的ロックだ。しかし、これは現実的でない。
 それは、シナリオを書いて、ロックしている範囲(時間)を考えれば分かる。
 
【シナリオ】
 Aさんは5個出庫したい
 ●在庫検索=商品名入力
 ロックスタート

 ●在庫検索結果表示
  Aさんは5個出庫したいが、在庫がなくなるとこまる
 ●出庫画面=5個出庫
 ●発注画面=5個発注
 ロック解除
【シナリオEND】

●が画面である。つまり、複数画面遷移して、やっとこさ、ロック解除できる
これは昔のように1アクション1画面のような同期を取る画面遷移では、
DBとの通信がきれてしまうし(でなければ、Staticでずっと、ロック&トランザクション
を保持していることになる)非同期でも、ドンだけ長い時間ロックしているんだよ!
ということになる。

 つまり、ロックをずっとかけているのは、こんな長時間の場合、現実的でない

★デッドロックには「ならない」
 そもそも、デッドロックでは?と思うかもしれないが、それもならない。
 デッドロックは、更新しているテーブルが、交互になるときである。
 今回は
  Aさん=出荷、発注
  Bさん=発注
 なので、Bさんは、(待つかもしれないけど)2つのテーブルを操作しているわけでは
 ないので、落ちない

★楽観的ロックでもX
 楽観的ロックは「更新」日時をチェックする。
 たとえば、入荷、出荷テーブルでなく、「在庫」テーブルがあるのなら、
  Aさん=在庫テーブル更新、発注テーブルにレコードを「挿入」する
  Bさん=発注テーブルにレコードを「挿入」する
 このBさんが発注テーブルを更新するときに、在庫テーブルの更新日時を確認すれば、
 この問題は防げる。今回、Bさんは、在庫テーブルの更新日時が違っていたので、
 全部のトランザクションをキャンセルすればよい

 しかし、今回は
  Aさん=出荷、発注テーブルにレコードを「挿入」する
  Bさん=発注テーブルにレコードを「挿入」する
 新規レコードの場合、データを追加する=挿入するのであって、更新するのではない
 ので、更新カウンターを見ない=楽観的ロックは使えない




■昔の対策

そこで、昔は以下のような対策が採られた

・テーブルロックをかける順番を決めておく
・親-子-孫・・・という関係がテーブル間にある場合、
 子、孫のテーブルを修正し、親テーブルを修正しない
 場合であっても、親テーブルの更新日時を変更する
 (例:受注明細しか変更しなくても、受注テーブルの更新日時を上げる)
・子テーブルに追加するときは、その子の直上の親テーブルレコードの更新日時を上げる
 一番上の親は、「更新カウンタテーブル」というテーブル(これが、ロックをかけるとき、最上位のテーブルになる)の親テーブルのレコードに対して、更新日時を上げる
・テーブル削除は、論理削除とする。よって、修正と同じ更新日時処理をする。

こうした場合、「更新カウンタテーブル」の「出庫」「発注」レコードに対して
  Aさんが検索をかけた時間を保持(出庫・発注1:23:45とする)
  Aさんが出庫データ書き出し
   現レコード1:23:45/保持1:23:45で一致するので書き出しOK
   出庫レコード書き出し時間を1:23:47とする
  Bさんも検索(出庫:1:23:47,発注1:23:45)
  Aさんが発注データ書き出し
   現レコード1:23:45/保持1:23:45で一致するので書き出しOK
   出庫レコード書き出し時間を1:23:50とする)
  Bさん書き出し
   →発注1:23:45 保持1:23:50で
矛盾して書き出せない

これを実現する為には、テーブルのロックをかける順番と、親となるテーブルを
定めなければならなかった。
 そこで、01D100などのファイル名になった。
 ここで 01:データベーススキーマ、サブシステムなど
     100:テーブルを示す。2桁目が00のものは、最上位の親テーブル、
        以下110,120・・・のように数字を変えて親子関係を並ばせる。
こうすると、数字が順番に並んでいることを確認するだけで、ロックに確認が出来る




■技術承継は・・・

しかし、

プロセスを継承することが技術継承なのか?だとしたら、いつかは環境変化で継承できなくなる
http://blog.goo.ne.jp/xmldtp/e/0a68cbb384be26f161f314442b861106

に書いたように、この方法を継承する必要はない
今は、テーブルを数字で表現しない。
だから、このやり方でやらなくていい。

たとえば、今風にすると、ロック&トランザクションをかけて、
その後、テーブルを更新するときに、満たさなければいけない制約をチェックする。
参照制約なら自動的にチェックしてくれ、おかしければエラーになるし、
それ以外でチェックしたければ、トリガーを組んでもいい。
(そのトリガーで制約を満たしていないことが分かったら、ロールバックする)

・・・なかんじかな。
この記事についてブログを書く
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« プロセスを継承することが技... | トップ | 排他制御、確実なのは「二股... »
最新の画像もっと見る

Weblog」カテゴリの最新記事