RDBMSの基本とNoSQL その2 第05回 16年12月 / 最終更新:2016.12.21

みなさん、こんにちは。エンジニアの古庄道明です。
前回に引き続き、RDBを含む、database周りについて少しお話しをさせていただければ、と思います。
今回は、前回少しだけお話を出させていただいていたもののうち「トランザクションの重要性」を中心にお話をさせていただければ、と思います。

まず「CAP定理」と「ACID特性」について簡単に説明をして、RDBのメリットとその具体例について言及していきましょう。

CAP定理は、簡単に書くと「以下の3つの要素をすべて満たす"共通のデータを持つ、ネットワークによってつながったシステム"は存在しない(ので、通常、いずれか2つを選び、1つを捨てる)」というものです。
要素は、以下の3つになります。

・一貫性(Consistency)
・可用性(Availability)
・分断耐性 (Partition tolerance)

上述ですが、一貫性は「どのサーバと通信をしても同じ情報(あるいは同じエラー)を受け取る事ができる」、可用性は「常に動いている。単一障害点が存在しない」、分断耐性は「ネットワーク(やサーバ)が分断して複数あっても問題なく動作できる」事をそれぞれ意味します。
厳密に書きますと「CAP定理の3つの要素の、3つのうち2つしか選べない」は些か極論で、ある程度「度合や程度がある」のですが、お話を簡略化するために一端「0%または100%」という前提でお話を進めていきます。
RDBは「一貫性+可用性」を持つdatabaseである、と言われています。

また、上述の3要素のうち「一貫性」が、トランザクションと密接な関係を持っています。
トランザクション自体は「ACID特性」が、持つべき性質として知られています。

・原子性(Atomicity)
・一貫性(Consistency)
・独立性(Isolation)
・永続性(Durability)

上述ですが、原子性は「トランザクションに含まれる処理は、"全て実行される"または"全て実行されない"のどちらかであることを保証する」、一貫性はこちらでは「トランザクション前後でデータの整合性が保たれ、矛盾がない事を保証する」、独立性は「あるトランザクションAは、別のトランザクションBに影響されず、独立して動くことを保証する」、永続性は「トランザクションが終了したら、データは永続となり、結果が失われない事を保証する」機能になります。
簡単にまとめると「ひとまとめの処理をつつがなく行う(か全部キャンセルする)ことで、データへの矛盾が起きない事を保証する」機能になります。

これは、ゲームの場合(に限りませんが)、特に「お金に絡む」あたりの処理で重要になります。
実例を出して考えてみましょう。例題として「課金することでカードが1枚引ける、有料課金がちゃ」を想定します。

データベース的には
1)ユーザの「課金テーブル」に、必要な金額があることを確認する
2)ユーザの「課金テーブル」から、必要な金額を減算する
3)ユーザの「所持カードテーブル」に、引いたカードを追加(insert)する
という処理になります。

この一連の処理が「すべてつつがなく行われている」ぶんには特に問題が起きないのですが、「部分的にエラー」が発生すると、困った事がおきます。

ケース1
もし「2番の減額は正常終了したけど、3番のカード追加が失敗した」場合、ユーザとしては大変な不利益になるので、クレームの対象になります。

ケース2
もし「2番の減額が失敗、3番のカード追加が成功した」場合、ユーザはおそらく(気づいても)連絡をしていただけない可能性が想起されますが、ビジネス的には(連続すれば、大きな)痛手となります。

ケース3
些か複雑な例ですが。
「1ユーザがタイミングを見計らって同時にアクセスをしてきた」場合、1番の「必要金額の確認」のロジックで問題が起きる場合があります。
複雑なケースなので、細かく記述してみましょう。
前提として「ユーザの所持金は100」「有料課金がちゃの金額も100」とします。

端末A操作 端末B操作
「がちゃを引く」アクセス
「がちゃを引く」アクセス
1)の金額確認→100なのでOK
1)の金額確認→100なのでOK
2)の減算→100から0に
2)の減算→100から0に、或いは「0から-100」で、カラム型によっては「負の値が入らない」ので0になる、またはエラー発生
3)のカード追加
3)のカード追加

このような流れから、金額減算が「0を2回」にせよ「負の値が入らない」にせよ、いずれにしても「1回の減算で2回がちゃが引ける」可能性が、高くなります。

こういった様々な問題が、トランザクションであれば、ガードすることができます。
ケース1および2は「原子性」の特性上「トランザクションに含まれる処理は、"全て実行される"または"全て実行されない"のどちらかであることを保証する」ので、そもそも発生しえない事象となります。
ケース3は、(SQLを正しく発行する前提ですが)「一貫性」および「独立性」によって、ガードをする事が可能になります。

上述のような「有料課金がちゃ」以外にも
・有料アイテムその他の購入
・(有料)アイテムの使用
・アイテムの(ユーザ同士での)交換や譲渡
などでも、トランザクションを適切に実装すれば問題が発生しない事象は、多々あります。
このように、RDBは「トランザクションの仕組みがあるので、比較的簡単にそれを使うことが出来る」のが大きな利点になります。

ただ一方で。
SQL文は非常に「柔軟」なコードが書ける分、どうしても「処理が重たくなる」傾向があります。
また一方で、特にWeb通信を伴うゲームにおいては「単純な参照(select)」や「大量データのinsert」など「それほど高機能は必要ない代わりに、大量の依頼の処理能力や、高速な速度が求められる」シーンがあるのも、一つの事実です。

そうして、その流れとニーズから、いわゆる「NoSQL」と呼ばれる製品が出てきました。

次回は「RDBでは満たしにくかったニーズを満たすべく作られた」NoSQLと呼ばれるものたち、について書いていきたいと思います。