sqlite 3のトランザクションとロック


SQLITEのロック
SQLiteでは、ロックはトランザクションと密接に関連しています.トランザクションを効率的に使用するには、ロックの方法についていくつかの知識が必要です.SQLiteは粗放型のロックを採用している.1つの接続がデータベースに書き込まれると、書き込み接続がトランザクションを終了するまで、他のすべての接続がロックされます.SQLiteには、異なる書き込みデータベースが最後の瞬間にロックされ、最大の同時性を保証するためにロックテーブルがあります. 
SQLiteは、データベースを書くために、接続を段階的に取得する必要があるロックステップアップメカニズムを使用します.
SQLiteには5つの異なるロック状態があります.
  • アンロック(UNLOCKED)
  • 共有(SHARED)
  • 保留(RESERVED)
  • 未決(PENDING)
  • 列(EXCLUSIVE).

  • 各データベース接続は、同じ時点で1つのステータスしか存在しません.各状態(ロックされていない状態を除く)には、1つのロックが対応しています. 
    最初の状態はロックされていない状態で、この状態では接続はまだデータベースにアクセスしていません.データベースに接続され、BEGINでトランザクションが開始された場合でも、接続はロックされていません.ロックされていない状態の次の状態は共有状態です.データベースからデータを読み取る(書かない)ためには、接続はまず共有状態に入る必要があります.つまり、まず共有ロックを取得する必要があります.複数の接続は、共有ロックを同時に取得して保持することができ、すなわち、複数の接続は、同じデータベースから同時にデータを読み出すことができる.ただし、共有ロックが1つだけ解放されていなくても、データベースへの接続や書き込みは許可されません.
    接続がデータベースを書き込む場合は、まず保持ロックを取得する必要があります.1つのデータベースに1つの予約ロックしかありません.予約ロックは共有ロックと共存し、予約ロックは書き込みデータベースの第1段階である.ロックを保持することは、共有ロックを持つ他の接続がデータベースの読み取りを継続することを阻止したり、他の接続が新しい共有ロックを取得することを阻止したりしません.接続が保留ロックされると、データベースの変更操作の処理を開始できますが、これらの変更は実際にディスクに書き込まれるのではなく、バッファでのみ実行できます.読み出し内容の変更はメモリバッファに保存されます. 
    接続が変更(またはトランザクション)をコミットする場合は、保持ロックを排他ロックに昇格させる必要があります.排他ロックを取得するには、まず保留ロックを未決ロックに昇格させる必要があります.未解決のロックを取得すると、他の接続では新しい共有ロックは取得できませんが、共有ロックを持っている接続では、データベースの正常な読み取りを継続できます.このとき、未解決のロックを持つ接続は、他の共有ロックを持つ接続が完了し、共有ロックが解放されるのを待つ.他のすべての共有ロックが解放されると、未解決のロックを持つ接続はロックを排他ロックに昇格させることができ、データベースを自由に変更することができます.以前にバッファに加えた変更はすべてデータベースファイルに書き込まれます.
    なぜreservedロックを?主に同時性を考慮する.sqlite 3はライブラリレベルの反発ロックexeclusivelockのみであるため.書き込みトランザクションが最初からexeclusiveロックされ、実際のデータ更新が行われ、書き込みディスク操作が行われると、同時性が大幅に低下します.一方、sqliteは、データベースのreservedロックを取得すると、キャッシュ内のデータを変更することができ、同時に他のプロセスは読み取り操作を継続することができます.データベースにexclusiveロックを追加するには、本当にディスクを書く必要があるまで.
    どうしてPending lockがあるの?主に餓死を防ぐためだ.トランザクションを書くにはまずreserved lockを取得する必要があるため、新しいshared lockが常に発生し、トランザクションを書くのに飢え死にする可能性があります.
     
    事務の種類
    SQLiteには3つの異なるトランザクションがあり、異なるロック状態を使用します.トランザクションは次のように開始できます.
  • DEFERRED
  • MMEDIATE
  • EXCLUSIVE

  • トランザクション・タイプは、BEGINコマンドで次のように指定されます.
    BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ] TRANSACTION; 

    DEFERREDトランザクションはロックが必要になるまでロックを取得せず、BEGIN文自体もUNLOCK状態から開始します.デフォルトでは、BEGINだけでトランザクションを開始すると、トランザクションはDEFERREDであり、ロックは取得されません.データベースの最初の読み込み操作を行うと、SHAREDロックが取得されます.同様に、最初の書き込み操作を行うと、RESERVEDロックが取得されます.
    BEGINから始まるIMMEDIATEトランザクションは、RESERVEDロックを取得しようとします.成功した場合、BEGIN IMMEDIATEは他の接続がデータベースに書き込まれないことを保証します.しかし、他の接続はデータベースの読み取り操作を行うことができる.RESERVEDロックは、他の接続のBEGIN IMMEDIATEまたはBEGIN EXCLUSIVEコマンドをブロックし、他の接続が上記のコマンドを実行するとSQLITE_を返します.BUSYエラー.データベースを変更できますが、まだコミットできません.COMMITするとSQLITE_が返されます.BUSYエラーです.これは、他のリードトランザクションが完了していないことを意味します.トランザクションをコミットするには、実行が完了するまで待たなければなりません.
    EXCLUSIVEトランザクションは、データベースの排他ロック(EXCLUSIVE)を取得してみます.これはIMMEDIATEと似ていますが、成功するとEXCLUSIVEトランザクションは他の接続がないことを保証するので、データベースの読み書き操作が可能になります.
    両方の接続がBEGIN IMMEDIATEでトランザクションを開始すると、デッドロックは発生しません.この場合、同じ時点でBEGIN IMMEDIATEに1つの接続しか入ることができず、他の接続は待たなければならない.
    BEGIN IMMEDIATEおよびBEGIN EXCLUSIVEは、通常、書き込みトランザクションで使用されます.同期メカニズムのように、デッドロックの発生を防止します.
    基本的なガイドラインは、使用しているデータベースに他の接続がない場合は、BEGINで十分です.ただし、使用するデータベースに他の接続があってもデータベースを書き込みます.BEGIN IMMEDIATEまたはBEGIN EXCLUSIVEを使用してトランザクションを開始する必要があります. 
     
    参照先:
    https://blog.csdn.net/cheng_fangang/article/details/22291693