SQLite Design and Concepts
API
分為兩大類
- core API. 基本的SQL操作
- extension API. 創(chuàng)建自定義的SQL操作。
基本數(shù)據(jù)結(jié)構
需要了解的組成部分有連接、statments、B樹、pager。
為了寫好SQLite代碼,需要了解的基本概念,API、事務和鎖。
連接和statments
這兩種數(shù)據(jù)結(jié)構和查詢語句的基礎。一個連接表示對數(shù)據(jù)庫的一個連接,也是一個事務上下文。statments由這些連接產(chǎn)生。statments在內(nèi)部由VDBE字節(jié)碼表示。
B-tree and pager
每個數(shù)據(jù)庫連接可以有多個數(shù)據(jù)庫。每一個數(shù)據(jù)庫對象有一個B-tree對象,每一個B-tree對象有一個pager對象。
pager的工作包括把page從磁盤導入到內(nèi)存中、寫數(shù)據(jù)庫、管理事務、處理鎖和從crash中恢復等。
一個數(shù)據(jù)庫連接中是在事務中操作,一個數(shù)據(jù)庫連接不會同時有兩個事務。
Core API
兩種方式來執(zhí)行SQL語句。
- prepared query. SQLite執(zhí)行命令的最終方式。包括三個階段
- preparation
- execution
- finalization
- wrapped query
連接數(shù)據(jù)庫
sqlite3_open()傳入:memory:或空字符串作為數(shù)據(jù)庫的名稱,會創(chuàng)建內(nèi)存中的數(shù)據(jù)庫。
如果數(shù)據(jù)庫的pagesize和操作系統(tǒng)的pagesize相同,會提高IO效率。
運行prepared query
-
prepation:把字符串轉(zhuǎn)化為VDBE字節(jié)碼。
sqlite3_prepare_v2()生成sqlite3_stmt句柄,包括編譯好的字節(jié)碼和需要執(zhí)行命令和迭代結(jié)果集的所有資源。 -
execution:
sqlite3_step使VDBE單步執(zhí)行字節(jié)碼。首次調(diào)用會獲取某種鎖。 -
finalization:
sqlite3_finalize()關閉statment,釋放相關資源。
使用參數(shù)化的SQL
包括兩種參數(shù)綁定
- positional
- named
使用參數(shù)綁定的優(yōu)勢
- 不需要編譯,可以執(zhí)行statment多次。只需要reset,然后重新bind,運行。
- 自動處理好轉(zhuǎn)義字符,避免數(shù)據(jù)庫注入和語法錯誤。
運行wrapped query
sqlite3_exec()和sqlite3_get_table()
exec()解析命令,識別出單獨的語句,并依次處理。
處理錯誤
sqlite3_errcode()返回最后執(zhí)行函數(shù)的錯誤碼。使用sqlite3_errmsg()提供最后錯誤的描述。
格式化SQL語句
sqlite3_mprintf().類似sprintf(),%q和%s類似,不過會把每一個單引號變?yōu)檫B續(xù)的兩個,防止數(shù)據(jù)庫注入。%Q會為字符串首尾加上單引號,空指針會變成NULL
操作控制
使用注冊filter或callback函數(shù)來達到在特定事件發(fā)生時會被monitor,control。
有三個hook函數(shù)
sqlite3_commit_hook()sqlite3_rollback_hook()sqlite3_update_hook()-
wal_hook()(3.7版本中)
使用多線程
shared cache mode:使用多個一個線程來管理多個SQL連接,這些連接共享page cache,降低了server的內(nèi)存使用。
extension API
事務
事務的生命周期
- 在事務下究竟是什么在運行。
在每個數(shù)據(jù)連接中的每個數(shù)據(jù)庫對象,有一個B-Tree和pager對象。pager管理事務、鎖、緩存、崩潰恢復。 - 事務何時開始,何時結(jié)束,何時開始影響其他數(shù)據(jù)庫連接。
可以短到一個語句,可以長到直到你命令終結(jié)。一個操作默認在autocommit模式,即每一個命令在一個事務中運行。何時影響和鎖的狀態(tài)有關。
鎖的狀態(tài)
每一個事務以unlocked,reserved,exclusize鎖狀態(tài)開始
讀事務
兩次讀操作
使用事務:unlocked->pending->shared->unlocked
不使用事務:unlocked->pending->shared->unlocked->pending->shared->unlocked
不適用事務,在兩次讀之間數(shù)據(jù)可能會改變
寫事務
每一個操作,讀或者寫,都必須經(jīng)過unlocked->pending->shared. pending是gateway lock。
reserved state
當要向數(shù)據(jù)庫中寫入時,需要從shared到reserved狀態(tài)。到reserved狀態(tài)后,可以把改變寫到本地緩存,而不是數(shù)據(jù)庫中。
進入reserved狀態(tài)后,pager初始化rollback journal,是用于回滾和crash recovery的文件。實際是為更改前的數(shù)據(jù)庫內(nèi)容。
在reserved狀態(tài)中,需要pager維護三種pages:改變的pages(存在page cache中),未改變的pages(讀出的沒有改變的page)以及日志pages(不在page cache中,而是在B-Tree改變page前寫入joural)。
pending state
pending lock是gateway lock,保證不會有鎖從unlocked到shared狀態(tài)。
exclusive state
此狀態(tài)下,主要工作是把改變的page從page cache沖入數(shù)據(jù)庫文件。此時pager真正開始改變數(shù)據(jù)庫。
在寫入數(shù)據(jù)庫之前,先確保日志文件已經(jīng)被寫入磁盤。
日志是系統(tǒng)崩潰或掉電后,日志是恢復數(shù)據(jù)庫的唯一方法。
日志文件在commit之后才會被清除。
自動提交和效率
自動提交效率不高,因為每個語句都要獲取鎖。而且語句之間可能會被改變。
調(diào)整page cache
轉(zhuǎn)向互斥狀態(tài)
一般來說,當pager不能存儲更多數(shù)據(jù)時,會從reserved狀態(tài)轉(zhuǎn)向互斥狀態(tài)。實際上,有soft limit和hard limit。
soft limit是指page cache首次被占滿,此時包括改變的和未更改page的混合。pager會移除未更改的page,如此重復,直到所有的都被更改的page占滿。此時page cache都是已更改的page,進入hard limit,不得不轉(zhuǎn)向互斥狀態(tài)。
cache_size調(diào)整page cache的大小。
決定page cache的大小
利用sqlite3_analyer
等待鎖
使用busy handler
sqlite3_busy_timeout()
使用正確的事務
寫操作使用begin immediate