世界上最流行的數(shù)據(jù)庫(kù),竟然是套殼的 !
世界上最流行的數(shù)據(jù)庫(kù)是什么?
Oracle? MySQL? PostGreSQL?
都不是,答案是SQLite。
你可能沒(méi)聽(tīng)說(shuō)過(guò)它,但是它就在你身邊的:
每一臺(tái)智能手機(jī)中(Android 和iOS)
每一臺(tái)Mac電腦中
每一臺(tái)Windows 10 電腦中
每一個(gè)主要的瀏覽器中(Chrome, Firefox,Safari)
大部分的機(jī)頂盒當(dāng)中
每個(gè)PHP和Python安裝目錄中
很多流行的桌面應(yīng)用(微信、QQ、 DropBox、 Skype、iMessage、WhatsApp、 Adobe Acrobat Reader....)
......
不信的話可以在電腦中搜索一下 “*.db”,看看能發(fā)現(xiàn)多少個(gè)。
每個(gè)流行的軟件都是為了解決一個(gè)痛點(diǎn)問(wèn)題,SQLite也不例外。
時(shí)光回到2000年,Richard Hipp為美國(guó)海軍的驅(qū)逐艦開(kāi)發(fā)軟件,這個(gè)軟件要對(duì)船上所有的閥門進(jìn)行管理和操作。
當(dāng)時(shí),美國(guó)海軍使用的是IBM的Informix數(shù)據(jù)庫(kù),這個(gè)軟件需要通過(guò)網(wǎng)絡(luò)訪問(wèn)它來(lái)讀取數(shù)據(jù)。
但是有時(shí)候Informix所在的服務(wù)器會(huì)掛掉,閥門管理軟件就會(huì)報(bào)錯(cuò):不能連接到服務(wù)器!
更糟糕的是,他們對(duì)這個(gè)數(shù)據(jù)庫(kù)服務(wù)器沒(méi)有任何控制權(quán),結(jié)果不出意外,誰(shuí)彈的報(bào)錯(cuò)對(duì)話框,誰(shuí)背鍋。
為了解決這個(gè)問(wèn)題,Richard團(tuán)隊(duì)想了一招:將所有數(shù)據(jù)讀入內(nèi)存!
好在他們的程序不需要增、刪、改數(shù)據(jù),只要將數(shù)據(jù)全部讀入內(nèi)存,就萬(wàn)事大吉了。
此時(shí),Richard腦中閃現(xiàn)出一個(gè)大膽的想法:既然如此,還要啥Informix呢?
直接從本地硬盤讀取數(shù)據(jù)就得了!這樣只要計(jì)算機(jī)能正常工作,程序就能正常運(yùn)行。
這其實(shí)就是嵌入式數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)和用戶程序位于同一臺(tái)機(jī)器的同一個(gè)進(jìn)程當(dāng)中。
圖片
不過(guò)當(dāng)時(shí)還沒(méi)有這樣的數(shù)據(jù)庫(kù),這時(shí),一位同事提醒Richard:“那你為什么不自己寫一個(gè)呢?”
當(dāng)時(shí)紐特·金里奇和比爾·克林頓正在“打架”,所有政府合同都暫停執(zhí)行,所以Richard失業(yè)了幾個(gè)月。
沒(méi)事兒可干的Richard決定把這個(gè)嵌入式數(shù)據(jù)庫(kù)給寫出來(lái)。
于是,SQLite誕生了!
SQLite第一版的提交歷史記錄
SQLite第一版其實(shí)也是個(gè)套殼數(shù)據(jù)庫(kù)
就像今天TiDB使用RocksDB作為其存儲(chǔ)引擎,SQLite的第一版也只不過(guò)是在GDBM——可謂是NoSQL的鼻祖——上套了個(gè)殼。SQLite第一版的README文件中這樣寫道:
SQLite: An SQL Database Built Upon GDBM
GDBM衍生自DBM,而DBM(DataBase Manager的縮寫)是來(lái)自Unix的鍵值數(shù)據(jù)庫(kù)(key-valuedatabase),支持通過(guò)主鍵快速訪問(wèn)數(shù)據(jù),最初由計(jì)算機(jī)界的大佬Ken Thompson編寫。
所以,我們可以把GDBM的數(shù)據(jù)庫(kù)看作是存儲(chǔ)在硬盤上的哈希表。
GDBM僅僅以代碼庫(kù)(library)的形式向用戶提供一系列API,并不支持SQL語(yǔ)句。
雖然是套殼數(shù)據(jù)庫(kù),但這個(gè)殼套得可不簡(jiǎn)單。
上世紀(jì)90年代,編程語(yǔ)言界誕生了不少基于字節(jié)碼的語(yǔ)言,也因此出現(xiàn)了各種虛擬機(jī)。
例如,Java有JVM,PHP有Zend Engine,Python和Ruby也有自己的虛擬機(jī)。
擁有豐富編譯器經(jīng)驗(yàn)的Richard也如法炮制,他也搞了虛擬機(jī),將SQL語(yǔ)句轉(zhuǎn)化為字節(jié)碼,在虛擬機(jī)上執(zhí)行。
下面我們就來(lái)梳理一下SQLite第一版的架構(gòu),看看它是怎么套殼GDBM的。
SQLite第一版的架構(gòu)
SQLite采用了典型的分層架構(gòu),一條SQL語(yǔ)句依次經(jīng)過(guò)
- 用戶接口(UI)層
- 詞法分析器(Tokenizer)層
- 語(yǔ)法解析器(Parser)層
- 字節(jié)碼生成器(Code Generator)層
- 虛擬機(jī)(虛擬數(shù)據(jù)庫(kù)引擎〔Virtual DataBase Engine〕)層
- 數(shù)據(jù)庫(kù)后端(DataBase BackEnd〔DBBE〕)層
最終到達(dá)GDBM數(shù)據(jù)庫(kù)層,變?yōu)閷?duì)GDBM數(shù)據(jù)庫(kù)的CRUD(通過(guò)調(diào)用GDBM提供的API)。
這些層在下圖中都用方框表示,方框內(nèi)寫有該層的名稱,名稱下方括號(hào)內(nèi)是實(shí)現(xiàn)層的源代碼文件。
SQLite第一版的架構(gòu)
以圖中的INSERT INTO語(yǔ)句為例,首先,UI層將用戶輸入的這條SQL語(yǔ)句傳遞給Tokenizer(詞法分析器)。
Tokenizer會(huì)將一條SQL語(yǔ)句分割為Token的序列(通過(guò)sqliteGetToken()函數(shù)),類似[INSERT,INTO, examp, VALUES, (, 'Hello, World!'...]。
接下來(lái)由Parser(語(yǔ)法分析器)根據(jù)語(yǔ)法規(guī)則(如圖中的cmd::= INSERT INTO...)解析來(lái)自Tokenizer的Token的序列,并調(diào)用與匹配的語(yǔ)法規(guī)則對(duì)應(yīng)的處理函數(shù),這里是INSERT語(yǔ)句的語(yǔ)法規(guī)則對(duì)應(yīng)的處理函數(shù)sqliteInsert()。
值得一提的是,SQLite第一版中的詞法分析器和語(yǔ)法分析器(叫做Lemon)都是Richard自己編寫的,他從一開(kāi)始就沒(méi)有采用lex和yacc等成熟的現(xiàn)成工具,這或許是源于他對(duì)自由的極度渴望。
文章最后我們會(huì)聊聊Richard在軟件世界中的生存態(tài)度,到時(shí)候就更能理解Richard為什么要重復(fù)造輪子了。
回到架構(gòu)圖中,在語(yǔ)法規(guī)則處理函數(shù)sqliteInsert()中(位于字節(jié)碼生成器〔Code Generator〕層),SQL語(yǔ)句被翻譯(通過(guò)sqliteVdbeAddOp()函數(shù))成了如下圖左側(cè)黃色方框中的字節(jié)碼(OpCode)(我這里進(jìn)行了簡(jiǎn)化,實(shí)際的字節(jié)碼要比這里的復(fù)雜一些)。
可以看到,此時(shí)一條INSERT語(yǔ)句被分解成為若干簡(jiǎn)單的指令:
Open examp——打開(kāi)名為examp的數(shù)據(jù)表;
Integer 99——添加一個(gè)整型的數(shù)據(jù)項(xiàng),值為99;
Put——向數(shù)據(jù)表中寫入記錄……
SQLite第一版的架構(gòu)(續(xù))
當(dāng)虛擬機(jī)(也叫VDBE,Virtual DataBaseEngine,虛擬數(shù)據(jù)庫(kù)引擎)接收到這一串指令后,就會(huì)根據(jù)指令的類型(是Open、是Put,還是……)調(diào)用對(duì)應(yīng)的函數(shù)。
如對(duì)于Put這個(gè)指令,就需要調(diào)用sqliteDbbePut()來(lái)處理。
現(xiàn)在處理流程終于進(jìn)入到了DBBE(DataBase BackEnd,數(shù)據(jù)庫(kù)后端)層,這里才是最貼近GDBM的那層殼。
既然Put指令(對(duì)應(yīng)sqliteDbbePut()函數(shù))要插入記錄,那就調(diào)用gdbm_store()這個(gè)GDBM的API完成數(shù)據(jù)寫入。
至此,SQLite第一版就處理完了一條INSERT語(yǔ)句,向examp表中插入了一條新紀(jì)錄("Hello,World!", 99)。
從設(shè)計(jì)模式的角度看,DBBE層相當(dāng)于接口,而GDBM相當(dāng)于實(shí)現(xiàn)類;從MySQL架構(gòu)的角度看,DBBE層相當(dāng)于MySQLServer層,而GDBM相當(dāng)于MyISAM或InnoDB,是一種Pluggable Storage Engine。
也許Richard一開(kāi)始就想到了不能在GDBM一棵樹(shù)上吊死,底層的存儲(chǔ)引擎應(yīng)該是可以替換的。
果然,到了2001年,Richard就決定干掉GDBM了。
他從書架上取出高德納(Donald Knuth)的巨著The Art ofComputer Programming(《計(jì)算機(jī)程序設(shè)計(jì)藝術(shù)》),用B樹(shù)替代掉了GDBM。
用B樹(shù)替代GDBM的提交記錄
B樹(shù)替代GDBM后,README文件的diff
“末日準(zhǔn)備者”Richard
最后我們來(lái)聊一聊Richard這個(gè)人。
世界上有這樣一類人,他們?cè)谝恍K土地上自己種植食物,生活完全靠自給自足,甚至不使用自來(lái)水、電、天然氣等公共設(shè)施。
人們稱他們?yōu)樯嬷髁x者(survivalist)或末日準(zhǔn)備者(preppers)。
而Richard仿佛也是他們之中的一員,時(shí)刻都在為軟件世界的“末日”做著準(zhǔn)備:
要是現(xiàn)有的語(yǔ)法分析器不能滿足我的需求怎么辦?——那我自己寫一個(gè)吧——于是有了Lemon。
要是版本控制系統(tǒng)不好用怎么辦?——那我自己寫一個(gè)吧——于是有了Fossil。
要是文本編輯器……——那我自己寫一個(gè)吧。
在一檔名為FLOSS Weekly的訪談節(jié)目的結(jié)尾(我搬運(yùn)到了B站??https://www.bilibili.com/video/BV1Hj411W7xc/?t=2944.1)
主持人問(wèn)了Richard一個(gè)例行問(wèn)題:“你最喜歡的編輯器是?”
“當(dāng)然是我自己寫的那個(gè),哈哈哈”
為了管理SQLite的代碼迭代,Richard還自己造了個(gè)叫做Fossil的版本控制系統(tǒng)。
更有意思的是,SQLite的源代碼使用Fossil來(lái)管理,而Fossil又依賴SQLite來(lái)存儲(chǔ)代碼倉(cāng)庫(kù)的信息,這就產(chǎn)生了一個(gè)在后人看來(lái)類似先有雞還是先有蛋的問(wèn)題。
毫不夸張地說(shuō),除了C編譯器和libc中的一些東西之外,SQLite基本上只依賴Richard自己構(gòu)建的軟件。
而這正是Richard所要的自由——自己編寫的組件越多,就越自由,就越少受到束縛。
日本動(dòng)畫片《新世紀(jì)福音戰(zhàn)士EVA》電視版的第26集也有一段對(duì)“自由”的描述(??https://www.bilibili.com/video/BV1yK411b7AT/),看過(guò)后可能會(huì)覺(jué)得絕對(duì)的自由會(huì)讓人感到不安、寂寞、迷茫。
而Richard卻堅(jiān)定地認(rèn)為,如果想要自由,就得自己動(dòng)手。如果有人跟你說(shuō)“讓我們?yōu)槟憬鉀Q問(wèn)題”,那你就要有所警惕,因?yàn)樗麄冋嬲胝f(shuō)的是,“我們將會(huì)剝奪你的一些自由”。
Richard不想讓別人控制自己的命運(yùn),他要牢牢掌握自己的命運(yùn),縱使會(huì)付出很多努力。
(完)
本文來(lái)自“胡譯胡說(shuō)” 的投稿。
如需轉(zhuǎn)載,請(qǐng)通過(guò)作者微信公眾號(hào)coderising獲取授權(quán)。