簡單分析SQLite4的一些設計改變,分析sqlite4設計
1.0 內容提要
SQLite4 是一個放在庫中的緊湊的,自包含的,零維護的的ACID資料庫引擎, 像SQLite3一樣, 但具有改進的介面和檔案格式.
運行時環境封裝到了一個對象之中.
使用了一個很不錯的索引值對儲存引擎:
- 一個獨立的大型鍵空間 - 不是SQLite3中那種每個表單獨的鍵空間和索引.
- 按字典順序的鍵排序.
- 多個儲存引擎,可在運行時互換.
- 預設在磁碟上的儲存殷勤使用了一個日誌結構的合并資料庫.
表的PRIMARY KEY真正被用作儲存引擎的鍵.
可以使用小數點運算.
外鍵約束和遞迴觸發器預設是啟用的.
覆蓋指數可以顯示聲明.
2.0 概述
SQLite4 對於SQLite3而言,是一個可選方案,而不是一個替代方案. SQLite3 還沒有過時. SQLite3 和 QLite4 將會並行受到支援. SQLite3 遺留的好處不會被拋棄. SQLite3 還將會被持續的維護和改進. 但如果需要的話,新系統的設計者現在將可以選擇 SQLite4 而不是 SQLite3.
SQLite4 努力保持了SQLite3的最優秀特性,同時在不破壞相容性的前提下解決了SQLite3中無法修複的問題. SQLite3 和 SQLite4 中將會持續保持一樣的特性有:
- SQLite4 是放在一個庫中並連結到一個大型應用程式的完整的,關係型,事務性的, ACID, SQL 資料庫引擎. 沒有伺服器,I/O直接面向硬碟.
- SQLite4 的原始碼任何人可以用於任何目的. 沒有著作權、發布或者公開原始碼或者編譯二進位檔案方面的的限制. 不用去擔心煩人的許可證.
- 使用了動態類型,而不是大多數其它的SQL資料庫引擎所使用的僵化的靜態類型.
- (預設)在磁碟上的鏡像是一個單獨的使用良好且穩定檔案格式的磁碟檔案, 使得SQLite4庫適合於作為一個應用程式的檔案格式使用.
- SQLite4 將會是快速且可靠的,無需管理員操心它就能運作的蠻好.
- SQLite4 的實現只有最簡化的依賴,因而它可以很容易的整合到嵌入式系統或者其它非常規的運行時環境.
實現上仍然採用常用的組合語言C。與SQLite3相比,SQLite4使用了更多C99特性,不過仍然可以使用常見的編譯器編譯。SQLite4使用了諸如size_t,int64_t,uint64_t以及其他標準資料類型。
SQLite4的編程介面與SQLite3的非常相似,只不過命名首碼都從sqlite3_更改為sqlite4_。SQLite3中舊的和作廢的介面已經從SQLite4中剔除了。給一些函數增加了參數,有時對參數稍作修改或者對其參數順序進行重新排序。修改了某些介面名字,使其更加符合其功能。總體上來說,SQLite4的編程介面與SQLite3的非常相似,這樣移植一個SQLite3上的應用到SQLite4上只需花一個小時或者兩個小時完成搜尋替代就可以了。
SQLite3和SQLite4沒有共用任何符號,因此把SQLite3和SQLite4同時嵌入到同一進程,同時使用它們都是可行的.
3.0 SQLite4的主要改變
3.1 運行時對象
SQLite4中一些介面的第一個參數接收一個(新加的)指向一個sqlite4_env對象的指標,它定義了運行時環境。需要接收sqlite4_env指標的樣本程式包括:
- sqlite4_open()
- sqlite4_malloc(), sqlite4_realloc(), and sqlite4_free()
- sqlite4_mprintf()
- sqlite4_random()
- sqlite4_config()
一個sqlite4_env對象執行個體定義了SQLite4與其他系統互動是如何互動的。一個sqlite4_env對象包含的方法能夠:
- 分配,進入,離開和收回互斥體
- 分配,調整和釋放堆記憶體,
- 訪問和控制底層鍵/值儲存引擎,
- 使用高品質隨機種子初始化內建PRNG,
- 取得目前時間和日期與本地時區,
- 記錄error日誌訊息.
標準平台(windows和Unix)的SQLite4構建包含了一個全域sqlite4_env對象,通常這個對象適配於所在平台。如果一個介面程式的參數中有一個指向sqlite4_env對象的指標,而且傳給這個參數的指標是null 指標時,這個介面程式就會使用預設的全域sqlite4_env對象。另外,某些應用可能要求在相同的地址空間上運行兩個或者多個SQLite4執行個體,同時每個執行個體使用了各自不同的互斥原語,不同的記憶體堆以及不同的時間日期函數等等。SQLite4通過對每個資料庫執行個體建立不同的sqlite4_env對象來滿足這種需求。sqlite4_env對象中還廢除了全域和靜態變數,這樣就可以非常容易地把SQLite4移植到哪些對靜態或者全域資料提供有限支援的嵌入式系統中。
3.2 簡化的鍵/值儲存引擎
相對於SQLite3,SQLite4使用的鍵/值儲存引擎擁有一個大大簡化了的介面。這個儲存引擎是可拔插的;通過對qlite4_env對象在開啟新的資料庫連接前做適當的改動,它可以在運行時被改變。
SQLite4 需要一個實現了有序鍵/值對的儲存引擎,它的鍵和值是任意長度的位元據。鍵必須惟一,且按字典排序。也就是說,鍵應該根據一個比較函數進行排序,例如:
複製代碼 代碼如下:
int key_compare(const void *key1, int n1, const void *key2, int n2){
int c = memcmp(key1, key2, n1<n2 ? n1 : n2);
if( c==0 ) c = n1 - n2;
return c;
}
給定一個探針鍵,SQLite4需要能夠找到它最近的鍵,然後以字典序升序或降序遍曆鍵。向一個已有的鍵插入資料時會覆蓋舊資料。事務,包括原子提交和復原,由儲存引擎負責。
SQLite4經由表和索引,將所有資料存放區到一個單獨的鍵空間, 與此相反,SQLite3中每個表和索引都需要一個單獨的鍵空間。SQLite4的儲存也與SQLite3不同,因為它需要儲存引擎以字典序對鍵進行排序, 而SQLite3使用了一個非常複雜的比較函數來決定記錄的儲存順序。
SQLite4與儲存引擎之間的通訊是通過一個定義完善的並且簡單的介面進行的。新的儲存引擎可以在運行期間進行替換:只要在指定資料庫連接之前替換sqlite4_env對象裡的某些函數指標就可以了。
如果替換的儲存引擎不支援復原,那麼這就意味著SQLite4無法運行ROLLBACK。如果替換的儲存引擎不支援事務嵌套,那麼這就意味著SQLite4無法運行嵌套事務。因此,嵌入到SQLite4中的儲存引擎的功能越少,對應的整體的系統功能就會越差。
預設情況下內建的儲存引擎是日誌結構的合并式資料庫。它比LevelDB要快很多倍,支援嵌套式事務,它把整個內容儲存在單一的磁碟檔案裡。SQLite4的未來版本還可能包含一個內建的採用B樹結構的儲存引擎。
3.3 現在的PRIMARY KEY是真正的主鍵
Sqlite3允許聲明表中的任何單個或多列為主鍵。但在內部,SQLite3隻將PRIMARY KEY簡單地當做唯一約束來對待。實際中用於儲存使用的鍵是每一行的rowid。
Sqlite4則相反,它真實地使用聲明的表主鍵(更確切地說是PRIMARY KEY的編碼值)作為鍵而插入儲存引擎。SQLite4表通常沒有rowid(除非在表沒有PRIMARY KEY的情況下,這時需要一個rowid作為隱式主鍵。)這意味著內容在磁碟中按主鍵的順序儲存。這也意味著可以通過對PRIMARY KEY的一次查詢即可定位記錄。在SQLite3中,主鍵上的一個搜尋意味著在自動建立的索引中先找到rowid,然後依據該rowid對主表做一個二次搜尋。
SQLite4 需要 PRIMARY KEY 的所有元素不可為空. 這是一條SQL標準. 由於早期版本的疏忽, SQLite3 沒有在PRIMARY KEY 列上強制加上這條NOT NULL約束,到該漏洞被發現的時候SQLite3已經在被廣泛使用了, NOT NULL 約束的啟用就可能會對太多的程式造成影響.
3.4 十進位數
SQLite4 使用十進位算數做所有的數字計算. SQLite4 從不使用C類型的double或者float(除了在double和內部十進位表示之間轉換時使用語介面常式中). 相反,所有數字值在內部表示方式為一個帶有3位元字的基於10的指數的18位十進位數. 這一表示方式的特性有:
- 即使是在缺乏對於IEEE 754 binary64浮點數字支援的平台上,其運作起來也是可靠和相容的.
- 貨幣計算通常可以精確的進行,不需要四捨五入.
- 任何有符號和無符號的64位整數都能被精確的表示.
- 浮點數範圍和精確度超出了 IEEE 754 binary64 浮點數.
- 正無窮和負無窮以及 NaN (Not-a-Number) 都有良好的表示方式.
SQLite4 使得整型和浮點型數字之間沒有了差別. 不到期卻有精確和近似數之間的分別. 在 C/C++ 中, 整形數是精確的而浮點數是近似的. 但SQLite4並不一定如此. 浮點數字在SQLite4可以是精確的. 可以用64位表示的整型數在SQLite4中總是精確的,而大型的整型數則可能是近似的.
SQLite4 數字格式是面向內部使用的. 數字可以在整型和double型之間轉換,用於輸入和輸出. 磁碟上的儲存空間需要從1到12位的SQLite4數字值, 這取決於其大小和重要的位元.
3.5 外鍵約束和遞迴觸發器是預設開啟的
外鍵約束在SQLite3的早期版本中是沒有的,並且他們預設關閉了向後相容的能力。但是外鍵約束一直是有效,並且在SQLite4中是預設開啟的。所有的外鍵約束都是預設延遲的,儘管他們能夠被定義成立即建立。但是,沒有有效機制去觸發外鍵約束,不管是延遲建立還是立即建立。
SQLite3提供了遞迴觸發器,但是這隻是在一個運行時才有效功能。在SQLite4中,所有的觸發器在所有的時間段裡都是遞迴的。
3.6 明確的索引覆蓋
SQLite4 像 CREATE TABLE 語句中條件了一個可選的子句,該子句定義了在索引中重複的附加列資訊. 這可以讓應用程式開發人員在SQLite4中無需使用任何花招就可以明確地建立覆蓋索引. 例如:
CREATE INDEX cover1 ON table1(a,b) COVERING(c,d);
上面的 cover1 索引可以在儲存引擎中用一個單一的尋找操作來進行形式如 "SELECT c,d FROM table1 WHERE a=?1 AND b=?2" 的查詢. 索引中如果沒有附加的 COVERING 子句, SQLite4 也許會在儲存引擎中做兩次操作; 一次操作基於a和b的值來找到主鍵,而第二次操作則基於主鍵來找到c和d的值. COVERING 子句使得c和d的值在索引中就可用,這意味著它們無需第二次尋找就可以從索引中提取出來.
COVERING 語句上有一個變化:
CREATE INDEX cover2 ON table(x,y) COVERING ALL;
COVERING ALL 語句的意思是資料表的所有列都在索引中被重複一份,這就確保了原來的資料表永遠也不會被訪問到,以完成一個使用了這個索引的查詢. 這種方法的缺點,當然就是資訊重複,而且因此讓資料庫檔案更大. 但是通過在索引指標上明確指定COVERING關閉,SQLite4可以讓應用程式開發人員權衡著從應用程式中騰出空間和時間來讓其更加適用.