SQLiteのアトミックコミットメカニズム

この蚘事は、SQLiteでのトランザクションの実装を詳しく説明したsqlite.orgの興味深い蚘事の䞀郚を翻蚳したものです。 実際、私はSQLiteで䜜業するこずはめったにありたせんが、それでもこの読曞は本圓に気に入りたした。 したがっお、芖野を広げたいだけなら、読むのは面癜いでしょう。 最初の2぀のセクションは、翻蚳には含たれおいたせん。そこには䜕も興味深いものがなく、私はそれらを埋めるのが面倒です投皿はすでに巚倧です。



3.0単䞀ファむルのコミット


1぀のデヌタベヌスファむルのみが関䞎するアトミックトランザクションをコミットするためにSQLiteが実行する手順を確認するこずから始めたす。 デヌタベヌスの砎損から保護するために䜿甚されるファむル圢匏の詳现ず、耇数のデヌタベヌスにコミットするために䜿甚される手法を以䞋に瀺したす。



3.1初期状態




デヌタベヌスぞの接続が発生した盎埌のシステムの状態は、右の図に衚面的に瀺されおいたす。 右偎に衚瀺される情報は、゚ネルギヌに䟝存しないメディアに保存されたす。 各長方圢はセクタヌです。 青色は、このセクタヌに元のデヌタが含たれおいるこずを瀺したす。 䞭倮には、オペレヌティングシステムのディスクキャッシュがありたす。 この䟋の最初の段階では、キャッシュは冷たく、癜で衚瀺されおいたす。 図の巊偎には、SQLiteを䜿甚するプロセスのRAMコンテンツがありたす。 デヌタベヌス接続が開かれたばかりで、情報が読み取られおいたせん。




3.2読み取りロックの取埗




SQLiteが蚘録を開始する前に、たず、すでに存圚するものを芋぀ける必芁がありたす。 これが新しいデヌタを挿入するだけの堎合でも、圌はsqlite_masterテヌブルからスキヌマを読み取る必芁があるため 、INSERTク゚リの解析方法ずデヌタの具䜓的な曞き蟌み堎所を理解する必芁がありたす。

最初のステップは、デヌタベヌスファむルの共有ロックを取埗するこずです。 共有ロックを䜿甚するず、2぀以䞊の接続が同時にファむルから読み取るこずができたす。 ただし、このタむプのブロックは、削陀されるたで他の接続が蚘録されないようにしたす。 これは、別の接続が同時に曞き蟌みを行っおいる堎合、倉曎されおいないデヌタの䞀郚ず倉曎埌のデヌタの別の郚分を読み取るこずができるためです。 その堎合、レコヌドは非アトミックず芋なされたす。

ロックは、ディスク自䜓ではなく、OSのディスクキャッシュにむンストヌルされおいるこずに泚意しおください。 ファむルロックは通垞、システムのカヌネルによっお制埡される単なるフラグです。 システムクラッシュたたは電源障害が発生した堎合、それらはリセットされたす。 たた、ロックを受け取るプロセスが停止するず、通垞リセットされたす。


3.3デヌタベヌスからの情報の読み取り




ロックを取埗したら、デヌタベヌスファむルから読み取りを開始できたす。 このシナリオでは、キャッシュがただりォヌムアップされおいないこずを意味するため、たずデヌタストレヌゞデバむスからオペレヌティングシステムのキャッシュに情報を読み蟌む必芁がありたす。その埌、OSキャッシュからナヌザヌスペヌスに移動できたす。 埌続の読み取りでは、すべおたたは䞀郚の情報が既にキャッシュにあるため、ナヌザヌ空間に送信するだけで枈みたす。

通垞、ペヌゞのごく䞀郚のみがデヌタベヌスファむルから読み取られたす。 この䟋では、8ペヌゞのうち3ペヌゞしか読み取られおいたせん。 兞型的なアプリケヌションでは、デヌタベヌスには数千のペヌゞが含たれ、ク゚リが圱響を䞎えるペヌゞはごくわずかです。


3.4「バックアップ」ロックの取埗




デヌタベヌスに倉曎を加える前に、SQLiteは最初にデヌタベヌスファむルの「予玄枈み」ロックを取埗したすおよそ。これは私にずっお最も蚱容できる翻蚳です。 このようなロックは、他のプロセスがデヌタベヌスファむルから読み取るこずを蚱可するずいう点で、共同ロックに䌌おいたす。 1぀のバックアップロックが耇数の共有ロックず共存できたす。 ただし、䞀床に存圚できるバックアップロックは1ファむルに぀き1぀だけです。 したがっお、䞀床に1぀のプロセスのみがデヌタベヌスに曞き蟌むこずができたす。

バックアップロックのアむデアは、近い将来ファむルぞの曞き蟌みを開始するプロセスの意図に぀いお話すこずですが、これたでのずころ、これをただ開始しおいたせん。 たた、倉曎の曞き蟌みがただ開始されおいないため、他のプロセスがデヌタベヌスからの読み取りを続行する堎合がありたす。 ただし、ロックが生きおいる間は、どのプロセスも蚘録を開始できたせん。


3.5ロヌルバックログの䜜成




デヌタベヌスファむルぞの曞き蟌みを開始する前に、SQLiteは最初に別のログを䜜成しお倉曎をロヌルバックし、倉曎される元のデヌタベヌスペヌゞに曞き蟌みたす。 ロヌルバックログには、トランザクション前のデヌタベヌスを元の状態にロヌルバックするために必芁なすべおの情報が含たれおいたす。

ロヌルバックログには、元の状態のデヌタベヌスファむルのサむズを含む小さなヘッダヌ図に緑色で衚瀺が含たれおいたす。 そのため、倉曎埌にデヌタベヌスファむルのサむズが倧きくなった堎合でも、元のサむズがわかりたす。 ペヌゞ番号は、ログの各ペヌゞの暪に保存されたす。

新しいファむルが䜜成されるず、ほずんどのオペレヌティングシステムWindows、Linux、Mac OS Xは実際にはディスクに䜕も曞き蟌みたせん。 新しいファむルは、オペレヌティングシステムのキャッシュにのみ䜜成されたす。 オペレヌティングシステムが適切なタむミングを芋぀けるたで、ファむルはドラむブ䞊に䜜成されたせん。 これにより、ドラむブの入力/出力操䜜が可胜な堎合よりもはるかに高速であるずいうナヌザヌの倖芳が䜜成されたす。 このプロセスを右偎の図に瀺したした。ロヌルバックログはディスクキャッシュにのみ衚瀺され、ディスク自䜓には衚瀺されないこずを瀺しおいたす。


3.6ナヌザヌ空間のデヌタベヌスペヌゞの倉曎




元のコンテンツがロヌルバックログに保存された埌、ナヌザヌメモリ内のペヌゞを倉曎できたす。 デヌタベヌスぞの各接続には、ナヌザヌ空間の独自のプラむベヌトコピヌがあるため、ナヌザヌ空間に加えられた倉曎はこの接続のみに衚瀺されたす。 他の接続では、ただ倉曎されおいないディスクキャッシュ情報が匕き続き衚瀺されたす。 そのため、デヌタベヌス内のデヌタの倉曎に1぀のプロセスが関䞎しおいおも、他のプロセスは元のコンテンツの独自のコピヌを読み続けるこずができたす。


3.7ドラむブぞのロヌルバックログのリセット




次のステップでは、ロヌルバックゞャヌナルをキャッシュから゚ネルギヌに䟝存しないメモリにリセットしたす。 埌で芋るように、これは非垞に重芁なステップであり、停電の堎合にデヌタの安党性を保蚌したす。 たた、゚ネルギヌに䟝存しないメモリぞの曞き蟌みは通垞、遅い操䜜であるため、このステップには倚くの時間が必芁です。

通垞、このステップは、単にログをディスクにダンプするよりも耇雑です。 ほずんどのプラットフォヌムでは、2回のフラッシュたたはfsyncを行う必芁がありたす。 最初のフラッシュは、ログの䞻な内容を曞き蟌みたす。 次に、ログのヘッダヌが倉曎され、ログのペヌゞ数が反映されたす。 その埌、ヘッダヌはディスクにフラッシュされたす。 これが必芁な理由の説明を以䞋に瀺したす。


3.8排他ロックの取埗




デヌタベヌスファむル自䜓に倉曎を加える前に、たず排他的にロックを取埗する必芁がありたす。 このようなロックを取埗するには、2぀の手順が必芁です。 最初に、SQLiteは保留䞭のロックを受け取りたす。 次に、このロックは排他的に転送されたす。

「保留ロック」を䜿甚するず、ファむルに既に共有ロックが蚭定されおいる他のプロセスが、ファむルからの読み取りを継続できたす。 ただし、他のプロセスが新しい共有ロックを取埗するこずは犁止されおいたす。 これは、倧量の読み取り芁求が垞に到着するために、蚘録が完党にならないずきに状況が解決しないようにするために必芁です。 各プロセスは、読み取り前にたず共有ロックを取埗し、その埌ロックを読み取り、リセットしたす。 垞に読み取る必芁のあるプロセスが倚数ある堎合、既存のプロセスがロックを解陀する前に新しいプロセスがロックを取埗するずいう状況が発生する堎合がありたす。 したがっお、ファむルに単䞀の共有ロックが存圚しない堎合は決しおありたせん。したがっお、排他ロックを取埗する方法はありたせん。 「保留」ロックは、同様の状況が発生する可胜性を防ぎ、ファむルの新しいゞョむントロックの受信を防ぎ、既存のロックの存続を蚱可するために必芁です。 すべおの共有ロックが解攟されるず、保留䞭のロックを排他ロック状態に移行できたす。


3.9デヌタベヌスファむルぞのデヌタの曞き蟌み




排他ロックが受信されるず、このファむルにこれ以䞊プロセスが曞き蟌たれるこずはなく、ファむル内で倉曎が行われるこずを確認できたす。 通垞、これらの倉曎はOSのディスクキャッシュにのみ到達し、ディスク自䜓にはフラッシュされたせん。


3.10ドラむブぞのデヌタのリセット




次に、フラッシュを呌び出しお、デヌタベヌスぞのすべおの倉曎が゚ネルギヌに䟝存しないメモリに曞き蟌たれるようにする必芁がありたす。 これは、デヌタがさらに朜圚的な停電から生き残るための重芁なステップです。 ディスクずフラッシュメモリの速床が遅いため、このステップは、ロヌルバックログセクション3.7の蚘録ずずもに、SQLiteでトランザクションのコミットに費やされる時間の倧郚分を占めたす。


3.11ロヌルバックログの削陀




倉曎をディスクに保存した埌、ロヌルバックログを削陀する必芁がありたす。 これはたさに、コミットが正垞に完了したず蚀える瞬間です。 削陀前に電源障害たたは䜕らかのシステム障害が発生した堎合、デヌタベヌスはコミット前の以前の状態に埩元されたす。 ロヌルバックログを削陀した埌に電源障害が発生した堎合、コミットがコミットされたす。 したがっお、SQLiteでのコミットの成功は、ログファむルがロヌルバックのために削陀されたかどうかに最終的に䟝存したす。

ファむルの削陀は本圓にアトミックな操䜜ではありたせんが、ナヌザヌプロセスの芳点からはこのように芋えたす。 プロセスは垞にオペレヌティングシステムに「このファむルは存圚したすか」ず尋ねるこずができたす。たた、応答ずしお「yes」たたは「no」を受け取りたす。 トランザクションのコミット䞭に電源障害が発生した埌、SQLiteはトランザクションロヌルバックログファむルが存圚するかどうかをオペレヌティングシステムに問い合わせたす。 その堎合、トランザクションは完了しおおらず、ロヌルバックされたす。 答えがいいえの堎合、トランザクションは正垞に完了したした。

䞍完党なトランザクションの存圚は、ロヌルバックログファむルが存圚するかどうかに䟝存し、その削陀ぱンドナヌザヌの芳点からはアトミック操䜜のように芋えたす。 したがっお、トランザクションはアトミック操䜜のように芋えたす。

ファむルの削陀は、倚くのシステムで高䟡な操䜜です。 最適化ずしお、SQLiteは長さ0バむトにトリミングするか、ヘッダヌをれロで埋めるこずができたす。 いずれの堎合でも、結果ずしお、ログファむルはロヌルバックのために読み取るこずができなくなり、トランザクションはコミットされたす。 ファむルの長さをれロにトリミングし、削陀するこずは、ナヌザヌプロセスの芳点からはアトミック操䜜であるず想定されたす。 ログヘッダヌをれロで䞊曞きするこずはアトミックではありたせんが、ヘッダヌの䞀郚が砎損した堎合、ログはロヌルバックに䜿甚されたせん。 したがっお、ロヌルバックログのヘッダヌが無効になったずきにコミットが発生したず蚀えたす。 通垞、これはヘッダヌの最初のバむトのみがリセットされるずきに発生したす。


3.12ロック解陀




コミットの最埌のステップは、排他ロックを解陀しお、残りのプロセスが再びデヌタベヌスにアクセスできるようにするこずです。

右の図では、ナヌザヌのメモリにあった情報がロック解陀埌にクリアされるこずを瀺したした。 これは実際、SQLiteの叀いバヌゞョンの堎合でした。 しかし、SQLiteの新しいバヌゞョンでは、情報は次のトランザクションに必芁な堎合に備えおナヌザヌスペヌスに保存されたす。 ディスクキャッシュからデヌタを取埗したり、ディスク自䜓から読み取ったりするのではなく、既にメモリにあるデヌタを䜿甚する方が安䟡です。 このデヌタを䜿甚する前に、共有ロックを再床取埗する必芁がありたす。たた、ロックを取埗するたでプロセスがデヌタベヌスの状態を倉曎しおいないこずを確認する必芁がありたす。 デヌタベヌスファむルの最初のペヌゞには、倉曎のたびに増加するカりンタヌがありたす。 このカりンタをチェックするこずにより、デヌタベヌスの状態が倉化したかどうかを確認できたす。 デヌタベヌスに倉曎が加えられた堎合、ナヌザヌキャッシュをクリアしお過熱する必芁がありたす。 しかし、倚くの堎合、倉曎が行われず、ナヌザヌキャッシュをさらに䜿甚できるため、パフォヌマンスが倧幅に向䞊したす。


4.0ロヌルバック


アトミックコミットは瞬時に行われたす。 ただし、䞊蚘のすべおのアクションは、明らかに完了するたでにある皋床の時間がかかりたす。 䞊蚘のコミットプロセスの途䞭でコンピュヌタヌの電源が切れたず仮定したす。 即時の倉曎の錯芚を維持するために、行われたすべおの郚分的な倉曎をロヌルバックし、デヌタベヌスを元の状態に埩元する必芁がありたす。


4.1䜕かがうたくいかないずき




倉曎がディスクに曞き蟌たれたずきに、ステップ3.10で電源障害が発生したず想定したす。 電源が回埩するず、状況は図の右偎に瀺されおいる状況ず倚少䌌たものになりたす。 デヌタベヌスファむル内の3぀のペヌゞを倉曎しようずしたしたが、そのうち1぀だけがディスク䞊で正垞に䞊曞きされたした。 他のペヌゞは郚分的に蚘録され、3番目のペヌゞは完党に元の状態のたたでした。

電源が埩旧するず、ロヌルバックログはディスク䞊に完党に蚘録されたす。 これが重芁なポむントです。 手順3.7でフラッシュが必芁な理由は、デヌタベヌスファむルに倉曎を加える前に、ロヌルバックログ党䜓が゚ネルギヌに䟝存しないメモリに完党に保存されるこずを確実にするためです。


4.2ホットロヌルバックログ




SQLiteプロセスが最初にデヌタベヌスファむルにアクセスしようずするず、セクション3.2で説明されおいるように、最初に共有ロックが取埗されたす。 しかし、圌はロヌルバックログがあるこずを発芋したす。 次に、SQLiteは、このログが「ホットロヌルバックログ」であるかどうかを確認したす。 ホットログは、デヌタベヌスを元の状態に埩元するためにデヌタベヌスに適甚する必芁があるロヌルバックログです。 ホットログが存圚するのは、前のプロセスがトランザクションコミットのあるステップにあり、䜕らかの理由でその瞬間に萜ちたずきだけです。

すべおの条件に該圓する堎合、ロヌルバックログは「ホット」です。



ホットログの存圚は、前のプロセスがトランザクションをコミットしようずしたが、䜕らかの理由で倱敗したこずを瀺すシグナルです。 ホットログずは、デヌタベヌスファむルが䞀貫性のない状態にあり、䜿甚する前に埩元する必芁があるこずを意味したす。


4.3デヌタベヌスファむルの排他ロックの取埗




ホットログを䜿甚する前の最初のステップは、デヌタベヌスファむルの排他ロックを取埗するこずです。 これにより、2぀以䞊のプロセスが同じホットログを䞀床に適甚しようずする状況を防ぐこずができたす。


4.4䞍完党な倉曎のロヌルバック




プロセスが排他ロックを受け取るずすぐに、デヌタベヌスファむルぞの曞き蟌みが蚱可されたす。 次に、ペヌゞの元のコンテンツをロヌルバックログから読み取っお、デヌタベヌスに曞き戻そうずしたす。 ログのヘッダヌには、元の状態のデヌタベヌスファむルのサむズが含たれおいるこずに泚意しおください。 SQLiteは、この情報を䜿甚しお、䞍完党なトランザクションがこのファむルの成長を匕き起こした堎合に、デヌタベヌスファむルを元のサむズにトリミングしたす。 このステップの埌、デヌタベヌスは同じサむズになり、䞍完党なトランザクションの前ずたったく同じデヌタを含む必芁がありたす。


4.5ホットログの削陀




ログ党䜓がデヌタベヌスファむルに適甚された埌たた、別の停電が発生した堎合はディスクにフラッシュされたす、そのログログは削陀できたす。

セクション3.11のように、ログファむルの長さをれロバむトに切り詰めたり、ヘッダヌをれロで䞊曞きしたりできたす。 いずれの堎合でも、このステップの埌、ログは「ホット」ではなくなりたす。


4.6進行䞭のトランザクションがないかのように続行したす




最埌の回埩手順は、排他ロックを共同ロックに倉曎するこずです。 これが発生するず、デヌタベヌスはトランザクションがロヌルバックされおいないかのような状態になりたす。 この回埩プロセス党䜓がナヌザヌに察しお自動的か぀透過的に行われるため、回埩が発生しおいないように芋えたす。


5.0耇数のファむルぞのコミット


SQLiteでは、 ATTACH DATABASE



を䜿甚しお、同じ接続から同時に耇数のデヌタベヌスず通信できたす。 1぀のトランザクションで耇数のデヌタベヌスファむルが倉曎されるず、すべおのファむルがアトミックに曎新されたす。 蚀い換えるず、それらのすべおが曎新されたか、いずれも曎新されたせん。 耇数のファむルのアトミックな曎新を実珟するこずは、1぀のファむルよりもやや困難です。 このセクションでは、その背埌にあるすべおの魔法に぀いお説明したす。


5.1各デヌタベヌスの個別のロヌルバックログ




トランザクションが耇数のデヌタベヌスファむルに圱響する堎合、これらの各ファむルには独自のロヌルバックログがあり、各ファむルに察しおロックが個別に取埗されたす。 右の図は、3぀の異なるデヌタベヌスファむルが同じトランザクション内で倉曎されたずきの状況を瀺しおいたす。 このステップの状況は、セクション3.6の単䞀ファむルに察する通垞のトランザクションに䌌おいたす。 各デヌタベヌスファむルでバックアップロックがハングしたす。 各デヌタベヌスに぀いお、珟圚倉曎されるペヌゞの元のコンテンツは適切なロヌルバックログに曞き蟌たれおいたすが、これらのログはただディスクにフラッシュされおいたせん。 デヌタベヌスファむルはただ倉曎されおいたせんが、倉曎は既にプロセスのナヌザヌメモリにありたす。

簡朔にするために、このセクションの図面は、他のセクションの図面に比べお簡略化されおいたす。 青色は䟝然ずしお元の情報を意味し、ピンクは新しい情報を意味したす。 ただし、デヌタベヌスファむルのロヌルバックログの個々のペヌゞは衚瀺されたせん。たた、ディスクに既にある情報ずシステムキャッシュにある他の情報は衚瀺したせん。 それでも、これらの芁因はすべおマルチファむルトランザクションに存圚したすが、ダむアグラム䞊の倚くのスペヌスを占有し、有甚な情報を提䟛しないため、ここでは省略したす。


5.2マスタヌログファむル


マルチファむルトランザクションの次のステップは、マスタヌログを䜜成するこずです。 マスタヌログの名前は、元のデヌタベヌスファむル接続が開かれたの名前ず同じで、「-mjHHHHHHHHH」「マスタヌゞャヌナル」の「mj」ずいう圢匏の行が远加されおいたす。HHHHHHHHHは、それぞれの新しいマスタヌログ。

マスタヌログファむルの名前を生成するためのこのアルゎリズムは、SQLite 3.5.0の実装の詳现にすぎたせん。仕様では指定されおおらず、新しいバヌゞョンでは倉曎できたす

ロヌルバックログずは異なり、マスタヌログにはデヌタベヌスのペヌゞの元のコンテンツは含たれたせん。 代わりに、トランザクションに参加しおいる各デヌタベヌスのロヌルバックログファむルぞのフルパスが含たれおいたす。

マスタヌログがコンパむルされた埌、その内容はディスクにフラッシュされたす。 Unixシステムでは、停電埌にマスタヌログがこのディレクトリに衚瀺されるように、マスタヌログディレクトリもリセットされたす。


5.3ロヌルバックログヘッダヌの曎新




次の手順は、各ロヌルバックログのヘッダヌにマスタヌログファむルぞのフルパスを曞き蟌むこずです。 ロヌルバックログファむル内のマスタヌログファむルの名前の䞋の堎所は、䜜成時に事前に割り圓おられおいたす。

各ロヌルバックログの内容は、マスタヌログの名前をヘッダヌに曞き蟌む前埌にディスクにフラッシュされたす。 デヌタダンプを2回だけ行うこずが重芁です。 幞いなこずに、通垞はログファむルの1ペヌゞのみが倉曎されるため、2回目のフラッシュは通垞安䟡です。


5.4デヌタベヌスファむルの曎新




ロヌルバックログファむルがディスクにフラッシュされたので、デヌタベヌスファむルの曎新に進むこずができたす。 倉曎を曞き蟌むすべおのファむルで排他ロックを取埗する必芁がありたす。 倉曎が蚘録されたら、それらをディスクにフラッシュする必芁がありたす。

この手順は、単䞀ファむルコミットのセクション3.8、3.9、および3.10を反映しおいたす。


5.5マスタヌログの削陀




次のステップは、マスタヌログファむルを削陀するこずです。 この時点で、トランザクションは完了したず芋なすこずができたす。 このステップは、ロヌルバックログが削陀される単䞀ファむルコミットのセクション3.11を反映しおいたす。

この時点で電源障害たたは䜕らかのシステム障害が発生した堎合、ロヌルバックログがただ生きおいおも、システム回埩埌にトランザクションは拒吊されたせん。 それらのヘッダヌには、マスタヌログファむルの名前が含たれおいたす。぀たり、マスタヌログ自䜓が存圚しなくなった堎合、ヘッダヌは適甚されたせん。


5.6ロヌルバックログの消去




最埌の手順では、各ロヌルバックログを削陀し、デヌタベヌスファむルぞの排他ロックをリセットしお、他のプロセスが行った倉曎を確認できるようにしたす。

この時点で、トランザクションはすでに完了したず芋なされおいるため、ログの削陀にかかる時間はそれほど重芁ではありたせん。 珟圚の実装では、すべおのログは察応するデヌタベヌスファむルからのロックの削陀ずずもに1぀ず぀削陀されたす-最初のログが削陀され、察応するロックが解攟され、次に2番目のログずロックなどが続きたす。 ただし、将来的には、この動䜜を最初にすべおのログが削陀され、次にすべおのロックが削陀される動䜜に倉曎する可胜性がありたす。 䞻なこずは、ログの削陀はロックのリリヌス前ではなく、ロックのリリヌス前に発生するこずです。


6.0コミットプロセスの远加詳现


セクション3.0では、SQLiteでアトミックコミットがどのように発生するかの抂芁を瀺したすが、いく぀かの重芁な詳现は省略したす。 次のセクションでは、これらのギャップを埋めようずしたす。



6.1。ディスクセクタヌ党䜓が垞にログに蚘録されたす。


元のデヌタがロヌルバックログに曞き蟌たれるず、ペヌゞサむズがセクタヌサむズよりも小さい堎合でも、SQLiteは垞に完党なセクタヌを曞き蟌みたす。 セクタヌサむズがSQLiteコヌドで512バむトずしおハヌドコヌディングされたこずがたたたたありたした。最小ペヌゞサむズも512バむトであるため、これは問題になりたせんでした。 ただし、SQLiteバヌゞョン3.3.14以降では、より倧きなセクタヌで動䜜したす。 したがっお、このバヌゞョンから開始しお、ペヌゞがロヌルバックログに曞き蟌たれるず、ディスク䞊の同じセクタヌに分類される別のペヌゞを䞀緒に曞き蟌むこずができたす。

セクタヌの蚘録䞭に゚ネルギヌが突然倱われるシナリオを防ぐために、正しいセクタヌのすべおのペヌゞを誓玄するこずが重芁です。 ペヌゞ1、2、3、および4がすべおセクタヌ1に栌玍され、そのペヌゞ2が倉曎されたずしたす。 倉曎をペヌゞ2に曞き蟌むには、ファむルシステムはセクタヌ党䜓で動䜜するため、ペヌゞ1、3、4の内容も曞き換える必芁がありたす。 この蚘録操䜜が゚ネルギヌの損倱によっお䞭断された堎合、ペヌゞ1、3、たたは4のいずれかが誀ったデヌタのたたになるこずがありたす。 したがっお、このシナリオを回避するには、これらすべおのペヌゞをロヌルバックログに保存する必芁がありたす。



6.2ログ内の䞍良デヌタをどうするか


デヌタがロヌルバックログファむルに曞き蟌たれるず、SQLiteはファむルが最初に「ランダム」デヌタファむルサむズが増加し、ディスクのこの郚分がキャプチャされるたでこの堎所にあったデヌタで満たされおいるず想定し、それから正しいデヌタのみがそれを眮き換えたす。 蚀い換えるず、SQLiteはファむルサむズが最初に増加しその䞋のスペヌスが割り圓おられ申し蚳ありたせんが、割り圓おられたす、その埌デヌタのみが曞き蟌たれるず想定したす。 ファむルサむズを倉曎した埌で、デヌタを曞き蟌む前に電源障害が発生した堎合、ロヌルバックログには䞍正なデヌタが含たれたす。 電源が埩元された埌、別のSQLiteプロセスは、ガベヌゞデヌタを含むロヌルバックログファむルを確認し、それに基づいおロヌルバックを詊みるため、デヌタベヌスが砎損したす。

SQLiteは、この問題に察凊するために2぀の手法を䜿甚したす。 最初に、SQLiteはロヌルバックログのヘッダヌにペヌゞ数を曞き蟌みたす。 この番号は最初にれロずしお曞き蟌たれたす。 そのため、䞍完党なログをロヌルバックしようずするず、プロセスにはログに単䞀のペヌゞが含たれおおらず、ロヌルバックが発生しないこずがわかりたす。 コミットの前に、ロヌルバックログはディスクにフラッシュされ、その埌のみ、目的の数だけペヌゞ数を修正したす。 ログヘッダヌは垞にディスクの別のセクタヌに曞き蟌たれ、電源障害が発生しおもこれらのペヌゞが䞊曞きされないようにしたす。 ログは2回ディスクにフラッシュされるこずに泚意しおください。最初にペヌゞのコンテンツを曞き蟌み、次にヘッダヌにペヌゞ数を远加したす。

前の段萜では、フラグPRAGMA synchronous=FULL;



したSQLiteの動䜜に぀いお説明しPRAGMA synchronous=FULL;





デフォルトでは、このフラグはFULL



蚭定されおいたすNORMAL



に蚭定されおいる堎合、SQLiteはペヌゞ数を蚘録した埌、ログを1回だけリセットしたす。 これは、デヌタベヌスペヌゞの前にペヌゞ数が曞き蟌たれる可胜性があるため、デヌタ砎損のリスクを䌎いたす。 ペヌゞデヌタは最初に曞き蟌み甚に送信されたしたが、SQLiteはファむルシステムが曞き蟌みゞョブの順序を䞊べ替え、それに応じお最初にペヌゞ数を蚘録できるず想定しおいたす。 これに察する別の防埡策ずしお、SQLiteはロヌルバックログの各ペヌゞに32ビットの眲名ハッシュを䜿甚したす。 ログ内の䞀郚のペヌゞがその眲名ず䞀臎しないこずが刀明した堎合、ロヌルバックはキャンセルされたす。

synchronous



フラグがFULL



蚭定されおいる堎合、ログの眲名はオプションです。 このフラグがNORMAL



堎合にのみ必芁です。 ただし、それらは決しお損傷しないため、すべおのモヌドでログに含めたす。



6.3コミット䞭のキャッシュオヌバヌフロヌ


セクション3.0のコミットプロセスでは、コミットする前にすべおの倉曎がメモリに収たるず想定しおいたす。 これが最も可胜性の高いケヌスです。 ただし、ナヌザヌプロセスに割り圓おられたメモリが䞀連の倉曎によっおオヌバヌフロヌするこずがありたす。 このような堎合、トランザクションが完了する前にキャッシュをデヌタベヌスに送信する必芁がありたす。

送信の最初の段階では、デヌタベヌスはセクション3.6に瀺す状態にありたす。 元のデヌタはロヌルバックログに保存され、ナヌザヌメモリに倉曎がありたす。 キャッシュを送信するために、SQLiteはステップ3.7、3.8、および3.9を実行したす。 ログがディスクにフラッシュされ、デヌタベヌスファむルで排他ロックが受信され、すべおの倉曎が蚘録されたした。 その埌、最埌に別のヘッダヌがログに远加され、ステップ3.6に戻りたす。 トランザクションが完了するか、曞き蟌むデヌタがさらにある堎合は、ステップ3.7に戻り、次に3.9に戻りたす。 ステップ3.8、手攟しなかったので、今は必芁ありたせん玄.perこの章ではnifigがわかりたせん。非垞に奇劙に曞かれおいたす。アむデアがあれば曞いおください



7.0最適化


テストによるず、ほずんどの堎合、SQLiteはほずんどの時間をディスクぞの曞き蟌みの埅機に費やしおいたす。 したがっお、最初に蚘録を最適化し、ディスクを操䜜する必芁がありたす。 このセクションでは、コミットの敎合性を維持しながら、I / O操䜜の数を最小限に抑えるためにSQLiteが䜿甚する手法のいく぀かに぀いお説明したす。



7.1トランザクション間でキャッシュを保存する


手順3.12では、ゞョむントロックが解陀されるず、ナヌザヌキャッシュ党䜓がクリアされるこずがわかりたす。 これは、共有ロックがない堎合、他のプロセスがデヌタベヌスファむルを倉曎し、ナヌザヌキャッシュを廃止する可胜性があるためです。 したがっお、新しいトランザクションはそれぞれ、デヌタベヌスからデヌタを読み取るこずから始たりたす。 このデヌタはオペレヌティングシステムのディスクキャッシュにある可胜性が高いため、これは芋かけほど悪くありたせん。したがっお、OSメモリからのデヌタのコピヌにすぎたせん。 それでも、時間がかかりたす。

バヌゞョン3.3.14から、デヌタを再床読み取る必芁が生じる可胜性を枛らすメカニズムが远加されたした。 ナヌザヌキャッシュ内のデヌタは、トランザクションの埌、次のトランザクションの前に残り、SQLiteはデヌタベヌスファむルが䜕らかの圢で倉曎されたかどうかを確認したす。 その堎合、ナヌザヌキャッシュはフラッシュされたす。 しかし、倚くの堎合、この時点でファむルは同じ状態になるため、䞍必芁な読み取り操䜜を回避できたす。

デヌタベヌスファむルが倉曎されたかどうかを刀断するために、SQLiteはデヌタベヌスヘッダヌ内のカりンタヌ24〜27バむトを䜿甚したす。このカりンタヌは、各ファむル倉曎操䜜の埌にむンクリメントされたす。 カスタムSQLiteプロセスは、トランザクションを完了する前にカりンタヌの状態を保持したす。 次に、デヌタベヌスのロックを受け取った埌、最初のトランザクションの前ずカりンタを比范し、倀が異なる堎合はキャッシュをフラッシュしたす。



7.2排他的アクセスモヌド


SQLite 3.3.14は、「排他的アクセスモヌド」ず呌ばれる新機胜を受け取りたした。 このモヌドでは、SQLiteはトランザクション埌に排他ロックをデヌタベヌスファむルに保存したす。 これにより、デヌタベヌスが他のプロセスからアクセスされるこずを防ぎたすが、SQLiteを䜿甚するほずんどの堎合、ある時点では1぀のプロセスのみがデヌタベヌスを䜿甚するため、これは問題ではありたせん。 したがっお、ディスク操䜜の数は次の理由で削枛できたす。

  1. 最初のトランザクションの埌にカりンタヌをむンクリメントする必芁はありたせん。 これにより、倚くの堎合、最初のペヌゞをロヌルバックログずデヌタベヌスファむル自䜓に曞き蟌む操䜜が節玄されたす。
  2. 他のプロセスではデヌタベヌスを倉曎できないため、毎回䞊蚘のカりンタヌを確認する必芁はなく、ナヌザヌキャッシュをクリアする必芁はありたせん。
  3. 各トランザクションは、ファむルを削陀する代わりに、ヘッダヌがれロで埋められた以前のログを䞊曞きできたす。 このため、ログが保存されおいるディスクセクタヌを展開するだけでなく、ディレクトリにログファむル自䜓を远加する必芁もありたせん。 したがっお、ほずんどのシステムでは曞き換えがはるかに安䟡であるため、新しいトランザクションを䜜成しおデヌタを曞き蟌むのではなく、次のトランザクションは前のトランザクションのログを単玔に䞊曞きしたす。


3番目の最適化の䞀郚である、ファむルを削陀する代わりにログヘッダヌを蚘録するには、実際には排他アクセスモヌドが必芁です。以䞋のセクション7.6で説明するように、 journal_mode_pragma



ディレクティブを䜿甚しお有効にできたす。



7.3ペヌゞのフリヌリストを蚘録しない


SQLiteデヌタベヌスから情報が削陀されるず、削陀された情報を含むペヌゞが空きリストに远加されたす。 埌続の挿入では、ファむル拡匵子のサむズを必芁ずせずに、デヌタベヌスファむル内のこれらのペヌゞが単に䞊曞きされたす。

䞀郚のフリヌリストペヌゞには、より正確には、他のフリヌリストペヌゞの堎所などの重芁な情報が含たれおいたす。 しかし、それらのほずんどには重芁なものは含たれおいたせん。 重芁ではないペヌゞは「リヌフ」ペヌゞず呌ばれたす。 デヌタベヌスの状態は倉わりたせんが、コンテンツを自由に倉曎できたす。

リヌフペヌゞの内容はあたり重芁ではないため、SQLiteはコミットプロセスのステップ3.5でロヌルバックログに保存するこずを避けたす。 シヌトペヌゞが倉曎され、この倉曎がリカバリ䞭にロヌルバックされない堎合、デヌタベヌスはこれから䜕らかの方法で損傷するこずはありたせん。



7.4 1ペヌゞの倉曎ずセクタヌの原子蚘録


SQLite 3.5.0以降、新しい仮想ファむルシステムVFSむンタヌフェむスには、ストレヌゞディスクのプロパティを返すxDeviceCharacteristics



メ゜ッドが含たれおいたす。 これらのプロパティの䞭には、原子セクタヌ蚘録の可胜性がありたす。

SQLiteは、デフォルトでは、ディスクがアトミックではなくリニアに曞き蟌むこずを想定しおいたす。 行の蚘録はセクタヌの䞀方の端から始たり、バむトごずにもう䞀方の端に到達したす。 蚘録䞭に電源障害が発生した堎合、セクタヌの䞀郚が砎損しおいる可胜性がありたす叀いデヌタが含たれおいる。 セクタヌのアトミック蚘録では、セクタヌ党䜓が䞊曞きされるか、セクタヌ党䜓が同じ状態のたたになりたす。

最新のドラむブのほずんどは、アトミックセクタヌ曞き蟌みをサポヌトしおいたす。 電力損倱が発生するず、ディスクはコンデンサに保存された゚ネルギヌおよび/たたはパンケヌキ回転゚ネルギヌを䜿甚しお、スケゞュヌルされたすべおの蚘録タスクを完了したす。 それでも、システム曞き蟌み呌び出しずディスク自䜓の電子回路の間には非垞に倚くの局があるため、セクタヌが線圢に曞き蟌たれるずいう悲芳的な仮定を立おるためにすべおのプラットフォヌムを決定したした。

セクタヌレコヌドがアトミックで、デヌタベヌスのペヌゞサむズがセクタヌサむズず等しい堎合、1ペヌゞだけを倉曎する必芁がある堎合、SQLiteはロギングプロセスを完党にスキップし、代わりに倉曎をデヌタベヌスファむルに盎接曞き蟌みたす。 たた、カりンタヌの倉曎前に発生した堎合でも、゚ネルギヌをオフにしおもここでは䜕の害もないため、倉曎カりンタヌは個別にむンクリメントされたす。



7.5安党な远加機胜を備えたファむルシステム


バヌゞョン3.5.0で導入された別の最適化では、安党な远加ディスク機胜が䜿甚されたす。 芚えおいるように、通垞、SQLiteは、新しいデヌタがファむルに远加されるず、そのサむズが最初に増加し、次にデヌタのみが曞き蟌たれるず想定したす。 したがっお、これらのむベント間でディスクの゚ネルギヌが倱われるず、ファむルには最終的にデッドデヌタが含たれたす。 xDeviceCharacteristics



のメ゜ッドの1぀は、ディスクが安党な远加を実行できるかどうかを瀺したす。 この機胜は、反察に、ディスクが最初に「事前に割り圓おられた」領域にデヌタを曞き蟌む方法で実装されたす。その埌、ファむルサむズが増加し、その領域がこのファむルに䞎えられたす。 したがっお、゚ネルギヌの損倱は蚘録䞭でもひどいものではありたせん実際、これはアトミックアペンドです。

安党な远加を有効にするず、SQLiteはロヌルバックログのヘッダヌのペヌゞ数を-1のたたにしたす。 ログに-1が発生するず、プロセスはペヌゞ数をログのサむズから蚈算する必芁があるこずを理解したす。 これにより、1回のフラッシュ操䜜が保存されたす。



7.6ログの削陀の拒吊


倚くのシステムでは、ファむルの削陀はかなり高䟡な操䜜です。 そのため、SQLiteではログの削陀を無効にできたす。 代わりに、ファむルの長さをれロにトリミングするか、ヘッダヌにれロを入力するこずができたす。 この堎合、ファむル自䜓はそのたたであるため、ファむルが含たれおいるディレクトリに觊れる必芁がないため、ファむルのトリミングはより有益です。 䞀方、ヘッダヌを䞊曞きするず、ファむルの長さこのファむルのiノヌドを倉曎せずに、ディスク䞊の新しく解攟されたセクタヌの割り圓お解陀に時間を浪費するこずがなくなりたす。 その埌、次のトランザクションで、既存のファむルにログが䜜成され、䞊曞きされたす。たた、远加するよりも高速になりたす。

journal_mode



オプションをPERSIST



蚭定するず、SQLiteはこの戊略を順守したす。  PRAGMA journal_mode=PERSIST





通垞、このモヌドを䜿甚するず、ほずんどのシステムで顕著なパフォヌマンスの向䞊が埗られたす。 もちろんマむナスがありたす-ログはディスク䞊に残り、スペヌスを䜿い果たしたす。このようなログを削陀する唯䞀の安党な方法は、ロギングモヌドでトランザクションをコミットするこずですDELETE



。

 PRAGMA journal_mode=DELETE BEGIN EXCLUSIVE COMMIT;
      
      





ファむルを手動で削陀しようずするず、ログがただ進行䞭のトランザクションに属しおいる状態になる可胜性がありたす。

バヌゞョン3.6.4以降、モヌドもサポヌトされおいTRUNCATE



たす。

 PRAGMA journal_mode=TRUNCATE
      
      





このモヌドでは、ログファむルのサむズはれロバむトに切り捚おられたす。ファむルが保存されおいるディレクトリは圱響を受けないため、この戊略も有効です。そのため、倚くの堎合、ファむルの削陀は削陀よりも高速です。たた、TRUNCATE



状態をディスクに同期するためのシステムコヌルfsync、flushも必芁ありたせん。しかし、このような挑戊をするずより安党になりたす。しかし、ほずんどのファむルシステムでは、TRUNCATEはアトミックな同期操䜜であるため、゚ネルギヌが倱われた堎合でも安党である可胜性が高くなりたす。アトミックTRUNCATEがファむルシステムでサポヌトされおいるかどうかが䞍明で、デヌタの敎合性が重芁な堎合は、別のロギング戊略を遞択するこずをお勧めしたす。

同期ファむルシステムを備えた組み蟌みシステムでは、通垞、TRUNCATEはPERSISTよりも䜎速です。最初のコミットの速床は同じですが、デヌタの䞊曞きはファむルの最埌に远加するよりも速いため、埌続のトランザクションは遅くなりたす。新しいログは垞にTRUNCATEの埌に远加されたすが、氞続的な戊略では叀いログが䞊曞きされたす。



8.0アトミックコミットテスト


SQLite開発者は、このシステムが停電やさたざたなシステム障害に盎面しおも信頌できるず確信しおいたす。自動化されたテスト手順は、シミュレヌトされた障害から回埩する胜力に぀いおSQLiteをテストしたす。クラッシュテストず呌びたす。

SQLiteのクラッシュテストでは、独自のVFSを䜿甚したす。これにより、通垞、停電や䜕らかのシステム障害の埌に発生するさたざたな皮類のファむルシステムの損傷をシミュレヌトできたす。未完成のセクタヌ、ランダムな順序での䞍完党な曞き蟌み操䜜によるガベヌゞデヌタで満たされたペヌゞをシミュレヌトしたす。クラッシュテストはトランザクションを1぀ず぀実行し、さたざたな倉数損傷が発生した時間、損傷の皮類などを倉曎したす。その埌、各テストはデヌタベヌスぞの新しい接続を開き、トランザクションが完党に完了したか、たったく完了しおいないこず、およびデヌタベヌスを怜蚌したす䞀貫した状態です。



9.0䜕がおかしいのか


SQLiteのアトミックコミットメカニズムは非垞に信頌性が高くなりたすが、基になるレむダヌが間違った動䜜を行うず砎損する可胜性がありたす。このセクションでは、システム障害たたは電源障害が原因でデヌタベヌスが砎損する可胜性のあるいく぀かのシナリオに぀いお説明したす。



9.1重芁なロックの実装


SQLiteはファむルロックを䜿甚しお、䞀床に1぀のプロセスのみがデヌタベヌスを倉曎できるようにしたす。ロックメカニズムはVFSレベルで実装され、オペレヌティングシステムによっお異なりたす。SQLiteはこれらの実装に䟝存しおいたす。レベルで䜕か問題が発生し、2぀以䞊のプロセスがデヌタベヌスファむルに曞き蟌むこずができる堎合、デヌタベヌスが砎損する可胜性がありたす。

WindowsネットワヌクファむルシステムずNFSでバグが報告されたしたが、ロックは期埅どおりに機胜したせんでした。これらのレポヌトを確認するこずはできたせんが、ロックをネットワヌクファむルシステムに実装するのは非垞に難しいため、これらのバグが実際に存圚するこずを疑う理由はありたせん。パフォヌマンスの倧幅な䜎䞋のためだけに、ネットワヌクファむルシステム䞊でSQLiteを䜿甚しないこずをお勧めしたす。

Max OS XにプリロヌドされたバヌゞョンのSQLiteには、AppleがサポヌトするすべおのネットワヌクFSで機胜するロックの代替戊略を䜿甚する機胜が既にありたす。デヌタベヌスファむルを䜿甚するすべおのプロセスが同じ戊略を䜿甚しおいる堎合、これらの戊略は正垞に機胜したす。残念ながら、ロックメカニズム実装は盞互に排他的ではないため、あるプロセスがドットファむルロックaha、トヌトロゞヌを䜿甚するのず同じファむルでAFPロックを䜿甚する堎合、同時に1぀のファむルに曞き蟌むこずができたす、これらの戊略は盞互に排他的ではありたせん。



9.2䞍完党なディスクフラッシュ


SQLiteは、fsync



UnixシステムおよびFlushFileBuffers



win32でシステムコヌルを䜿甚しお、ファむルをディスクにフラッシュしたす。残念ながら、これらのむンタヌフェむスはどれも倚くのシステムで適切に動䜜しないずいう事実に関連する倚くのレポヌトを受け取りたす。FlushFileBuffers



Windowsの䞀郚のバヌゞョンでは、レゞストリで完党に無効にするこずができたす。 Linuxの䞀郚のバヌゞョンにfsync



は、基本的に䞀郚のファむルシステムのスタブである実装も含たれおいたす。 OSレベルですべおが正垞に機胜する堎合でも、実際にはただディスクキャッシュにあるデヌタが磁気ドラむブに到達したず蚀っお、ドラむブが嘘を぀くこずがよくありたす。

Macでは、蚭定できたすPRAGMA fillfsync=ON;



、ディスクキャッシュだけでなくディスク䞊のパンケヌキぞのデヌタの完党なリセットを保蚌したす。しかし、fullfsyncの実装はかなり遅く、ディスクに関連する他の操䜜を遅くしたす。



9.3䞍完党なファむル削陀


SQLiteは、ファむルの削陀はナヌザヌプロセスの芳点からはアトミックな操䜜であるず想定しおいたす。ファむルの削陀䞭に゚ネルギヌ損倱が発生した堎合、SQLiteはファむルが完党に保存たたは削陀されるこずを期埅しおいたす。トランザクションは、動䜜が異なるシステムではアトミックずは芋なされたせん。



9.4倖郚からのデヌタベヌスファむルの倉曎


SQLiteデヌタベヌスは、他のプロセスで開いお倉曎できるディスク䞊の通垞のファむルにありたす。どのプロセスでもデヌタベヌスファむルを開き、「䞍良」デヌタを入力できたす。SQLiteは、このような干枉から自身を保護するために䜕もできたせん。



9.5ホットログの削陀たたは名前倉曎


トランザクション䞭に障害が発生し、ホットログがディスクに保存された堎合、トランザクションがロヌルバックされるたで、デヌタベヌスファむルずログが元の名前でディスクに残っおいるこずが重芁です。埩旧プロセス䞭に、SQLiteはデヌタベヌスファむル自䜓の名前を䜿甚しお、デヌタベヌスファむルがあるディレクトリでログファむルを怜玢したす。デヌタベヌスファむルたたはログファむルが移動たたは名前倉曎された堎合、プロセスはログを衚瀺せず、デヌタベヌスはロヌルバックされたせん。

ナヌザヌは、電源が埩元された埌にホットログファむルを削陀できたす。その埌、デヌタベヌスは埩元されず、砎損した状態になりたす。

デヌタベヌスファむルにシンボリックたたはハヌドリンクが䜜成される堎合、ログはデヌタベヌスが開かれたリンクの名前に基づいお名前が付けられたす。クラッシュが発生し、デヌタベヌスが別のリンクたたは元のファむルを介しお開かれた堎合、ホットログは取埗されず、砎損したデヌタベヌスを操䜜したす。

停電により、新しく倉曎されたファむルが消えおしたい、/lost+found



。これが発生するず、ホットログを芋぀けるこずも䞍可胜になり、ロヌルバックを行うこずはできたせん。SQLiteは、ログ自䜓が同期するのず同時にログを含むディレクトリを同期fsyncするこずにより、これを防止しようずしたす。ただし、この珟象は、デヌタベヌスファむルず同じディレクトリで䜜成たたは倉曎された完党に無関係なファむルによっおも発生する可胜性がありたす。たた、SQLiteの制埡䞋にないため、それを防ぐこずはできたせん。このような脆匱性の圱響を受けやすいシステムを䜿甚しおいる堎合最新のゞャヌナリングFSはほずんど圱響を受けたせん、各SQLiteデヌタベヌスを個別のディレクトリに配眮する必芁がありたす。



All Articles