を訳して理解する

というわけで訳してみました。

Master Detail Transactions in MongoDB

RDBにおいて、トランザクションはデータのアトミックな更新を可能にしています。関係スキーマは高度に正規化されているために、ほとんどの論理的なトランザクションスパンは複数のテーブルにまたがります。それゆえ、複数の更新をアトミック(すべてか全部ダメか)に行えることが重要になっています。

MongoDBは複数ドキュメントのトランザクションを行うことができませんが、ドキュメント指向のデータモデルを通して多くのユースケースで埋め合わせをしています。このポストでは、Master-Detailデザインパターンについて語ります。
これは、RDBMSにおけるマルチステートメントトランザクションを常に要求するようなデータモデルでしばしば見られ、しかし、MongoDBにおいてはクロスステートメントなトランザクションなしに容易に操作ができます。

Master-Detail transactions in an RDBMS

Master-Detail パターンの例として、複数のラインアイテムをも購買注文システムを考えましょう。RDBMSでは、Purchase Orderテーブル(Master)とLine Itemテーブル(Detail)としてモデル化されます。購買注文を受けためには、購買注文における情報全てを取得するために、Purchase OrderとLine ItemテーブルをJOINしなければなりません。

RDBMSではこんな感じです

CREATE TABLE purchase_orders ( 
	id INT NOT NULL,
	title VARCHAR(100),
	total DECIMAL(10,2)
);

CREATE TABLE line_items (
	id INT NOT NULL,
	sku VARCHAR(100),
	quantity INT,
	price DECIMAL(10,2),
	purchase_order_id INT,
	Foreign Key (purchase_order_id) references purchase_orders(id)
);

https://gist.github.com/1068265#file_po_schema.sql

もしアトミックにpurchase orderとline itemを更新したければ、マルチステートメントトランザクションが必要になります。例えば、購買注文をつくろうとすると、下記のようなステップになります。

START TRANSACTION;

/* Create a purchase order row */
INSERT INTO purchase_orders (id,title,total) VALUES (1, ‘purchase order 1’, 10.50);

/* Create a line item, including the foreign key of the purchase order we just created */
INSERT INTO line_items(id,sku,quantity,price,purchase_order_id) VALUES (2, ‘a’, 1, 10.50 1);

COMMIT;

Gists for transactions post — Gist

この更新で、Purchase Orderが存在し、しかし、それがLine Itemを持っていないことはありません。全体のオブジェクトとその詳細はひとつのトランザクションでコミットされます。

Purchase Orderを更新する必要、例えば、他のLine itemを追加する、がある場合、次のようなトランザクションを実行します。

START TRANSACTION;

/* Add some new line item to the PO */
INSERT INTO line_items(id,sku,quantity,purchase_order_id) VALUES (3, ‘b’, 1, 12.34, 1);
INSERT INTO line_items(id,sku,quantity,purchase_order_id) VALUES (4, ‘c’, 1, 15.25, 1);

/* Update the “total” field of the purchase order to reflect the added line items */ 
UPDATE purchase_orders SET total = (total + 12.34 + 15.25) WHERE id = 1;

COMMIT;

https://gist.github.com/1068265#file_update_po.sql

この場合、2つの新しいLine Itemが同時に現れる(もしくは両方無い)こと、そして、Purchase Orderのフィールドの全体が同時に更新されることを保証します。いずれかひとつのLine Itemが存在して、ほかは無い、という状態がみられてしまうクライアントはありません。(※訳注:簡略しました)

Master-Detail in MongoDB

MongoDBでの動作は少し異なっています。マルチドキュメントトランザクションを実行する機能を持ち合わせていません。しかし、このMaster-Detailのパターンは、MongoDBのリッチなデータモデルのおかげで、マルチステートメントトランザクションなしに操作可能です。

MongDBはデータをネストされたオブジェクト、配列その他のリッチなデータタイプを含むドキュメントとして保持します。そのため、Master-Detailのエンティティを複数のコレクションまたは二つ以上のドキュメントで保持する必要がありません。MongoDBではそのようなオブジェクトの一般的なモデル化では、ネストされたドキュメントを利用します。Purchase Orderモデルは次のようになります

var purchase_order = { 
  _id: 1
  title: ‘Purchase order 1’,
  total: 10.50,
  line_items: [ 
    { sku: ‘a’, quantity: 1, price: 10.50 }
  ]
} 
[https://gist.github.com/1068265#file_po_document.js]

どのようにして、MongoDBではマルチステートメントトランザクションなしで同じレベルのアトミック性を得ているかを見てみましょう!

まず、新しい購買注文と最初のLine itemをアトミックにつくることが可能なことが期待されます。

>|javascript|
db.purchase_orders.save( purchase_order )

https://gist.github.com/1068265#file_po_insert.js

これにより、購買注文と初期アイテムの生成をひとつのオペレーションをアトミックに実行します。
SQLシナリオのように、クライアントは購買注文がからの状況を見ることがありません。これはシングルステップですべて成功するからです。

この購買注文の変更についてはどうでしょう?もし、この購買注文に対して追加したい場合、このようにします。

db.purchase_orders.update( { _id: 1234 }, { 
  $pushAll: { line_items: [
      { sku: ‘c’, quantity: 1, price: 12.34 },
      { sku: ‘d’, quantity: 1, price: 15.25 } 
    ],
  $inc: { total: 27.59 } }
});

https://gist.github.com/1068265#file_po_update.js

$pushAllオペレーションはアトミックに値を配列要素に追加します。RDBMSシナリオで見たように、この更新はアトミックで全体のコマンドが成功するか失敗するかです。一方で$incオペレーションはアトミックに購買の「total」フィールドをインクリ目とします。これらすべての更新はグループとして、アトミックに起こり、これらは成功するか失敗するかであり、他のクライアントから注文の状況で一貫性がない状態を観察することはできません。

まとめ

MongoDBのリッチなネストされたドキュメントとしてオブジェクトをモデル化することで、RDBMSでのMaster-Detailパターンで得られるものと同一レベルのコンシステンシーを複数テーブルのJOINではなく得ることができた。MongoDBのアトミックな更新オペレーションを組み合わせることができ、RDBMSでのマルチステートメントトランザクションで伝統的に実施してきた多くのケースを解決できます。

RDBMSでがこれらのシナリオでマルチステートメントトランザクションを必要としているのは、これらのタイプのオブジェクトを複数のテーブルで持つしか無いからです。対照的に、MongoDBではシングルステートメントトランザクションがはるかに適しているのは、シンプルな更新でジョインを必要としないためです。

これは、マルチステートメントトランザクションが不便だということではありません。もし、エンティティをまたぐトランザクションが必要な場合(line itemをある購買注文から別のに移す場合など)や購買注文を変更し、一度にオブジェクトのリストを作ることを1つのステップでやる必要がある場合には、マルチステートメントトランザクションが必要になるでしょう。または、それ以外の代替となるアプローチを取らざるを得ません。

しかし、伝統的なマルチステートメントトランザクションが必要となる多くのケースに、オブジェクトをドキュメントとしてモデル化し、アトミックにこれらのドキュメントを更新することで対処できることがわかりました。

トランザクションのその他の面やACIDは別問題です。MongoDBは一般的なスナップショットをサポートしていません。ここではそれは議論していません。おそらく、今後の別のブログポストではいいトピックになることでしょう。


(※ タイトルだけ見てwktkしながら、訳しながら読んだらふつうのコトだったので凹みました