実際の皆さん、こんにちは。London Tech Talkへようこそ。イギリスのロンドンからお届けしています。Ken Wagatamaです。
本日は、London Tech Talk 第四弾目となるBookclub、Database Internalsの振り返りを収録していきます。
今回は、Chapter 5 Transaction Processing and Recoveryについて振り返っていきます。
今回のショーの目玉は、何と言ってもページキャッシュとトランザクションですね。
そこに合わせて、コンカレンシーコントロールの話題も入ってきているので、第1章から第7章にかけての前半部分でも一番難解な章なんじゃないでしょうか。
この章の目的としては、なぜデータベースシステムがトランザクションを実行するのに、こんなに考えることがたくさんあるのかということを理解できたらいいんじゃないかなと思っています。
この章のコンテンツの、こういう構成になっているロジックなんですけれども、まず大前提として、2つ大事なデータベースに期待されるものがあります。
1つはデータベースは整合性を担保するというところですね。
2つ目は、データベースというのはクラッシュしてもデータを復元してくれる、リカバリしてくれると。
こういった2つの大前提がデータベースにあります。
まず1つ目のデータベースは整合性を担保するというところなんですけれども、整合性を担保するためにデータベースがクエリを実行するときにはトランザクションという単位でクエリを実行していきますね。
しかし、トランザクションを実行するためには全てのトランザクションをシリアルに実行していたら、使い物にならないほど遅いんですね。
なので、平行処理ですけれども、コンカレンシーにトランザクションを実行していきたい。
逆に言うとコンカレンシーにトランザクションを実行できる仕組みがないと、実用段階というかプラクティカルに使えるデータベースを実現できないんですね。
なので、そのためにどのような平行処理の仕組みがデータベースにあるのか、それを理解するために必要な基本的な概念は何かというのをshowの後半のコンカレンシーコントロールでやっていきます。
2つ目の大前提なんですけれども、データベースはクラッシュしてもきちんとデータを復元してくれる。
逆に言うと、データベースのインスタンスがクラッシュするたびにデータがなくなってしまったら、そもそもデータを保存するプロセスではないですよね。
なので、このデータベースがクラッシュしてもデータを復元するときに、どのようにリカバリしているのか、どのようなレイアウトでディスクに保存しているのかというのを詳しく深掘っていくshowになっています。
ここをさらに問題を複雑にさせるのが、ページキャッシュと呼ばれるメモリにそのテンポラリーのデータを保存しているんですけれども、
これが入ってくることによって、シンプルにデータベースはファイルに保存しているからデータを復元するということが言えなくなってくるんですね。
ページキャッシュというテンポラリーなデータを保存する置き場と合わせて、どのようなリカバリシナリオがあるのかというのを中の仕組みから理解していくのがこのshowになっています。
ということで、ちょっと難しいshowだったんですけれども、一緒に読んでくれているリスナーの皆さん、Book Clubに参加してくれている参加者の皆さん、どうだったでしょうか。
個人的にもですね、一番最初にこのDatabase Internalsの本を手に取ったときに、第1章から第7章の中でとても難しかったなという記憶を覚えています。
何度か読み直してようやく少しずつ理解できるようになってきたなというぐらいですので、今回のshowもリスナーの皆さんと一緒に紐解いていこうと思います。
ではまず最初に本章の内容を簡単に紹介しようと思います。
本章は3段階構成になっていますね。
1つ目がバッファーマネジメントというところで、Databaseにおけるランダムアクセスメモリーを活用したキャッシュについて、この具体的な仕組みについて紹介されています。
それを踏まえた上でリカバリ2つ目ですね。
その3つ目がコンカーリシンコントロールという流れになっています。
ではまず1つ目のバッファーマネジメントのセクションから深掘っていこうと思います。
まずそもそもほとんどのデータベースマネジメントシステムというのは2つのレイヤーにデータを保存していると紹介されていました。
まず基本的にはディスクですね。
ファイルとしてデータ本体を保存していますと。
しかしそのディスクにファイル本体をデータブロック、ページと呼ばれるものを保存するだけではどうしてもパフォーマンスが上がらないので、
メモリに、ここで言っているメモリというのはRAMですね。
ランダムアクセスメモリを活用してここにそのデータブロックの単位でもあるページをキャッシュとして書き込もうというのがバッファーマネジメントの基本的には発想になっています。
なのでその書き込みがあるために毎回すべてのデータをディスクに書き込もうとするとディスクIOも発生してそこが遅くなってしまうので、
まずは一旦メモリにバッファーとして書き込んでおいて、どこかのタイミングでディスクに書き込もうというのが基本的な発想になっています。
バッファリングと言っても間違いはないと思います。
このどこかのタイミングでディスクに書き込むと言ったんですけれども、
このどこかのタイミングというのが結構ポイントですね。
雑に実装してしまうとメモリにテンポラインに置いてあるキャッシュがディスクにフラッシュされないタイミングでクラッシュしてしまうとそのデータが失われてしまうわけですよね。
これはそもそもデータベースの大前提であるクラッシュしてもデータを復元するというところが実現できません。
なのでまずはこのバッファーマネジメントがどういうふうに動くかを理解した上で、
そのリカバリにどうつなげていくかというのがこの第1セクションでバッファーマネジメントをやって、
次に第2セクションでリカバリをやる流れに相手しているのは、それが著者の意図じゃないかなと僕は思っています。
なのでこのセクションの目的としてはバッファーマネジメントについて理解しましょうというところかなと個人的には著者からのメッセージをそう受け取りました。
ここで本書でも書かれているんですけどもバッファーマネジメントにはいくつかの呼び方があって、
ページキャッシュと呼ぶこともあればバッファープールと呼ぶこともあると。
本書はどうやらバッファープール推しのようですね。
このブッククラブで話したんですけども実際Googleで検索してみるとページキャッシュって書いてある記事のほうが多いのでページキャッシュのほうがいいんじゃないみたいな意見もあったりして、
ここはどっちを使っても文脈によってコンテキストによって使い分ければいいのかなと思っています。
そもそもRAMにデータが書き込まれていない状態のことをダーティページとか言ったりするんですけども、
このキャッシュにあるメモリにあるバッファリングされたデータをどこかのタイミングでディスクに書き込まなきゃいけないんですね。
ここで3つ考えることがあります。
キャッシュをするときに考えなくてはいけないことなんですけども、
まず一つ目はですね、エビクション。
エビクションっていうのはキャッシュから立ちのかせるってことなんですけど、ディスクに書き込む。
キャッシュがいっぱいになったときに、いつ、どのタイミングで立ちのき宣言をするかっていうところですね。
例えばこれポストグレスだと、バックグラウンドフラッシュライターっていう非同期で動いてフラッシュさせる、
ここでいうフラッシュっていうのはメモリからディスクに書き込むことをフラッシュって言うんですけども、
バックグラウンドで動くプロセスがあったりしますと。
それを書き込むときには、ちょっと細かくはリカバリのセクションで紹介するんですけど、
デュラビリティを担保するためにチェックポイントプロセスとかワルログっていうのも一緒に書き込まなきゃいけません、
ということが紹介されていました。
キャッシュするときに考えなくてはいけないことの2つ目が、キャッシュのヒットレシをどうやって上げるかということですね。
キャッシュに置かれているページを、よく使われるものをメモリに置いておいたほうが、
ディスクアイを発生させて読み取りに行くよりも圧倒的にパフォーマンスが高いので、
キャッシュによく使われるページをメモリ上にロックする、もしくはピニングすると言ったりするんですけども、
それについても紹介されていました。
もしくはデータベースのインスタンスプロセスを起動させたときに、
そもそもよく使われることが想定できるならプリフェッチしておくのもありだよね、実際そういう実装もあるよねということも紹介されていましたね。
キャッシュをするときに考えなくてはいけないことの3つ目が、
キャッシュがいっぱいになったときに、1つ目がいつエビクトさせるか、いつ立ちのき宣言をするかですけれども、
この3つ目は誰をエビクトさせるか、これはキャッシングストラタジーと呼ばれることもあるんですけれども、
キャッシュがいっぱいになりましたと、実際に今メモリのキャッシュに入っているページの中で、
どいつを立ちのかせるかというところも1つアルゴリズムの剪定に関わっている箇所になっています。
本書では結構いくつか紹介されていましたね。
まずそもそもナイーブな実装としては、FIFOとかLRU、Fast In Fast Outとか、
Least Recently Usedというキャッシュストラタジーがあるけれども、
それだと実用段階にはないので、ポストグレでも使われているクロックと呼ばれるアルゴリズム、
それからLFU、Least Frequencyかな、LFはフリクエンシー、頻度ですね、よくアクセスされるという意味ですけれども、
よくアクセスされるキャッシュをキャッシュに残しておこうという考え方。
これらについて細かく紹介されていました。
クロックとLFUについても簡単に説明しようかなと思うんですけれども、
クロックというのは時計のクロックと同じなんですけれども、
Circular Bufferと呼ばれる、それこそビジュアライズした時に時計の針みたいな感じなんですけれども、
循環配列と呼ばれるものに、それぞれ時計の1時、2時、3時みたいなところに、
0と1のビットを保存しておくんですね。
論理上の概念です。
メモリ上は単純な配列にもちろんなっているんですけれども。
キャッシュがいっぱいになったときに誰をイビットさせるかを考えるときに、
まるで時計の針が時計周りに動いていくように、
ポインターを配列上時計周りに動かしていくんですね。
このときにページが使われているかどうかというバイナリーのフラグをオンオフしていくんですよ。
そこで空いているページを探すというアルゴリズムになっています。
これがクロックと呼ばれるポスクでも使われているキャッシュストラテジーになっていて、
ここは結構細かく紹介されていましたね。
2つ目がLFU。
具体的にはTinyLFUの紹介がありましたね。
このLRUと呼ばれるのがLeast Recently Usedなので、
一番最近に使われたですね。
一番直近に使われたページを残すというのがLRUで、
LFUというのはリセンシーじゃなくて、
フリクエンシー、よりよくアクセスされる、
特にキャッシュがいっぱいになったときに、
誰を立ち抜きさせるかというキャッシュストラテジーについて紹介されていました。
この第一セクションバッファーマネジメント4の時点で、
読者としては、データベースはパフォーマンスを上げるために、
書き込みのパフォーマンスを上げるために、
メモリ上にキャッシュを持っているんだということが分かりました。
一歩引いてみると、そのままデータベースがクラッシュしたときに、
データを復元する必要がありました。
ここでナイーブに実装すると矛盾が生まれてしまうわけです。
例えばメモリというのは起発性があるので、
例えば書き込みがたくさん起こっている状態で、
そのバッファープールの中にテンポラリに書き込まれたデータがあるとします。
そのデータがディスク上にまだフラッシュされていないとします。
例えば10件ぐらい書き込みの操作がメモリにあると想定しますね。
この時に、例えば理由は何でもいいんですけれども、
データセンターが火事になったとか、ちょっと無双ですね、
ちょっとプロセスがクラッシュしちゃったとか、
ネットワークの寸断とか理由でデータベースがクラッシュしてしまいました、
という時にこのメモリがリセットされてしまうわけですよね。
なのでここのメモリに入っているデータというのは消えてしまう。
なので単純にバッファープールを使いますというだけだと、
実はパフォーマンスは上がっているけど、
データベースのそもそもの大前提の一つであるデータがクラッシュするときに、
データを復旧してくれるという大前提が失われてしまうわけですよね。
じゃあデータベースってこのバッファープールみたいなパフォーマンスを上げる仕組みを使いながら、
どうやってリカバリを担保しているんだというのを説明しているのが、
この2つ目のセクションになっています。
めちゃくちゃ雑にというか一言でまとめると、
このセクションのゴールは悪を理解することかなと思っています。
悪、新しいキーワードが出てきましたね。
悪はWriteAheadLogということで、
アペンドオンリー、どんどん追記していくだけのディスクに書き込まれるログですね。
イメージとしてはどんどん書類がスタックしていくみたいな感じかな。
どんどん書き込みされていくアペンドログなことをWriteAheadLog、悪って言ったりします。
別名、コミットログと呼ばれることもあって、
MySQLとかPostgreでも使われています。
この悪ってのがバッファープールを使いつつ、
リカバリを担保する上で必要となってくる概念ですね。
なのでこのセクションを読んで、
とりあえず悪というものの存在と、悪が何をしているのか、
それやデータベースのドゥラビリティを担保していることだということが分かれば、
ゴールかなと思います。
バッファープールがEfficiency、効率性を実現する一方で、
悪というのがドゥラビリティを担保している。
この悪についても簡単に説明しようかなと思います。
ここでこの3つのコンポーネントをイメージしてください。
まず1つはディスクですね。
ここのディスクにデータ、ページ本体がどんどん書き込まれて、
ディスクに書き込まれればデータベースがクラッシュしても、
そこからデータが復元できますよね。
1つ目のセクションでバッファープールというのも紹介しました。
これがメモリにあるバッファーのコンポーネント、キャッシュですね。
ここの3つ目、ディスク上に、ページとは別の場所に悪というのを書き込んできます。
この悪というのは、まだフラッシュされてない書き込み操作を
Append Onlyでどんどんセクエンシャルに書き込んでいくということなんですね。
この悪というのはAppend Onlyでセクエンシャルに書き込んでいくので、
ハードディスクのようなセクエンシャルライトがそこそこ動くようなディスクを使っても、
プレイ上のパフォーマンスで書き込みができるんですね。
ディスクは遅いと言いましたけれども、
ディスク上にB3のランダムな書き込みが発生するようなときにはディスクはめちゃくちゃ遅いんです。
ランダムな書き込みが発生するから。
特にハードディスクみたいなセクエンシャルに動くものであれば。
だけどこの悪というのはディスクに書き込むんですけれども、
セクエンシャルに書いていくので、ディスク上でB3をメンテナンスするよりは
圧倒的にパフォーマンスが高いですと。
このドゥラビリティというのをバッファープールと悪の合わせ技で担保していきます。
まずバッファープールにはその一時的なキャッシュの情報が入っていると言いましたね。
その書き込みのオペレーションというのが悪にも書き込んできます。
一方的にどんどんどんどんアペン通りで。
そうするとこの、もし途中でクラッシュしてもですね。
悪に入っている書き込みのオペレーションからバッファープールのキャッシュの状態というのが復元できるんです。
クラッシュしてもその悪の中にバッファープールのテンポラリーなデータを復元できる情報があり、
ディスクのページ、B3の方にはデータ本体があるので、悪からバッファープールのステートを再現し、
そことディスクに入っているB3の実際のページを合わせることでデータをリカバリできるというこの一連の流れがまずは理解できればOKかなと思います。
実際にその悪をイメージしてみると、この悪というのは無限に伸びていくわけですよね。
どんどんどんどんアペン通りなので。
そうするとデータベースのプロセスが長生きすればするほど無限に伸びていって、
これはこれで非効率なので、チェックポイントと呼ばれるコンパクションっぽいことかな。
コンパクションというとちょっと語弊があるかな。
その悪が無限に伸びていくログなんですけど、このセーブポイントみたいなのが途中にある感じですね。
この時点までの悪はディスクにフラッシュしましたよ、ディスクに書き込みましたよというセーブポイントがあって、
それをチェックポイントの度にどんどんどんどんログの先頭に移動させていくみたいな感じですね。
なのでクラッシュした時にはチェックポイントから悪の先頭までのデータを使ってバッファーマネジメントにあるガッシュを復元すればいいということになります。
とりあえず第一回目としてデータベースインターナルズを読む人のゴールは、
この悪とバッファープールの合わせ技でドゥラビリティを担保しているということが理解できればいいかなと思います。
2回目以降に読む僕のような人なんですけど、この後半で結構重要な概念を紹介しました。
これはキーワードで言うとスティール・ノンスティール、あとはフォース・ノンフォース・ポリシーと呼ばれるものなんですけども、
これが結構重要な概念なのでここについても紹介しようと思います。
参加者の方の中にはChatGPT4のオムニオのイメージジェネレーションを使って、
ジブリ風にスティール・ノンスティール、フォース・ノンフォースを紹介させている画像を貼ってきてくれた方もいて結構面白かったですね。
このスティールとフォース・ポリシーというのは一旦抑えておくと、
ここら辺のそのドゥラビリティの話とかバフアッププールの話をするときに立ち戻れるかなと思います。
まず2つのキーワードですね。スティール・ノンスティールとフォース・ノンフォースというのがあります。
本書で書いているようなテキストブックライクな説明をここで言ってもちょっと伝わりづらいと思うので、
アナロジーで紹介しようかなと思います。
イメージとしてはGoogle Docsのようなオンラインドキュメントにミーティングにみんな参加していて、
そこに議事録を書いているようなイメージを、シチュエーションを想像してほしいかなと思います。
皆さんの会社でもGoogle Docsとかを使って議事録を書いていたりするかどうかわからないですけど、
最近だと結構Zoomにインタグレーションされた機能とかでAIに勝手にこのボイスをレコードさせて、
いい感じにサマリーを出してくれるみたいなプロトクトも出てきましたけど、
誰か1人かもしくは何人かが議事録を書いていくようなシチュエーションを仮定していこうと思います。
じゃあまずStealとNonStealからですね。
Stealっていうのは文字通り盗むっていうことなんですけれども、
Google Docsに共同編集、リアルタイムの編集機能がありますよね。
他の人が書いた書き込みっていうのは自分のブラウザ上にリアルタイムで反映されるんですけど、
共同編集でみんなで議事録を書いていくイメージです。
NonStealっていうのが議事録の人を1人だけ決めて、その人だけが書けるようにする。
議事録の担当者を1人制にする感じですね。
その人が疲れたらちょっと他の人に交換したりするかもしれないんですけども、
議事録に書き込みをするパソコンは必ず担当者は1人だけに決める。
これがNonStealのイメージかなと思います。
もうちょっと詳しく説明しますね。
StealありのGoogle Docsの共同編集の場合は、
みんなで同時に1つの議事録を編集するイメージなんですけども、
例えば誰かが、ボブが何かマネージャーが言ったことを書き加えると、
他の人のブラウザの画面にもほぼニアリリアルタイムで反映されていくと思います。
これでどんどんどんどん途中経過が共有されていって、
例えばボブが議事録を書いているんですけども、
それを聞いているアリスが、
マネージャーの言っていることを書きこぼしているなって気づいたら、
サポートするような感じで、
マネージャーの言った議事録を議事録に追加してあげるような感じで、
それを聞いた、またそれを見たボブが、
アリスがサポートしてくれている間に、
マネージャーの議事録のところにタイプを見つけたので、
それを全然別の人が直してあげるみたいな、そんな感じですね。
一方、スティールなし、ノンスティールの場合は議事録担当者が一人なので、
その人が自分の領域で議事録を書いています。
他の人は書き込み経験がないみたいな、そんな状況ですね。
このアナロジーにおいて、
Google Docs みたいな共有ドキュメント自体は、
データベースだと思ってください。
個々人の議事録者の作業というのが、
データベースを発行するトランザクションのイメージですね。
実際に議事録にキーボードをタイピングして書き込むというのが、
データベースの書き込みですね。
議事録担当者みたいなのは、トランザクションを一つずつ処理していく仕組みだと思ってください。
このスティール、ノンスティールと直交する概念として、
フォース、強制、フォースとノンフォースというのもあるので、
これも同じアナロジーで紹介していこうと思います。
このフォースというのが、トランザクションのコミットのことなんですけれども、
Google Docsの例で言うと、
自動保存プラス名刺的な完了があるみたいなイメージですね。
Google Docsに完了というボタンはないんですけれども、
ミーティングが終わりましたと。
それまではGoogle Docsが自動でオートセービングしてくれますけれども、
参加者全員がじゃあ、これではい、ミーティングは終わりです。
議事録担当者がこれで完了って宣言、もしくは合意をしたら、
その時点の内容が完全に記録として固定されるイメージです。
データベースの世界で言うと、トランザクションがコミットされた状態ですね。
その完了ボタンをないんですけれども、押したような状態というか、
Google Docsの議事録をExport to PDFに出力した状態で、
これが確定版として保存されるようなイメージです。
一方でそのノンフォース、フォースなしの状態はどういうことかというと、
明示的な完了のタイミングがないということですね。
Export to PDFでPDFにエクスポートして、他の人に共有する必要がなくて、
Google Docsの状態として常に残しておいて、
例えばじゃあミーティングが終わった時に、
Bobがそういえばミーティングの内容をサマリーにして送ってあげたら、
みんな助かるかもなと思って、
その議事録のトップのほうにTLDRみたいな感じでサマリーを書いてあげたりとか、
あとアリスが内容からアクションアイテムを導き出して担当者に送るみたいなのをするときに、
下のほうにアクションアイテムを書いてみたりみたいな、
ミーティングが終わっても、このタイミングで完了だよみたいな、
フォースのタイミングがなくて、常にデータベースの書き込みが発生しているようなイメージですね。
このスティールとフォース、それぞれありなしのパターンがあったんですけども、
これ直行する概念なので、これ合わせて4つのパターンが基本的には議論されます。
要するに、スティール、ノンスティール、フォース、スティール、フォース、ノンスティール、ノンフォース、スティール、ノンフォースのこの4パターンですね。
x軸、y軸にスティールありなし、フォースありなしをマッピングしたら、
この4つの次元が第1証言、第2証言、第3証言、第4証言がイメージできると思うんですけども、
その4つの組み合わせで考えることですね。
例えば、スティールあり、フォースなしみたいな状況はどういうことかというと、
スティールありなのでみんなでリアルタイムに記事録を共同編集して、
だけどフォースがないので完了時にPDFで保存みたいな感じで明確な区切りがないので、
ミーティングが終わっても参加者が常に戻って書き込みしていくようなことができるという状況ですね。
これのデータベースに戻った時にどういう問題があるかというと、
常に最新の状態を上書きしていくので、
例えばその参加者の1人が意地悪をしてというのもちょっとあれですけど、
自分にとって都合のいいように後から書き換えたりして、
最新の記事録というのが失われる可能性もありますね、このスティールあり、フォースなしだと。
じゃあフォースありにしてみましょうと。
スティールあり、フォースありにしてみるとこれはどういうことかというと、
みんなでリアルタイムにミーティング中は記事録を共同編集するんですけども、
ミーティングが終了した時点で記事録担当者がPDFにエクスポートして、
みんなに参加者にメールで送ると。
なのでみんなはトランスクションがコミットされた最後の状態が記録としてPDFとして完全に固定されるので、
合意を取りやすいということが、これがスティールありフォースなしの状況ですね。
そんなアナロジーを紹介しながら、スティールありなしとフォースありなしについて説明していきました。
ちょっと説明が長くなったのでステップバックすると、
今はですね、第1チャプター5のセクションの2つ目、リカバリーの後半で出てきた
スティール&フォースポリシーについて説明していました。
ということで第1セクションのバッファーマネジメントでキャッシュ、
2つ目のリカバリーでワルの話とスティール&フォースポリシーについてやってきました。
結構キーワード、データベース会話の難解なキーワードがバンバン出てくるので、
本書を読んだだけでも頭がいっぱいになってきちゃうので、
なるべくストーリー仕立てというかアナロジーで紹介しようかなと思います。
今回はまた別のアナロジーを使おうと思うんですけども、
図書館ってありますよね。地元の図書館でもいいですし、
シティの図書館でもいいんですけども、
図書館に行って本を貸し出しする。
それを予約するみたいなアナロジーで考えてみようと思います。
じゃあブッククラブをやるということが決まったので、
図書館にどうやらデータベースインターナルの本が1冊だけあるみたいだと。
なのでそれを図書館に行って借りようみたいな、そんなシチュエーションですね。
図書館に行って本を借りるためには図書館にトコトコと歩いていって、
借りたい本を手に取って、貸し出し係に言って、
これを借りますという典型的な普通の図書館だと思ってください。
でもですね、データベースインターナルというのがどうやら人気な本でですね、
実は自分以外にも借りたい人が5人、10人出てきたと。
もしかしたら皆さん論のテクトを聞いてくれているのかもしれません。
そんなことはないんですけども、そうだとして自分が借りたいと思った本がめちゃくちゃ人気だと。
そうすると誰か1人しか借りれないですよね。
トランザクションも同様で、自分が書き込みとかモディファイしようとするデータに対して、
自分が書き込んでいる間に他の人が書き込むブロックしないと不整合なデータができてしまうので、
本を借りるという状況をトランザクションがロックするみたいな状況だと、
思ってください。
この前提の上で、
ペシミスティックコンカルシンコントロールってどういうことっていうのを紹介しようと思います。
なぜ悲観的っていうかっていうのは、悲観的か楽観的かなんですけど、
主語はデータベースですね。
データベースの気持ちになったときに、いろんなトランザクションがバンバンやってくると。
こいつら絶対競合するだろって悲観的に考えるデータベースは、
ペシミスティックコンカルシンコントロールを実装しますね。
一方で、トランザクションいっぱい来るけど、こいつら多分いい感じにやってくるでしょ。
問題起こったら何とかするわみたいな、
そういう楽観的なデータベース君は、
オプティミスティックコンカルシンコントロールを実装します。
悲観的楽観的でいきなりエモーショナルな言葉が出てきたので、
誰のことってことなんですけど、データベースの気持ちになってみるとちょっとわかるかなと思います。
まずペシミスティックコンカルシンコントロールについて。
この図書館、このデータベースにはめちゃくちゃ悲観的な、
めちゃくちゃ悲観的なって言ったらあれかな、
ペシミスティックな貸し出し係、めちゃくちゃ厳しい人がいますと。
その人は自分の仕事をちゃんとこなして、
誰かに本を貸し出すときに、
あれ、これ私も実は予約してましたみたいな競合が起きないようにしたい、
仕事をすごいパーフェクトにこなしたい厳しい貸し出し係がいるとします。
なので、この貸し出し係の人は絶対に競合が、データの矛盾が起こらないようにしたいので、
誰かがデータベースインターナルズみたいな特定の人気のある本を手に取ったその瞬間に、
その本に何でもいいんだけど、
貸し出し中のラベルとかスティッカーとか分かりやすい札をつけるんですね。
このタグ、ラベル、札がついている間は、
他の人は絶対その本を借りることも予約することもできないと。
最初に借りたデータベースインターナルズという本を手に取った人が、
横目で他の人も実はこれ借りたかったんだけどなみたいな人がいても、
その人がラベルを返すまでひたすら待つしかないという感じですね。
なのでこれはデータの矛盾は絶対起こり得ないんですけども、
人気のある本というのはすぐに貸し出し中になってしまうので、
他の人は長い間待たされるのが容易に想像できるかなと思います。
この厳しいというかパーフェクショニズムの貸し出し係によって、
図書館の利用効率というのは悪くなるかもしれませんね。
これがペシミスティックコンカルシーコントロールです。
ちょっと別の例を2つ目のオプティミスティックコンカルシーコントロールと。
実はこの図書館には2人の貸し出し係がいましたと。
この厳しいシニアの貸し出し係の人とは別に新しく入ってきた、
オプティミスティックな楽観的な貸し出し係の人がいて、
この人はですね、みんなが自由に本棚から本を取り出して見ているのを、
机の上からコーヒーでも飲みながら、鼻歌でも歌いながら、
ただ眺めているだけなんですね。
データベースインターナルズ人気だなとか、この本は今人気なんだな、
みんなが手に取っている状況をただ見ているだけで、
実際に貸し出しの手続きをするときになって、
貸し出し係としての仕事をちょっとだけしますと、
あれ、この本、例えば君が予約したときから誰か借りていないかなみたいな最終チェックを、
図書館のデータベース、図書館のコンピューターでちょっと確認します。
もしすでに他の人が借りていたら、
このデータベースインターナル、実はちょっと予約が入っていたので、
その予約の人は30分後に来るみたいなんですよね。
なので予約リストにちょっと入っておいてくださいとか、
待ってくださいみたいなコミュニケーションをそこでするということですね。
これはそのデータベースインターナルを手に持って、
カウンターまで来た人からするとショックですよね。
借りれると思って行って、最後の借りれるというタイミングまで来たら、
実は予約が入っていたみたいな。
それだったらちゃんと最初からラベルを貼っておいてよみたいな、
期待しちゃうじゃんみたいな感じなんですけど、
この楽観的並行制御はですね、
最後の最後のヤバくなる一歩手前でちゃんと確認するというのがポイントなんですね。
ポイントとしては、悲観的並行制御の場合は、
データベースインターナルを借りようかなと思って手に取った瞬間に、
それがもう借りられないようにブロックしちゃうんですけど、
オプティミスティックコンカレーシングコントロールの場合は、
本当にコミットするタイミングですね。
ここでいうとその貸し出しの処理をデータベース上でするタイミングで、
本当にこの人が借りていいのかなというのを最終確認する。
はい。
なのでこの悲観的並行制御と違ってですね、
オプティミスティックの場合は、
この図書館の利用者のイメージとしては、
みんな結構自分のペースで本を借りたりとか、
手に取って気軽に見れるので、
図書館の使い勝手としては、
エクスペリエンスとしてはめちゃくちゃいいと思いますね。
で、その本がたくさん並んでて、
人気のある本というのが比較的少なくて、
人気のない本が多い場合には特に効果的ですよね。
で、貸し出し係の仕事も大幅に減るので、
矛盾が発生しない範囲では借りたい人が勝手に借りていくみたいな、
結果ハッピーみたいな形になります。
ただ、最後の最後でチェックしないと、
それは強豪が起こってしまうことになるので、
全く仕事をしないというわけではないですね。
なのでコミットする瞬間までに、
データベースの気持ちとして、
楽観的に構えているか、
悲観的に予防的に動くか、
みたいな違いかなと思います。