本文是投稿文章,作者:空之境界
在我眼裡,MySQL和Oracle是這樣的
而SQLite在是這樣的
所以這麼萌的數據庫,我真的應該多了解她的。
簡介
SQLite,是一款輕型的數據庫,是遵守ACID的關系型數據庫管理系統。它的設計目標是嵌入式的,目前Android和iOS的設備內置的都是SQLite數據庫。SQLite雖然嬌小,但也支持事務和多數的SQL92標准。
主要特點
Zero-Configuration 無需安裝和管理配置。
Serverless 無需服務器支持。
Single Database File 數據文件存儲在一個單一的磁盤文件。
Stable Cross-Platform Database File 數據庫文件格式跨平台,無論是大小端,或者是32bit或64bit機器都沒有關系
Compact 完整特性的SQLite編譯出來在500KiB左右,裁剪特性甚至可以得到低於300KiB的庫(當前版本3.8.11.1)。
Manifest typing 可以聲明數據庫字段類型,但是字段存儲的類型實際的存儲類型和實際值相關,單獨的一個字段可能包含不同存儲類的值。
Variable-length records 可變長度記錄,例如你存儲一個字符到VARCHAR(100) 的列,實際需要的存儲空間一個字符加一個字節的存儲空間。
SQL statements compile into virtual machine code SQL語句會被編譯成虛擬機代碼,這種虛擬機代碼直白可讀,便於調試。
Public domain?完全開源。
SQL language extensions
主要缺點
SQLite 只提供數據庫級的鎖定,所以不支持高並發。
不支持存儲過程。
SQLite?沒有用戶帳戶概念,而是根據文件系統確定所有數據庫的權限。這會使強制執行存儲配額發生困難,強制執行用戶許可變得不可能。
如果只在移動設備使用SQLite,那麼他的優點足夠好,並且缺點不明顯,所以大叔MySQL走開。SQLite妹妹快過來╭(╯3╰)╮。
事務與鎖( < 3.7.0)
SQLite的事務和鎖是很重要的概念。
鎖
SQLite有5個不同的鎖狀態
UNLOCKED(未加鎖)
SHARED(共享)
RESERVED(保留)
PENDING(未決)
EXCLUSIVE(排它)
SQLite有一個加鎖表,記錄數據庫連接的鎖狀態。每個數據庫連接在同一時刻只能處於其中一個鎖狀態。每種狀態(UNLOCKED)都有一種鎖與之對應。
讀
數據庫連接最初處於UNLOCKED狀態,在此狀態下,連接還沒有存取數據庫。當連接到了一個數據庫,甚至已經用BEGIN開始了一個事務時,連接都還處於UNLOCKED狀態。為了能夠從數據庫中讀取數據,連接必須必須進入SHARED狀態,也就是說首先要獲得一個SHARED鎖。多個連接可以同時獲得並保持共享鎖,也就是說多個連接可以同時從同一個數據庫中讀數據,SQLite是支持並發讀取數據的。
寫
一個連接想要寫數據庫,它必須首先獲得一個RESERVED鎖。一個數據庫上同時只能有一個RESERVED鎖,保留鎖可以與共享鎖共存,RESERVED鎖即不阻止其它擁有SHARED鎖的連接繼續讀數據庫,也不阻止其它連接獲得新的SHARED鎖。 一旦一個連接獲得了RESERVED鎖,它就可以將數據寫入緩沖區,而不是實際地寫到磁盤。 當連接想要提交修改(或事務)時,需要獲得PENDING鎖,之後連接就不能再獲得新的SHARED鎖了,但已經擁有SHARED鎖的連接仍然可以繼續正常讀數據庫。當所有其它SHARED鎖都被釋放時,擁有PENDING鎖的連接就可以將其鎖提升至EXCLUSIVE鎖,此時就可以將以前對緩沖區所做的修改寫到數據庫文件。所以SQLite是不支持並發寫的。
事務
SQLite有三種不同的事務
DEFERRED(推遲)
MMEDIATE(立即)
EXCLUSIVE(排它)
事務類型在BEGIN命令中指定:
DEFERRED
一個DEFERRED事務不獲取任何鎖(直到它需要鎖的時候),BEGIN語句本身也不會做什麼事情——它開始於UNLOCK狀態。默認情況下就是這樣的,如果僅僅用BEGIN開始一個事務,那麼事務就是DEFERRED的,同時它不會獲取任何鎖;當對數據庫進行第一次讀操作時,它會獲取SHARED鎖;同樣,當進行第一次寫操作時,它會獲取RESERVED鎖。
MMEDIATE
由BEGIN開始的IMMEDIATE事務會嘗試獲取RESERVED鎖。如果成功,BEGIN IMMEDIATE保證沒有別的連接可以寫數據庫。但是,別的連接可以對數據庫進行讀操作;但是,RESERVED鎖會阻止其它連接的BEGIN IMMEDIATE或者BEGIN EXCLUSIVE命令,當其它連接執行上述命令時,會返回SQLITE_BUSY錯誤。這時你就可以對數據庫進行修改操作了,但是你還不能提交,當你COMMIT時,會返回SQLITE_BUSY錯誤,這意味著還有其它的讀事務沒有完成,得等它們執行完後才能提交事務。
EXCLUSIVE
EXCLUSIVE事務會試著獲取對數據庫的EXCLUSIVE鎖。這與IMMEDIATE類似,但是一旦成功,EXCLUSIVE事務保證沒有其它的連接,所以就可對數據庫進行讀寫操作了。
死鎖
如果兩個以BEGIN DEFERRED開始事務的連接都處於SHARED狀態,並且都在等待對方結束SHARED從而進入RESERVED的話,就會進入死鎖狀態。所以BEGIN DEFERRED開始的事務是有可能產生死鎖的.
Write-Ahead Logging ( >=3.7.0 )
SQLite 3.7.0之前是不支持寫的時候讀得。為了能夠讀得時候寫,引入了Write-Ahead Logging(WAL)機制,這樣可以支持一個寫和多個讀並發。
在引入WAL機制之前,SQLite使用rollback journal機制實現原子事務。
rollback journal機制的原理是:在修改數據庫文件中的數據之前,先將修改所在分頁中的數據備份在另外一個地方,然後才將修改寫入到數據庫文件中;如果事務失敗,則將備份數據拷貝回來,撤銷修改;如果事務成功,則刪除備份數據,提交修改。
WAL機制的原理是:修改並不直接寫入到數據庫文件中,而是寫入到另外一個稱為WAL的文件中;如果事務失敗,WAL中的記錄會被忽略,撤銷修改;如果事務成功,它將在隨後的某個時間被寫回到數據庫文件中,提交修改。
同步WAL文件和數據庫文件的行為被稱為checkpoint(檢查點),它由SQLite自動執行,默認是在WAL文件積累到1000頁修改的時候;當然,在適當的時候,也可以手動執行checkpoint,SQLite提供了相關的接口。執行checkpoint之後,WAL文件會被清空。
在讀的時候,SQLite將在WAL文件中搜索,找到最後一個寫入點,記住它,並忽略在此之後的寫入點(這保證了讀寫和讀讀可以並行執行);隨後,它確定所要讀的數據所在頁是否在WAL文件中,如果在,則讀WAL文件中的數據,如果不在,則直接讀數據庫文件中的數據。
在寫的時候,SQLite將之寫入到WAL文件中即可,但是必須保證獨占寫入,因此寫寫之間不能並行執行。
WAL在實現的過程中,使用了共享內存技術,因此,所有的讀寫進程必須在同一個機器上,否則,無法保證數據一致性。
優點
讀和寫可以完全地並發執行,不會互相阻塞(但是寫之間仍然不能並發)。
WAL在大多數情況下,擁有更好的性能(因為無需每次寫入時都要寫兩個文件)。
磁盤I/O行為更容易被預測
缺點
訪問數據庫的所有程序必須在同一主機上,且支持共享內存技術。
每個數據庫現在對應3個文件:.db,-wal,-shm。
當寫入數據達到GB級的時候,數據庫性能將下降。
3.7.0之前的SQLite無法識別啟用了WAL機制的數據庫文件。
WAL引入的兼容性問題。在啟用了WAL之後,數據庫文件格式的版本號由1升級到了2,因此,3.7.0之前的SQLite無法識別啟用了WAL機制的數據庫文件。禁用WAL會使數據庫文件格式的版本號恢復到1,從而可以被SQLite 3.7.0之前的版本識別。
WAL引入的性能問題。在一般情況下,WAL會提高SQLite的事務性能;但是在某些極端情況下,卻會導致SQLite事務性能的下降。
? ?1.在事務執行時間較長或者要修改的數據量達到GB級的時候,WAL文件會被占用,它會暫時阻止checkpoint的執行(checkpoint會清空WAL文件),這將導致WAL文件變得 ? ?很大,增加尋址時間,最終導致讀寫性能的下降。
? ?2.當checkpoint執行的時候,會降低當時的讀寫性能,因此,WAL可能會導致周期性的性能下降