■テーブルにロックをかけるケースを考える
たとえば、貯金残高や、在庫数など、2箇所以上で同時に操作されたときに、
矛盾を引き起こさない為に、データベースには、一時に1箇所でしかDBを
利用できないようにロックをかけたりする
さらに、テーブルが複数あって、関連を持っている場合
(どちらかのテーブルの主キーが他方のテーブルの外部キーの場合)、
1つのテーブルは処理完了して、もう一つのテーブルは完了していない
となるとおかしなことになるので、トランザクション処理として、
すべて更新するか、全く更新していないかの2つの状態になるようにする。
この場合にも、テーブルに対して、ロックがかかる。
■2つ以上が同時に操作すると、一方はロックのために待ちになる
このように2つ以上のアプリが同時に同じテーブルを操作しようとして
テーブルにロックがかかる場合、デッドロックで「なければ」、
一方のアプリが処理を行い、その間、他方のアプリが待ちとなる。
待ちの場合、すぐにまたアクセスにいけるようにしてしまうと、
ロック解除されるまでに何回もアクセス可能かどうか聞いてしまい、
このループの処理ばかりするようになり、CPU処理量100%となってしまうので、
ふつうは、ロック待ちになると、Sleepを入れて寝かせる。
ただし、このsleepは、シードなしはもちろん、時間でシードを持たせると、
別の場所で、同時刻のシードとなることがある。
sleepするアプリが2つ以上あって、シードなしの場合、または
同じシードの場合は、乱数を振ると、
同じ結果が返ってくる=同じ時間寝る=同じ時間に起きる=またロックになる。
■スケールアウトすると、どこかで誰かがロックのために待ちになる・・・
DBを更新するアプリが少ない場合は問題ない。
ただ、更新するアプリサーバーが増えてきて、同じDBをアクセスするように
なると、ロック待ちが起こり出す。
このとき、複数サーバーでロック待ちをしている際に、
何らかの弾みで、上記のように待ち時間がたまたま合ってしまうと、
・・・永遠と振られる乱数結果が一緒で、ずっとロックしていることになってしまう
(ライブロック)
つまり、スケールアウトするときにテーブルロックの問題が起こることがある
この事態を避けるには、どうしたらよいか?
(それいぜんの、CPU100%でループという事態も含めて)
■この解決法
そもそも、やりたいことは、ロックによる排他制御なのか?
順番に処理させたいという目的があって、その手段として、
トランザクション&ロックをつかっているのではないのか?
・・・だとしたら、守るのは順番である
順番を守るだけなら、ロックをかけなくてもいい。
メッセージキューを1つだけ用意し、そのキューに入っている
DB更新処理を順番に行えばよい。
1つしかないので、事実上、排他的に処理される
スケールアウトする場合には、キューを利用したほうがよいこともある。
たとえば、貯金残高や、在庫数など、2箇所以上で同時に操作されたときに、
矛盾を引き起こさない為に、データベースには、一時に1箇所でしかDBを
利用できないようにロックをかけたりする
さらに、テーブルが複数あって、関連を持っている場合
(どちらかのテーブルの主キーが他方のテーブルの外部キーの場合)、
1つのテーブルは処理完了して、もう一つのテーブルは完了していない
となるとおかしなことになるので、トランザクション処理として、
すべて更新するか、全く更新していないかの2つの状態になるようにする。
この場合にも、テーブルに対して、ロックがかかる。
■2つ以上が同時に操作すると、一方はロックのために待ちになる
このように2つ以上のアプリが同時に同じテーブルを操作しようとして
テーブルにロックがかかる場合、デッドロックで「なければ」、
一方のアプリが処理を行い、その間、他方のアプリが待ちとなる。
待ちの場合、すぐにまたアクセスにいけるようにしてしまうと、
ロック解除されるまでに何回もアクセス可能かどうか聞いてしまい、
このループの処理ばかりするようになり、CPU処理量100%となってしまうので、
ふつうは、ロック待ちになると、Sleepを入れて寝かせる。
ただし、このsleepは、シードなしはもちろん、時間でシードを持たせると、
別の場所で、同時刻のシードとなることがある。
sleepするアプリが2つ以上あって、シードなしの場合、または
同じシードの場合は、乱数を振ると、
同じ結果が返ってくる=同じ時間寝る=同じ時間に起きる=またロックになる。
■スケールアウトすると、どこかで誰かがロックのために待ちになる・・・
DBを更新するアプリが少ない場合は問題ない。
ただ、更新するアプリサーバーが増えてきて、同じDBをアクセスするように
なると、ロック待ちが起こり出す。
このとき、複数サーバーでロック待ちをしている際に、
何らかの弾みで、上記のように待ち時間がたまたま合ってしまうと、
・・・永遠と振られる乱数結果が一緒で、ずっとロックしていることになってしまう
(ライブロック)
つまり、スケールアウトするときにテーブルロックの問題が起こることがある
この事態を避けるには、どうしたらよいか?
(それいぜんの、CPU100%でループという事態も含めて)
■この解決法
そもそも、やりたいことは、ロックによる排他制御なのか?
順番に処理させたいという目的があって、その手段として、
トランザクション&ロックをつかっているのではないのか?
・・・だとしたら、守るのは順番である
順番を守るだけなら、ロックをかけなくてもいい。
メッセージキューを1つだけ用意し、そのキューに入っている
DB更新処理を順番に行えばよい。
1つしかないので、事実上、排他的に処理される
スケールアウトする場合には、キューを利用したほうがよいこともある。