33. 水平分割された DB への問い合わせ
DatabaseAccessMultiNode クラスにより実現
複数 DB に同じ SQL を投げ、結果をマージ
使用者は複数 DB への問い合わせである事を意識
せず、単一 DB を扱うのと同様に記述する事がで
きる
// 単一 DB への問い合わせ
$dba->user($user_id)->select('user_tbl');
$dba->user($user_id)->update('user_tbl');
// 複数 DB への問い合わせ
$dba->user_multi($user_id_arr)->select('user_tbl');
$dba->user_multi($user_id_arr)->update('user_tbl');
34. 水平分割された DB への問い合わせ
__call と call_user_func_array によって実装
DatabaseAccessNode クラスに単一 DB へ問い合
わせる処理を追加すると、
DatabaseAccessMultiNode クラスを経由して複
数 DB に問い合わせもする事ができる
class DatabaseAccessMultiNode
{
function __call($func_name, $args = array())
{
// 各 DB に対して実行
foreach ($this->dban_arr as $key => $dban) {
$tmp_data_arr[] =
call_user_func_array(array($dban, $func_name), $args);
}
36. 水平分割された DB への問い合わせ
user_id をキーにした場合
UPDATE は対象レコードが存在しないため問題無いが
User1
(1000-1999)
User2
(2000-2999)
User3
(3000-3999)
UPDATE t SET a = a + 100 WHERE
user_id IN (1000, 2000, 3000);
UPDATE t SET a = a + 100 WHERE
user_id IN (1000, 2000, 3000);
UPDATE t SET a = a + 100 WHERE
user_id IN (1000, 2000, 3000);
37. 水平分割された DB への問い合わせ
INSERT は気をつける必要がある
User1
(1000-1999)
User2
(2000-2999)
User3
(3000-3999)
INSERT INTO t (user_id, a) VALUES
(1000, 0), (2000, 0), (3000, 0);
INSERT INTO t (user_id, a) VALUES
(1000, 0), (2000, 0), (3000, 0);
INSERT INTO t (user_id, a) VALUES
(1000, 0), (2000, 0), (3000, 0);
38. 水平分割された DB への問い合わせ
INSERT は気をつける必要がある
User1
(1000-1999)
User2
(2000-2999)
User3
(3000-3999)
INSERT INTO t (user_id, a) VALUES
(1000, 0), (2000, 0), (3000, 0);
INSERT INTO t (user_id, a) VALUES
(1000, 0), (2000, 0), (3000, 0);
INSERT INTO t (user_id, a) VALUES
(1000, 0), (2000, 0), (3000, 0);
不要なレコードまで INSERT されてしまう
39. 水平分割された DB への問い合わせ
__call による実装は、あくまでも全 DB に同じ
クエリを投げているだけ
レスポンスも型によって機械的に対応している
INSERT のようにそれでは問題がある場合は、
専用メソッドを立てて対応する
40. トランザクション
トランザクションは DB 単位でかかるため
管理に気を使わなくてはいけない
// 対象のユーザ DB にトランザクション開始
$dba->beginTransactionToUser($user_id);
// この更新はグローバル DB への更新のため
// トランザクションの対象とならない
$dba->gb()->update();
XA トランザクション…
うっ…頭が…
( 分離レベルが SERIALIZABLE に限られる、
挙動が怪しいという事で、ミドルウェアに頼らず
アプリによる実装としました )
41. トランザクションの開始
複数 DB へのトランザクション開始方法は 2 通り
・最初にまとめて開始してしまう
・必要になった時点で開始する
トランザクションが必要な事がわかりきっている場合は最
初にまとめてしまう方が管理が楽かつ簡単
どれか 1 つでもトランザクションがかかっていれば他の DB
も更新処理時に自動でトランザクション状態となるオート
モードも用意したが、管理できなくなる懸念があったため
使っていない
43. トランザクションの開始
■ 必要になった時点で開始する場合
ある DB に対してトランザクションをかけるが、
ある DB に対しては 10 回に 1 回くらいしかトランザクショ
ンが必要が無い処理の場合、必要になった時にトランザク
ションを開始する
別々のユーザ ID が対象になった際に両方のユーザ ID が同
じ DB に所属している場合など、既にトランザクションが開
始されている事もある
// 対象のユーザ DB にトランザクション開始
$dba->beginTransactionToUser($user_id);
// たまにしかここに来ないので、ここでトランザクション開始
// 既にトランザクション開始されているならスルーする
If ($dba->user($other_user_id)->isTransaction()) {
// 同じ DB の場合はここにはこない
$dba->beginTransactionToUser($other_user_id);
}
44. コミット
各 DB に対してバラバラのタイミングでコミットを行うとつ
くりが複雑になり、データ不整合となるバグを引き起こす
可能性が高くなる
悪い例
// グローバル DB をアップデート
$dba->gb()->update();
// グローバル DB をコミット
$dba->commitToGlobal();
// ユーザ DB をアップデート
$dba->user($user_id)->update();
// ユーザ DB をコミット
$dba->commitToUser($user_id);
ここでエラーが起こるとユーザ DB のみ更新されず
データ不整合状態となる
Global User
UPDATE
COMMIT
UPDATE
COMMIT