? ? 事務定義了一組SQL命令的邊界,這組命令或者作為一個整體被全部執(zhí)行,或者都不執(zhí)行,這稱為數據庫完整性的原子性原則.
事務的范圍
事務由3個命令控制:begin,commit和rollback.begin開始一個事務,begin之后的所有操作都可以取消,如果連接終止前沒有發(fā)出commit,也會被取消.commit提交事務開始所后執(zhí)行的所有操作,rollback還原begin之后所有的操作.
sqlite> begin;
sqlite> delete from test;
sqlite> rollback;
sqlite> select count(*) from test;
比如上面開始了一個事務,先刪除了test表中所有的行,但是有用rollback進行了回滾.在執(zhí)行select時.你會發(fā)現表中沒有發(fā)生任何改變.
默認情況下,SQLite中每條SQL語句自成事務(自動提交模式).也就是說,如果沒有手動使用begin...commit/rollback定義事務的范圍,SQLite默認每條單獨的SQL命令就是begin....commit/rollback的事務.這種情況下,所有成功完成的命令都自動提交.同樣.所有遇見錯誤的命令都回滾,
沖突的解決
違反約束會導致事務的終止.SQLite有其獨特的方法指定不同的方式來處理約束違反,這種功能稱為沖突解決.SQLite提供五種可能的沖突解決方案:
replace:當違反了唯一性約束時,SQLite將造成這種違反的記錄刪除,以插入貨修改的新紀錄代替,SQL繼續(xù)執(zhí)行,且不報錯.
ignore:當違反約束時,SQLite允許命令繼續(xù)執(zhí)行,違反約束的行保持不變.,它之前之后的記錄繼續(xù)修改,且不報錯.
fail:當違反約束時,SQLite終止命令,但是不恢復約束違反之前已經修改的記錄.在約束違法發(fā)生前的改變將保留.
abort(SQLite默認的沖突解決方法):當違反約束時,SQLite恢復命令所做的所有改變并終止命令.abort是最昂貴的沖突解決方案,要求額外的工作.
rollback:當違反約束時,SQLite執(zhí)行回滾.終止當前命令和整個事務.
沖突解決方法可以在SQL明命令中指定,也可以在表和索引的定義中執(zhí)行,沖突解決策略緊跟在insert或者update后面,.并加上前綴or,例如:
update or fail test set id=100 modified='yes';
insert or ignore into from test values('aaron');
也可以在表內定義時,為單個字段指定沖突解決方法,例如:
create temp table cast(name text unique on conflict rollback);
數據庫鎖
在Sqlite中,鎖和事務是緊密聯系的.SQLite采用粗粒度的鎖.當一個連接要寫數據庫時,所有其他的連接被鎖住,知道寫連接結束它的事務.SQLite使用鎖逐步提升機制,為了寫數據庫,連接需要逐級獲得排他鎖.SQLite有5種不同的鎖狀態(tài):未加鎖(unlocked),共享(shared),預留(reserved),未決(pending)和排他(exclusive).每個數據庫連接在同一時刻只能處于其中一個狀態(tài),每種狀態(tài)(未加鎖狀態(tài)除外)都有一種鎖與之對應.SQlite鎖轉換圖如下圖所示:
最初的狀態(tài)是未加鎖狀態(tài),在此狀態(tài)下,連接還沒有訪問數據庫.當連接一個數據庫,甚至已經用begin開始了一個事務時,連接都還處于未加鎖狀態(tài).
未加鎖狀態(tài)的下一個狀態(tài)就是共享狀態(tài).為了能夠從數據庫中讀(而不是寫)數據,連接必須首先進入共享狀態(tài),也就是說,首先要獲得一個共享鎖.多個連接可以同時獲得并保持共享鎖,也就是說,多個連接可以同時從一個數據庫中讀取數據,但是哪怕只有一個共享鎖還沒有釋放,也不允許任何連接寫數據庫.
如果一個連接想要寫數據庫,他必須首先獲得一個預留鎖.一個數據庫同時只能有一個預留鎖,該預留鎖可以與共享鎖共存,他是寫數據庫的第一階段,預留鎖既不阻止其他擁有共享鎖的連接繼續(xù)讀數據可,也不阻止其他連接獲得新的共享鎖.
一旦一個連接獲得了預留鎖,他就可以開始處理數據庫修改的操作了,盡管這些修改只能在緩沖區(qū)中進行,而不是實際寫到磁盤,對讀出內容所做的修改保存在內存緩沖區(qū).當連接想要提交修改(或事務)時,為了得到排他鎖,必須首先將預留鎖提升為未決鎖.獲得未決鎖之后,其他連接就不能在獲得新的共享鎖了,但已經擁有共享所的連接仍然可以繼續(xù)正常讀取數據庫,此時,擁有未決鎖的連接等待其他擁有共享鎖的連接完成工作并釋放共享鎖.
一旦所有的其他的共享鎖都被釋放,擁有未決鎖的連接就可以將其鎖提升為排他鎖,此時就可以自由的對數據庫進行修改,所有以前所緩存的修改都會被寫到數據庫文件中.
事務類型
SQLite有三種不同的事務類型,他們以不同的鎖狀態(tài)啟動事務.事務可以開始于:deferred,immediate和exclusive.事務類型在begin命令中指定:
begin [deferred | immediate | exclusive ] transaction
一個deferred直到必須使用時才獲取鎖.因此,對于延遲事務,begin語句本身不會做什么事情,它從未鎖定狀態(tài)開始.這是默然的情況.
由begin開始的immediate事務在begin執(zhí)行時試圖獲取預留鎖.若果成功,begin immediate 保證沒有其他的連接可以寫數據庫,其他的連接可以繼續(xù)對數據可進行讀操作.預留鎖的其他結果是沒有其他連接能成功啟動begin immedicate 或者begin exclusive 命令,當其他連接執(zhí)行上述命令時,SQLite會返回SQLITE_BUSY錯誤.這時您可以對數據庫進行修改操作,但是調用commit時,如果其他的讀事務還沒有完成,會返回SQLITE_BUSY錯誤,需要等他們執(zhí)行完畢才能提交事務.
exclusive事務會試著獲取對數據庫的排他鎖,一旦成功,exclusive事務保證數據庫中沒有其他的活動連接,所以可對數據庫進行任意的讀寫操作.
基本的準則是:如果使用的數據庫沒有其他連接,用begin就足夠了,但是,如果使用的數據庫有其他也會對數據庫進行寫操作的連接,就得使用begin immedicate 或者begin exclusive 開始事務.