トランザクションとは、ひとまとまりの処理(処理単位)のこと。
|
トランザクションは、視点(観点)の違いによっていくつかに分類できると思う。
勝手な用語 | 勝手な取り決め |
---|---|
業務トランザクション | 業務(アプリケーション)レベルの一連の処理。 OLTP(オンライントランザクション処理)とかいうものを想定。 |
システムトランザクション | DB以外でシステム的に整合性をとるべき一連の処理。 JMSのトランザクションとか。 |
DBトランザクション | データベース(テーブル)更新レベルの一連の処理。 DBMSが備える仕組みを想定。 |
いずれにしても、そのレベル(範囲)において、整合性がとれていなければならない一連(ひとかたまり)の処理を意味する。
レベルの違いを除けば、欲しい機能(トランザクションに求めれられる事)は同じ。
すなわち、一連の処理中に同じデータに対して他の処理が更新をかけることが無いようにしたい。
一連の処理が途中で中断された場合、半端な状態になるのは困るので、何らかの対処が欲しい(トランザクション処理開始前の状態に戻るとか)。
当ページで扱いたいのは、データベースが持つ仕組みとしてのトランザクション。
特に、それを使ってどうプログラミングするか・その為にどういう設計が必要か、という考察。
リレーショナルデータベース(RDB)においてデータ(テーブル)を更新するには、トランザクションを使用する。
DBアクセスする為には、クライアントからDBへ接続(connect)する。コネクションを取得・獲得する、あるいはコネクションを張ると言う。
トランザクションは、その接続(コネクション)内で有効になる。
同一クライアントが複数のコネクションを取得すると、(同一クライアントであっても)別々のトランザクションとして扱われることになる。
1つのコネクション内で複数回のトランザクション処理(更新処理)を行うことも出来る。
トランザクションを使用するには、まず、トランザクションを開始する。(BEGIN TRANSACTION)
大抵のRDBMSでは、DBへ接続した際に暗黙に始まっている。また、コミットまたはロールバックが行われると自動的に次のトランザクションが開始される。
途中の状態まで戻す事を可能とする、セーブポイントの機能を持つRDBMSもある。(使ったことないけどねー)
そしてテーブル操作(INSERTやUPDATE・DELETE)を行う。
ここで更新された内容は、他のトランザクションからはまだ見ることが出来ない。
データを取得してその内容に対して変更を加えたい場合、その間に他のトランザクションが同一データを更新してしまうと、全体(業務レベル)の整合性がとれなくなってしまう。そういう場合は(SELECT
FOR UPDATEによって)ロックをかけて、他のトランザクションがそのデータを(更新用には)取得できないようにしておくのが一般的。
(UPDATE文を実行した時点で、自動的にロックされるものもある)
最後にコミット(COMMIT)またはロールバック(ROLLBACK)を行う。
コミットすれば更新した内容が全て反映され、他のトランザクションから見ることが出来るようになる。
ロールバックすると、更新しようとした内容は全て破棄され、トランザクション開始前の状態に戻る。
こういったトランザクションの機能は厳密にはどういう事なのかを示した言葉が、ACID特性。
RDBにおけるトランザクションが満たすべき機能を表しているのが、ACID特性。
エイシッドと読むのかアシッドと読んでいいのかは、知らない^^;
機能 | 概要 | 実現方法 | |
---|---|---|---|
Atomicity | 原子性 | トランザクション内の一連の変更処理が、行われるか行われないかのどちらかしか無いこと。 全更新が反映(コミット)されるか、全て元の状態に戻る(ロールバック)か。 1レコード内の複数項目の更新、あるいは同テーブル内の複数レコードの更新、あるいは別々のテーブルの更新。それら全てが対象。 |
トランザクション |
Consistency | 一貫性 整合性 |
DBが正しい状態に保たれること・矛盾が無いこと。 整合性制約を満たすことや、同時更新されてもそれぞれの処理が正しく行われることを示すと思われる。 |
整合性制約 排他制御 |
Isolation | 独立性 隔離性 分離性 |
他のトランザクションの影響を受けないこと。(トランザクション処理途中の変更内容が、他から見られないこと。) 他の処理からは、自分のトランザクションの処理前か処理完了後のどちらかの状態しか参照できない。 自分の処理からは、他のトランザクションの処理途中の状態を参照することは無い・出来ない。 |
トランザクション |
Durability | 永続性 持続性 |
トランザクションが確定したら、それがずっと続くこと(元に戻らないこと)。 耐障害性(障害回復)という意味合いらしい。 |
障害回復 |
ACID特性は「(RDBの)トランザクションが満たすべき機能」とのことらしいが、トランザクションにとどまらないレベルを含んでいるような気がする。(理解不足?)
ちなみにアシッドクラウドと言えば酸の雲という呪文、メタルギアアシッドはゲーム名。
原子性が保証されていなければ、どうなるか。
テーブル1とテーブル2を更新しようとしたが テーブル1の更新後に処理が異常終了した場合、テーブル1だけ更新された状態のまま(不整合)になってしまう。
トランザクションがコミットまたはロールバックによって終了し、必ず更新後か更新前のどちらかの状態になることを考えれば、原子性はまさにトランザクションが備える機能だと思う。
独立性が無い、すなわちトランザクション処理中の内容が他のトランザクションから見えてしまうと、中間状態に対して処理をしてしまう可能性がある。
例えばトランザクション1(t1)がaに100足してから200足す処理を行い、トランザクション2(t2)がbにaの値を入れる処理を行うとする。
aの初期値が0でaに100足された状態でt2が動くと、bは100が入る。
原子性の観点から、aは初期値の0か最終状態の300のどちらかになるべきであり、bはaの値を入れるので、0か300のどちらかになるべきなのに、違う状態になってしまう。
独立性(他のトランザクションの値はコミットされるまで見えない)を保つためには、トランザクション処理中の更新内容をどう持っておくかという仕組みが必要となる。
なので、独立性はトランザクションが備える機能だと思う。
データが矛盾しないこと、データの整合性が保たれること。一言で言えばその通りだが、具体的には何を意味しているのか?
整合性という言葉から思い付くのは、整合性制約。テーブルに入れるデータを制限することが出来る。NotNull制約とか。参照整合性制約とか。
銀行口座テーブルの金額項目にはマイナス値は入れられないとか。
でもこれってトランザクションの機能なのかなぁ?
もっと下のレベル、テーブルに値を格納する(INSERT文やUPDATE文を実行する)DBMSの機能なんじゃないかという気がする。
まぁ、INSERTやUPDATEは常に“トランザクションを伴わないと実行できない”ので、整合性制約のチェックはトランザクションの仕事に含まれるという事なのだろうか。
あるいは逆にもっと上の業務レベル(口座がマイナスになるのは駄目、という仕様)かもしれない。整合性制約なんて無くてもアプリがチェックすればいい、むしろアプリがチェックしなければならない話なので。
この意味では、一貫性を保証するのは業務アプリケーションであって、トランザクションではない。つまり、トランザクションの機能として整合制約のチェックなんて不要。「整合性制約のチェック=一貫性の保証」だとすれば、トランザクションの機能として一貫性の保証が不要ということになってしまう。
ただ、参照整合性あたりをアプリでチェックするのは違うかもしれないな〜。
よくある口座間の資金移動の整合性の例は、DBレベルのトランザクションではなく、業務レベルの話だと思う。
銀行内の口座1から口座2へ送金する際、口座1から減る金額と口座2に増える金額は等しくなければならない(=一貫性・整合性)、というもの。
そうでないと銀行全体のお金が増えたり減ったりしてしまうというのはその通りだが、銀行全体を見ている時点で、DBレベルでなく業務レベルになっている。
つまり資金移動で金額に着目する例は、“(DBレベルの)トランザクションの”一貫性の例としては不適切。
もしかして口座移動の「同じ金額」って、指示した値で変更されること、という意味?
例えば「10万円にせよ」と指示したらそれ以外の値に更新されることは無い、と。
でも10って書き込んで100になってたら、バグでしょ! そこまで低レベルな話なのかなぁ?
そういう事なら、DBのトランザクションに限った話ではなく、ファイルシステムでも通信でもCPUのメモリーでも、同じこと。
確かにファイルや通信・RAMなら誤り検出(誤り訂正)も現実に重要だけど。DBのトランザクションの一貫性は誤り検出?
さすがにそんな説明は聞いたことないな〜(苦笑)
また、「トランザクションの順序が異なっても同じ結果になること」という説明をする人がいるみたいだが、これは明らかに誤り。
5000円プレゼントキャンペーンと口座2倍キャンペーン(どどーん!)の両方に該当する人がいたら、これら2つのトランザクションの順序が異なると結果が違うのは明白。
最初1万円 → 5000円加算して1万5千円 → 2倍して3万円
最初1万円 → 2倍して2万円 → 5000円加算して2万5千円
“増減”だけだったら、順序が変わっても同じになるんだろうけどねー。(減る際に金額がマイナスになる場合の考慮を除けば)
むしろ一貫性というのは、複数テーブル(複数レコード)にまたがったデータの整合性を言っている気がする。
口座の資金移動で言えば、口座1が減ったのに口座2は変わっていない、あるいは口座2は増えているのに口座1は変わっていない、というのは不整合状態。
複数データの更新が同時に反映されるという機能が無いと、こうした一貫性は保てない。
…でもこれって、原子性の機能のような気もする…。
順序が異なっても同じ結果という説明は誤りだが、トランザクションが同時に実行されても別々に処理されたのと同じ結果になる(混ざらない)という意味なら、正しいかも。
1を加算して2倍する、という処理を同時に2つ実行した場合、トランザクション1(t1)の計算途中の値を他方(t2)が読み込んで処理を開始したら、最終的に誤った結果になってしまう。
最初0 → t1が1足して1 → t1が2倍して2 → t2が1足して3 → t2が2倍して6 … 期待される結果
最初0 → t1が1足して1 → t2が1足して2 → t1が2倍して4 → t2が2倍して8 … 最終結果が8なので誤り
要するにマルチスレッドでの変数の共有と問題と一緒。
…でもこれって、独立性の機能のような気もする…。他のトランザクション処理中の内容を参照しちゃってるから。
待てよ。独立性が保たれているなら、実際はこんな感じになる。
最初0 → t1が1足して1 → t2が(0に対して)1足して1 → t1が2倍して2(コミット) → t2が2倍して2(コミット) … 最終結果が2なので誤り
あるいは、最後のt2が2倍するところでt1のコミット結果を読み込んで4になるとか。いずれにしても期待した結果と違うが。
つまり、同じデータを同時に更新しようとすると不整合になる可能性があるから、そうならないようにする仕組みが必要で、それが一貫性(整合性)という事か?
t1:最初0 → ロック獲得 → 1足して1 → 2倍して2(コミット)
t2:最初0 → ロックしようとして、待ち………………………ロック獲得 → 1足して3 → 2倍して6(コミット)
排他(ロック)を獲得するのはトランザクション単位なので、これはトランザクションの機能。
コミットしたらその内容がずっと反映され続けること。って、当たり前に聞こえるが。
DBMSが更新ログ(ある時点からの更新内容)と実際のテーブルの内容(ある時点のスナップショット)を別々に保持するような仕組みになっている場合、あるいは可用性の為にキャッシュするような仕組みになっている場合、障害が起きると、テーブルの内容は更新前の古いままである可能性がある。
永続性とは、そういった事が無いようにしてね、という意味だと思う。
障害を復旧させる為に、ある時点のバックアップに対してそれ以降の更新ログの内容を反映させていけば最新状態に戻せる(ロールフォワード)とか、仕組み・方法は色々あるだろう
。
DBMSとしては重要な機能だ。
が、これを「トランザクションが持つ機能」と言われると、なんだか違和感がある…。
コミットして結果が反映された時点で、トランザクションは終わりじゃないの?
「トランザクションが完了したら」という条件が付くからトランザクションに関係しているという意味で、トランザクションの特性なのかなぁ?
でもトランザクションが無くても、更新した内容は保持されるんじゃないのかなぁ?
コミット時点でなく更新時点で永続性が始まるだけ。つまり開始がいつになるかが違うだけで、DBMSに永続性が必要な事に変わりは無い。
さてさて、最近、NoSQL(RDBでないDBMS)が注目を浴びている。
中でも本命はColumnFamilyタイプの分散key-valueストア(KVS)だと思っているが、分散KVSはACIDトランザクションをサポートしていないのが普通。
分散KVSは、複数のコンピューター(ノード)に分散してデータが置かれる。
複数の場所に分散しているので、関連するノードを同時に更新するのは、原理的に無理。
したがってACID特性を厳密に守ることが出来ない。
ACID特性の代わりに考えられているのが、BASEという考え方らしい。ただし、BASEはDBレベルのトランザクションの考え方というよりは、もう少し上のレベルを指す概念らしい。
機能 | 勝手な訳 | 自分の解釈 |
---|---|---|
Basically Available | 基本的に使用可能 | 可用性を優先する。 分散しているので、1ノードが障害を起こしていても、他ノードを使って運用できる。 |
Soft-State | 柔軟な状態 | 厳密な状態をとることが出来ない。 分散しているので、1ノードが最新状態でも他ノードは古いという状態が生じうる。 ACID特性のAtomicity(原子性)やConsistency(厳密な一貫性)やIsolation(独立性)は保証できない。 |
Eventual Consistency | 結果整合性 結果一貫性 最終的一貫性 最終的整合性 |
最終的には一貫性(整合性)が保たれる状態になるが、一時的には不整合な状態が生ずる。 分散しているので、1ノードが最新状態でも他ノードは古いという状態が生じる。 最終的には更新が伝播して全ノードが最新状態になるけれども、それまでの間は不整合になる。 |
分散コンピューティング自体は昔から色々考えられている分野だと思うが、分散KVSにおいては、分散されていることを隠蔽しない。
分散されていることを意識せず透過的に扱えれば便利だが、結局それは難しいので、今まで主流になっていないのだろう(少なくともGoogleやAmazonのような大規模なレベルでは)。
なのでKVSを使う際は、分散していることを念頭において考えないといけない。
排他制御に関して。
ACIDトランザクションの場合、整合性を保つために、更新処理を行う際は事前にロックをかける方式をとる。
ロックをかけて、他のトランザクションが更新できないようにする。
この方式を悲観的並行性制御(pessimistic concurrency control:PCC)と言う。
一方、楽観的並行性制御(optimistic concurrency
control:OCC)という方式がある。
(事前にロックをかけたりはせず、)更新時点で他者が更新していないかどうかチェックし、されていなければ更新実行、されていたら失敗するというもの。
失敗の場合、(処理内容にもよるが、)更新処理を最初からやり直す(再実行する)。
更新頻度が多いようだと競合する確率が高くなって再実行が多くなってしまうが、普通はそんなに頻繁に更新しない(競合しない)だろう。という辺りが楽観的?
ロックをかける方式は、楽観的の反対だから悲観的…?
分散環境ではノードにまたがってロックをかけるのが難しい為、PCCでなくOCCが使われるのだろう。
ただし、OCCもチェックと更新が一元的(原子的・アトミック・一息・同時)に行われなければ意味が無いので、この機能がDBMSによって提供されている必要がある。
(チェックと更新が同時に行われないと、チェックOKと判断して更新しようとした間に他の処理が同様にチェックOKと判断して更新してしまう可能性がある)
チェックに使う項目は、更新対象項目そのものやバージョン管理項目・更新日時項目などが考えられる。
DBの研究者ならちゃんとした名称で分類していると思うけれど、自分は知らないので、パターン名は適当です。
実際の業務で使用するには、パターンが色々組み合わさるはず。
コミットすれば全てが同時に反映され、ロールバックすれば全てが元の状態に戻るトランザクションは、細かいことを何も考えなくていいので確かに便利。
トランザクションが使えない場合にどうやってそのパターンを実現すればいいのか考えるのは大変だ…。
DBから値を読み込み、その値を加工して、同じ項目に更新するパターン。
例えば
値を読み込んでから更新するまでの間に他のクライアントが同じ場所を更新すると、整合性がとれなくなってしまう。
したがって、排他制御が必要になる。
悲観的並行性制御(PCC)では、読み込み前にロックをかけ、更新後にロックを解除する。
こうすれば、ロック中は他のクライアントは更新しないことが保証される為、不整合にならない。
楽観的並行性制御(OCC)では、更新時にDB内の値が自分の処理直前の値だったかどうかをチェックし、値が一致していれば更新する。
不一致だったら他者更新済みなので、自分の更新は行わない(失敗する)。
この場合、同じ処理を最初からやり直せば問題ないという処理もあるだろう。(例えば単なる件数カウンターなら、再実行すればOK)
このチェック&更新は同時に行われる必要がある。
(チェックの為の読み込み直後に他者が登録できちゃったら意味が無い)
自分のデータに関する複数テーブル(従属関係にあるテーブル)を更新するパターン。
例えばECサイト等で溜まったポイントを使う場合、現在のポイント数を保持しているテーブルからポイントを減らすのと、使用履歴テーブルに履歴レコードを登録するのは、どちらも行われなければならない。
片方のみ成功した状態、というのは困る。
RDBでは、トランザクションをコミットまたはロールバックすることにより、必ず両方更新成功か両方更新前の状態が保証される。
トランザクションが無いDBMSの場合、片方だけ失敗した場合に不整合な状態になってしまう。
また、両方が成功するとしても、他のクライアントから見た場合に片方のテーブルだけ新しい情報で、他方のテーブルは古い情報を参照してしまう可能性(タイミング)が起こり得る。
この場合でも、テーブルの同一レコード(ROW)内は同時に更新されることが保証されるなら、1つのテーブル内に現在ポイントの情報と履歴データを両方とも保持するという設計にすれば大丈夫そう。(KVSではそういう持ち方が出来る)
同一テーブル内でも、複数の異なるレコードを同時に更新するパターン。
例えば口座1から口座2へ送金する。すなわち口座1からお金を減らし、口座2の残高を増やす。
これも両方が成功する必要がある。
悲観的並行性制御(PCC)では、口座1と口座2(のレコード)に事前にロックをかけることになると思うが、
口座1→口座2の送金と口座2→口座1の送金が同時に発生した場合、プログラミング方法によってはデッドロックが発生する可能性がある。
また、後に処理した更新が失敗した場合、ロールバックすれば先に処理した更新も元に戻ってくれる。
トランザクションが無いと、これを処理するのは難しい気がする。
クライアントからサーバーへ更新1を指示し、更新2を送る前にクライアントが壊れた(LANケーブルが抜けたとか)とする。
RDBであれば、DBサーバー側で通信失敗(障害)を検知し、自動的にロールバックして、更新1も無かったことになるだろう。
つまりトランザクションが無いDBMSを使う場合、DBサーバー側では何もしてくれないので、障害時の復旧処理はクライアントからの指示で行うしか無い。が、クライアントが壊れた場合を想定すると、クライアントの同一処理内で障害復旧処理(更新1を戻す為の更新処理)を記述しても完璧ではない。
リカバリーを行うための仕組みを別途用意する必要がありそう。
すると、トランザクションが無い場合、直接更新を実行するのではなく、キューを利用して処理ステップを分けるのかな。
例えば以下のような感じ。
主たる処理 | 処理失敗後のリカバリー | ||
---|---|---|---|
1 | 「口座1から減らす指示」というデータを口座テーブルの口座1レコードに登録(追加)する。 (これが口座1の増減用のキュー) |
まだ何も登録されていないので、当処理以降を再実行する。 | |
2 | 「口座2を増やす指示」というデータを口座テーブルの口座2レコードに登録(追加)する。 (これが口座2の増減用のキュー) |
口座1の指示レコードはそのままにして、当処理以降を再実行する。 口座1の指示データの中に口座2という情報を入れておけば、 ペアになっていないデータを探し出すことが出来るはず。 |
|
ここまでで、送金処理は一旦終了。 | |||
3 | ここから、別の口座増減処理が動く。常駐プログラムという感じ? (別のプログラムである必要も無さそうだけど、リカバリー時は別プログラムかな?) |
||
4 | 1 | 口座テーブルから未処理の増減指示データを拾う。(口座1) | 当処理だけ再実行する。 |
2 | 口座1から金額を減らす。 同時に、指示データを処理済状態に更新する。 この時点で口座に金額が足りなかった場合は、別途(業務的な)対処が必要。 |
||
5 | 1 | 口座テーブルから未処理の増減指示データを拾う。(口座2) | |
2 | 口座2の金額を増やす。 同時に、指示データを処理済状態に更新する。 |
増減させたい口座テーブルのレコード内に増減指示データを入れるのがポイント。
KVSでは、同一レコード内であれば同時に更新されることがほとんどなので、上記4・5それぞれの内部の更新は不可分になる。つまり失敗しても不整合状態にはならない。
口座毎には口座増減処理が1つしか動かないようにすれば、排他制御を気にする必要は無い。
複数の口座増減プロセスを実行する場合でも、悲観的排他制御(OCC)が使えるなら、他者更新済の場合は自処理は破棄すればいい。
口座1と口座2の増減は同時には行われないが、最終的にはどちらも実行される。
BASEのEventual Consistency(最終的整合性)の考え方。
(ところで この遅延って、実際にはどれくらいなんだろうね?1秒?10秒?もっと?)
口座の増減指示データがキューになっている理由は、口座3や口座4からの増減指示が来るかもしれないから、それらも保持する為。
問題は、同一データでもノードが分散している為、古いデータを読み取って再処理してしまわないかどうか。
イベント | ノードA | ノードB | |||||
---|---|---|---|---|---|---|---|
0 | 初期状態 | 100 | +0 | 0:01 | 100 | +0 | 0:01 |
1 | 増減指示データ(+200)がAに書き込まれる。 | 100 | +200 | 1:02 | 100 | +0 | 0:01 |
2 | 増減処理がAの指示を読み取って処理する。 | 300 | +0 | 1:03 | 100 | +0 | 0:01 |
3 | 増減指示データ(+200)がBに到達。 | 300 | +0 | 1:03 | 100 | +200 | 1:02 |
4 | Aが落ちる。 | 100 | +200 | 1:02 | |||
5 | 増減処理がBの指示を読み取って処理する。 | 300 | +0 | 1:04 | |||
6 | Aが復旧する。 | 300 | +0 | 1:03 | 300 | +0 | 1:04 |
7 | BにAの最初の処理結果(300・+0・1:03)が到達。でも古いから無視。 | 300 | +0 | 1:03 | 300 | +0 | 1:04 |
8 | AにBの処理結果が到達。 | 300 | +0 | 1:04 | 300 | +0 | 1:04 |
イベント | ノードA | ノードB | |||||
---|---|---|---|---|---|---|---|
0 | 初期状態 | 100 | +0 | 0:01 | 100 | +0 | 0:01 |
1 | 増減指示データ1(+200)がAに書き込まれる。 | 100 | +200 | 2:02 | 100 | +0 | 0:01 |
2 | 増減指示データ2(+400)がBに書き込まれる。 | 100 | +200 | 2:02 | 100 | +400 | 2:03 |
3 | 増減処理がAの指示を読み取って処理する。 | 300 | +0 | 2:04 | 100 | +400 | 2:03 |
4 | Aが落ちる。 | 100 | +400 | 2:03 | |||
5 | 増減処理がBの指示を読み取って処理する。 | 500 | +0 | 2:05 | |||
6 | Aが復旧する。 | 300 | +0 | 2:04 | 500 | +0 | 2:05 |
7 | Aに指示2(+400)が到達。 | 300 | +400 | 2:06 | 500 | +0 | 2:05 |
8 | Bに指示1(+200)が到達。 | 300 | +400 | 2:06 | 500 | +200 | 2:07 |
9 | AにBの処理結果(500・+0・2:05)が到達。でも古いので無視。 | 300 | +400 | 2:06 | 500 | +200 | 2:07 |
10 | 増減処理がBの指示を読み取って処理する。 | 300 | +400 | 2:06 | 700 | +0 | 2:08 |
11 | AにBの指示追加結果(500・+200・2:07)が到達。 | 500 | +200 | 2:07 | 700 | +0 | 2:08 |
12 | AにBの処理結果が到達。 | 700 | +0 | 2:08 | 700 | +0 | 2:08 |
うーん、全ケースでこれをシミュレートしないといけないのか(汗)
でもその時点での金額と増減値はペアで管理されているから、処理順序が入れ替わることがあったとしても、最終的には大丈夫そうな気がするようなしないような…(苦笑)
誰か数学的に証明してください^^;
でも少なくとも、順序が入れ替わるとまずい処理では、このやり方は駄目だなー。
データ追加のみ行い、そのデータに対する更新は行わないパターン。
例えば各コンピューターから出力されるログをDBに登録しておくとか。
この場合、キーさえ重複しなければ、競合の問題を考える必要が無い。
登録元を一意に識別するコードとタイムスタンプを組み合わせてキーにすれば、重複することは無いだろう。
競合(重複)することが無いので、トランザクションや排他制御は不要。
そのデータに対する更新を行う処理が1つしか無いパターン。
他にそのデータを更新する処理が無いわけだから、トランザクションや排他制御は不要。
ある時点より前の過去の履歴を集計するパターン。
いわゆる日次集計とか月次集計とか。
対象は過去データなので、もう変わらない。それを参照するだけなので、別にトランザクションや排他制御は要らない。
単なる参照のみなので、(DBへの負荷を除くと)他のトランザクションへ影響することも無い。
ちなみに、こういった集計なら、何度実行しても同じ結果になるはず。何度実行しても同じ結果になる処理の事を「冪等な処理」と呼ぶらしい。
古くなって不要になったデータを削除(浄化)するパターン。
ただ単に削除するだけなら、トランザクションは不要。
念の為どこかにバックアップしてから消すような場合は、バックアップへの書き込みが成功してから消すようにすればいい。
バックアップ先も同一のDBMSの場合、RDBMSであれば素直にトランザクションを使えば、失敗してもそこからやり直せる。あるいはバックアップ先を削除してから再実行する。
バックアップ先がKVSの場合、同一キーのデータが無ければ追加、有れば上書きされるのが普通なので、特に気にせずコピーすればいい。