Transactions
- 企業アプリケーションにおける、並列性を取り回す主要な道具
-
“transaction”という言葉
-
金銭やモノの交換を想起させる言葉
-
ATM
- PIN入力する
- 現金引き出す
- Golden Gate Bridgeで通行料払う
- 酒場でビール買う
-
-
-
定義づけ
-
いくつかの一連の仕事からなる
- 始まりと終わりが適切に定義されている
- ATMの例
-
始点
- カード挿入する
-
終点
- 現金出てくる
- 預金残高不足で取引中断する
-
最初と最後で状態が一貫している = 変な状態にならない
- お金が減っていないのに商品だけ手に入ったりしたらまずい
- 全成功or何もしない
-
ACID
-
Atomicity
-
トランザクションで囲まれた一連の各ステップは、全成功か巻き戻されるか
- commit
- rollback
-
-
Consistency
- トランザクションの開始・完了時で一貫した状態であること
-
Isolation
- 未完了のトランザクション経過は他のトランザクションから見えないこと
-
Durability
-
トランザクションの結果は永続化されること
- いかな種類のクラッシュがおきても生き残る
-
Transactonal Resources
-
DBをはじめとするあらゆるものがtransactionalたりえる
- メッセージキュー
- プリンタ
- ATM
-
トランザクションはなるべく短く
- スループットをなるべく損ねないよう
-
long transaction
- 複数リクエストをまたぐ(システム)トランザクション
- よくない
-
request transaction
- 1リクエスト発行〜完了を1トランザクションでくるむ
- シンプル
-
多くのプラットフォームで宣言的に実施できる
-
【補】AOP: アスペクト指向プログラミング
- Laravel Aspectでは、
/** @transactional */
アノテーションを付与することで
メソッドをトランザクションで包める
- Laravel Aspectでは、
-
-
late transaction
-
トランザクションの開始をなるべく遅らせることで、トランザクションを短くする
- トランザクション開始までの部分はtransactionalじゃなくなる
-
ので、並列制御に与ることができなくなる
- inconsistent readのもと
- どうしても必要、とならなければやる価値は薄い
- とはいえ、ビジネストランザクションはlong transactionなので、やらざるを得まい
-
-
トランザクションの最中、何がロックされているのかを認識せよ
-
多くのRDBMSでは、関与する行がロックされる
- 複数のトランザクションが同一の表にアクセスできるよう
-
lock escalation
- 同時にロックできる限度を超えてしまうと、表全体がロックされてしまう
- 並列性に深刻な影響を与える
-
ゆえに、Layer Supertypeパターンをドメイン層に適用する場合、 基底クラスに対応する表をつくってはいけない
-
Layer Supertype
- A type that acts as the supertype for all types in its layer
- 【補】あらゆるドメインオブジェクトの永続化が同一の表の行をロックし、ロックがエスカレーションしてしまう
-
-
Reducing Transaction Isolation for Liveness
- Transaction Isolation LevelとInconsistent Read
Isolation Level | Dirty Read | Unrepeatable Read | Phantom |
---|---|---|---|
Read Uncommitted | Yes | Yes | Yes |
Read Committed | No | Yes | Yes |
Repeatable Read | No | No | Yes |
Serializable | No | No | No |
-
Serializable
- トランザクションが完全に独立している
- 並列して実施しても、順次実行した場合と同じ結果を得ることができる
step | package A | package B | A+B |
---|---|---|---|
0. init | 7 | 5 | 12 |
1. Martin Begins Tr | |||
2. Martin Reads A | 7 | ||
3. David Commits A+2,B+3 | 9 | 8 | 17 |
4. Martin Reads B | 5 | ||
5. Martin Commits | |||
6. Martin Calcs A+B | 7 | 5 | 12 |
step | package A | package B | A+B |
---|---|---|---|
0. init | 7 | 5 | 12 |
1. Martin Begins Tr | |||
2. Martin Reads A | 7 | ||
3. Martin Reads B | 5 | ||
4. Martin Commits | |||
5. Martin Calcs A+B | 7 | 5 | 12 |
6. David Commits A+2,B+3 | 9 | 8 | 17 |
-
Repeatable Reads
- Phantomを許容する
- ReaderのReadがトランザクションでないとおきる
step | package A | package B | A+B |
---|---|---|---|
0. init | 7 | 5 | 12 |
1. Martin Begins Tr | |||
2. Martin Reads A | 7 | ||
3. David Commits A+2,B+3 | 9 | 8 | 17 |
4. Martin Reads B | 8 | ||
5. Martin Commits | |||
6. Martin Calcs A+B | 7 | 8 | 15(inconsistent) |
-
Read Committed
-
Unrepeatable Readsを許容する
- package Aの「7クラス」という値を再度得られなくなることを許容
-
step | package A | package B | A+B |
---|---|---|---|
0. init | 7 | 5 | 12 |
1. Martin Begins Tr | |||
2. Martin Reads A | 7 | ||
3. David Commits A+2,B+3 | 9 | 8 | 17 |
4. Martin Reads B | 8 | ||
5. Martin Rereads A/B | 9(7->9) | 8 | 17 |
6. Martin Commits |
-
Read Uncommited
- Dirty Readsを許容する
-
他のトランザクションの未コミットの変更が見える
- まだないはずのファイル見える
- 見えていたファイル消える
step | package A | package B | A+B |
---|---|---|---|
0. init | 7 | 5 | 12 |
1. Martin Begins Tr | |||
2. David Begins Tr | |||
3. David adds A+1 | 8 | ||
4. Martin Reads A | 8 | ||
5. David adds A+1,B+3 | 9 | 8 | 17 |
6. David Commits | 9 | 8 | 17 |
7. Martin Reads B | 8 | ||
8. Martin Commits | |||
9. Martin Calcs A+B | 8 | 8 | 16(inconsistent) |
step | package A | package B | A+B |
---|---|---|---|
0. init | 7 | 5 | 12 |
1. Martin Begins Tr | |||
2. David Begins Tr | |||
3. David adds A+1 | 8 | ||
4. Martin Reads A | 8 | ||
6. David Rolls Back | 7 | 5 | 12 |
7. Martin Reads B | 5 | ||
8. Martin Commits | |||
9. Martin Calcs A+B | 8 | 5 | 13(inconsistent) |
- 読み取り一貫性を確保できるのはSerializableのみ
- スループットのために何かを許容して妥協する
- すべての場所で同じ分離レベルを採用する必要はない
Business and System Transactions
-
ここまでDBについて「トランザクション」と呼んできたのはシステムトランザクション
-
SQLコマンド
- 開始
- 終了
-
ありふれており、ありがたみの薄れたもの
- 枯れている
- よく理解されている
-
- システムトランザクションは、システム利用者にとって意味のあるものではない
-
ビジネストランザクション
- システムの利用者にとって意味のあるトランザクション
-
システムトランザクションよりも大きい単位
- ログインする
- 口座選択する
- 支払い額設定する
- OK押下する
- 支払いが実行される
-
システムトランザクションと同じく、ACID特性を有する
- OKを押下して初めてすべての状態が永続化される
- 押下しなければ何も起きない
-
自明な解法: 一連の処理群をシステムトランザクションでくるむ
- たいていのビジネストランザクションは複数リクエストからなる
- 1つのシステムトランザクションでくるむとlong transactionになる
- 効率性低い(スループット低い)
- long transactionで問題ないならこれが一番いい
- が、long transactionをshort transactionsにするリファクタリングは複雑で理解困難
-
なので多くの企業アプリケーションでは、long transactionのリスクは取らない
- 【補】長い長い運用があるから、そのうちスループットが問題になるかもしれない
-
ビジネストランザクションを、一連の短い(システム)トランザクション群に分割する
-
offline concurrency
- 自前でACID特性を担保しなければならない
-
ここにおいてもシステムトランザクションはいまだ関連深い
- ビジネストランザクションがtransactional resourceと相互作用するかぎり、その相互作用はシステムトランザクション内で実行することになる
- 一連のシステムトランザクションを紐でつなぎ合わせるだけでは不十分
- のり付けが必要
-
- AtomicityとDurabilityはビジネストランザクションでも簡単に実現できる ..
-
Consistencyは潜在的にやっかい
-
変更のセットが正しいこと、それをビジネストランザクションにわたって維持すること
- 【補】借方と貸方がバランスしなかったりするのは駄目
- Domain Modelを採用しているならば、Unit of Workにより変更を正しく追跡できる
-
Transaction Scriptを採用しているならば、自前で変更を追跡する必要がある
- が、Transaction Scriptを採用しているということはドメインモデルがシンプルなはずなので、おそらく問題にはならない
-
-
Isolationはしんどい
- Isolation Levelについて前述したように、Isolationが崩れるとConsistencyも崩れる
-
あるセッションが他のセッションを踏み越えないことの保証が必要になる
- 他のユーザの変更を上書きしてしまわないよう
-
読み取り一貫性がないのもまずい
- 複数システムをまたぐ場合、各システムから取得したデータそれぞれが一貫性があっても、
データ群として一貫性の保証がない
- 複数システムをまたぐ場合、各システムから取得したデータそれぞれが一貫性があっても、
-
ビジネストランザクションとセッションとは密接に関わる
- ユーザ始点では、1つのセッションは複数のビジネストランザクションからなる
- 「複数のセッションが1つのビジネストランザクションを構成する」ケースも考えられるが混乱の元なので避ける