まえに、
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
に書いたように、この方法を継承する必要はない
今は、テーブルを数字で表現しない。
だから、このやり方でやらなくていい。
たとえば、今風にすると、ロック&トランザクションをかけて、
その後、テーブルを更新するときに、満たさなければいけない制約をチェックする。
参照制約なら自動的にチェックしてくれ、おかしければエラーになるし、
それ以外でチェックしたければ、トリガーを組んでもいい。
(そのトリガーで制約を満たしていないことが分かったら、ロールバックする)
・・・なかんじかな。
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
に書いたように、この方法を継承する必要はない
今は、テーブルを数字で表現しない。
だから、このやり方でやらなくていい。
たとえば、今風にすると、ロック&トランザクションをかけて、
その後、テーブルを更新するときに、満たさなければいけない制約をチェックする。
参照制約なら自動的にチェックしてくれ、おかしければエラーになるし、
それ以外でチェックしたければ、トリガーを組んでもいい。
(そのトリガーで制約を満たしていないことが分かったら、ロールバックする)
・・・なかんじかな。