**:
2、 事務(transaction)
2.1、事務的週期(transaction lifecycles)
程 序與事務之間有兩件事值得注意:
(1) 哪些物件在事務下執行——這直接與api有關。
(2) 事務的生命週期,即什麼時候開始,什麼時候結束以及它在什麼時候開始影響別的連線(這點對於併發性很重要)——這涉及到sqlite的具體實現。
一 個連線(connection)可以包含多個(statement),而且每個連線有乙個與資料庫關聯的b-tree和乙個pager。pager在連線 中起著很重要的作用,因為它管理事務、鎖、記憶體快取以及負責崩潰恢復(crash recovery)。當你進行資料庫寫操作時,記住最重要的一件事:在任何時候,只在乙個事務下執行乙個連線。這些回答了第乙個問題。
一般來說, 乙個事務的生命和statement差不多,你也可以手動結束它。預設情況下,事務自動提交,當然你也可以通過begin..commit手動提交。接下 來就是鎖的問題。
2.2、鎖的狀態(lock states)
鎖 對於實現併發訪問很重要,而對於大型通用的dbms,鎖的實現也十分複雜,而sqlite相對較簡單。通常情況下,它的持續時間和事務一致。乙個事務開 始,它會先加鎖,事務結束,釋放鎖。但是系統在事務沒有結束的情況下崩潰,那麼下乙個訪問資料庫的連線會處理這種情況。
在sqlite中有5種不 同狀態的鎖,連線(connection)任何時候都處於其中的乙個狀態。下圖顯示了相應的狀態以及鎖的生命週期。
關於這個圖有以下幾點 值得注意:
(1) 乙個事務可以在unlocked,reserved或exclusive三種狀態下開始。預設情況下在unlocked時開始。
(2) 白色框中的unlocked, pending, shared和 reserved可以在乙個資料庫的同一時存在。
(3) 從灰色的pending開始,事情就變得嚴格起來,意味著事務想得到排斥鎖(exclusive)(注意與白色框中的區別)。
雖然鎖有這麼多狀 態,但是從體質上來說,只有兩種情況:讀事務和寫事務。
2.3、讀事務(read transactions)
我們先來看看select語句執行時鎖的狀態變化過程,非常簡單:乙個連線執行select語句,觸 發乙個事務,從unlocked到shared,當事務commit時,又回到unlocked,就這麼簡單。
考慮下面的例子(為了簡單,這裡用 了偽碼):
db = open('foods.db')
db.exec('begin')
db.exec('select * from episodes')
db.exec('select * from episodes')
db.exec('commit')
db.close()
由 於顯式的使用了begin和commit,兩個select命令在乙個事務下執行。第乙個exec()執行時,connection處於shared,然 後第二個exec()執行,當事務提交時,connection又從shared回到unlocked狀態,如下:
unlocked→pending→shared→unlocked
如 果沒有begin和commit兩行時如下:
unlocked→pending→shared→unlocked→pending→ shared→unlocked
2.4、寫事務(write transactions)
下面我們來考慮寫資料庫,比如update。和讀事務一樣,它也會經歷 unlocked→pending→shared,但接下來卻是灰色的pending,
2.4.1、the reserved states
當乙個連線(connection)向資料庫寫資料時,從 shared狀態變為reserved狀態,如果它得到reserved鎖,也就意味著它已經準備好進行寫操作了。即使它沒有把修改寫入資料庫,也可以把 修改儲存到位於pager中快取中(page cache)。
當乙個連線進入reserved狀態,pager就開始初始化恢復日誌 (rollback journal)。在reserved狀態下,pager管理著三種頁面:
(1) modified pages:包含被b-樹修改的記錄,位於page cache中。
(2) unmodified pages:包含沒有被b-tree修改的記錄。
(3) journal pages:這是修改頁面以前的版本,這些並不儲存在page cache中,而是在b-tree修改頁面之前寫入日誌。
page cache非常重要,正是因為它的存在,乙個處於reserved狀態的連線可以真正的開始工作,而不會干擾其它的(讀)連線。所以,sqlite可以高 效的處理在同一時刻的多個讀連線和乙個寫連線。
2.4.2 、the pending states
當乙個連線完成修改,就真正開始提交事務,執行該過程的pager進入exclusive狀態。從 reserved狀態,pager試著獲取 pending鎖,一旦得到,就獨佔它,不允許任何其它連線獲得pending鎖(pending is a gateway lock)。既然寫操作持有pending鎖,其它任何連線都不能從unlocked狀態進入shared狀態,即沒有任何連線可以進入資料(no new readers, no new writers)。只有那些已經處於shared狀態的連線可以繼續工作。而處於pending狀態的writer會一直等到所有這些連線釋放它們的鎖, 然後對資料庫加excusive鎖,進入exclusive狀態,獨佔資料庫(討論到這裡,對sqlite的加鎖機制應該比較清晰了)。
2.4.3、the exclusive state
在exclusive狀態下,主 要的工作是把修改的頁面從page cache寫入資料庫檔案,這是真正進行寫操作的地方。
在pager寫入modified pages之前,它還得先做一件事:寫日誌。它檢查是否所有的日誌都寫入了磁碟,而這些通常位於操作的緩衝區中,所以pager得告訴os把所有的檔案寫 入磁碟,這是由程式synchronous(通過呼叫os的相應的api實現)完成的。
日誌是資料庫進行恢復的惟一方法,所以日誌對於dbms非 常重要。如果日誌頁面沒有完全寫入磁碟而發生崩潰,資料庫就不能恢復到它原來的狀態,此時資料庫就處於不一致狀態。日誌寫入完成後,pager就把所有的 modified pages寫入資料庫檔案。接下來就取決於事務提交的模式,如果是自動提交,那麼pager清理日誌,page cache,然後由exclusive進入unlocked。如果是手動提交,那麼pager繼續持有exclusive鎖和儲存日誌,直到commit 或者rollback。
總之,從效能方面來說,程序占有排斥鎖的時間應該盡可能的短,所以dbms通常都是在真正寫檔案時才會占有排斥 鎖,這樣能大大提高併發效能。
事務和鎖 SQLite
2 事務 transaction 2.1 事務的週期 transaction lifecycles 程 序與事務之間有兩件事值得注意 1 哪些物件在事務下執行 這直接與api有關。2 事務的生命週期,即什麼時候開始,什麼時候結束以及它在什麼時候開始影響別的連線 這點對於併發性很重要 這涉及到sqli...
事務和鎖 SQLite
2 事務 transaction 2.1 事務的週期 transaction lifecycles 程 序與事務之間有兩件事值得注意 1 哪些物件在事務下執行 這直接與api有關。2 事務的生命週期,即什麼時候開始,什麼時候結束以及它在什麼時候開始影響別的連線 這點對於併發性很重要 這涉及到sqli...
sqlite的事務和鎖
一 事務 事務定義了一組sql命令的邊界,這組命令或者作為乙個整體被全部執行,或者都不執行。事務的典型例項是轉帳。二 事務的範圍 事務由3個命令控制 begin commit和rollback。begin開始乙個事務,之後的所有操作都可以取消。commit使begin後的所有命令得到確認 而roll...