本篇趟個雷,把數據庫歸入到輪子中了,前面說到了數據庫
其實不算輪子,也說到了其實我寫不出來數據庫,這裏所說的數據庫嚴格來講是關係型數據庫
,他比輪子複雜多了,是一個和操做系統差很少複雜度的東西,因此才能經過一個oralce養活一家全球50強的公司,其次,數據庫太複雜了,要寫出來實在是力所不能及,可是後來有想了一下,若是咱們從另一個角度來審視數據庫,那麼也有比較容易的實現辦法,那麼,這一篇,咱們來造個數據庫吧,看吧,把關係型
去掉了,由於,有了關係型
幾個字,數據庫就變得複雜多了。git
關係型數據庫(Relational Database)是一個偉大的發明,通常的數據庫的理論,大概會分紅如下三個部分。github
首先,數據庫是創建在關係模型基礎上的,而且從理論上來說,是有完備的數學模型,也就是集合代數
來作支撐的,他把咱們真實世界中的聯繫和實體抽象成了關係模型
,並用這個發展出了數據庫理論,這是數據庫的理論基礎。算法
其次,也有人經過這個關係模型,發明了SQL這種進行關係查詢的編程語言,用來對這個關係型的數據集合進行操做。這個實際上給出了經過集合代數發展出來的關係型數據庫怎麼進行數據操做和檢索的。sql
還有人,發展出了數據庫設計的理論,也就是你們所熟悉的數據庫三大範式【應該是5大範式】,用來教咱們在實際場景中怎麼設計一個數據庫,幾大範式其實是把關係模型
這個抽象的概念變成了幾條規則,按照這幾條規則去設計數據庫,就能產生最少的數據冗餘,最能體現出關係
這個模型的核心。數據庫
咱們發現,上面三個大的部分都是數據庫的理論知識,其實並無人告訴咱們怎麼來用代碼實現一個數據庫,由於科學家們認爲實現它並不重要,那是工程師要考慮的事情,Too Simple,科學家只負責搞出理論,反正咱們也不是科學家,那麼咱們就來作作工程師吧。編程
既然是工程師,首先想到的就是如何來實現一個數據庫了,一個標準的數據庫大概主要會包含如下幾個大的模塊。後端
底層的存儲層,這個是必不可少的,他是整個數據庫的核心數據結構,也就是數據是如何保存的,通常提供最簡單的原子增刪改查。緩存
存儲層上面就是引擎層了,這裏會對底層的存儲層進行各類組合型的操做用來知足查詢的需求之類的,並且數據庫的事務支持也在這一層,咱們熟悉的innoDB
就是一個數據庫的存儲引擎,他其實包含的就是這個引擎層和存儲層了,引擎層提供對數據層的操做方法集合。微信
在引擎層之上還有個SQL的解析層,主要用來對SQL語句進行解析,分析,優化了,而後把SQL語句轉化成引擎層的接口,進行具體的數據操做。網絡
最上面就是對外的UI了,也就是用戶交互層了,通常咱們熟悉的就是網絡交互了。
雖然看起來好像挺簡單,就是這麼三層,可是實際的數據庫是很是很是複雜的,除了這些之外還有不少其餘模塊,好比用戶權限管理,緩存模塊,日誌模塊,備份模塊等等等等
,你們能夠仔細去看看innoDB
的書籍或者innoDB
的代碼,光一個binlog
就特別麻煩。
其實要保存數據,搜索系統也能保存數據,並且檢索起來更快,而且二者的底層數據結構其實差異不是很大,但爲何用數據庫呢?由於數據庫的核心是可靠
,這個可靠
就是考數據庫的引擎層來保證的,完整的binlog記錄,崩潰後完整的重放機制,數據雙寫,內存數據定時刷新到磁盤,全部的這些都是爲了保證數據的可靠
,不會丟失數據。
而上面說的每個功能,都能單獨的寫一篇長文,因此說要實現一個數據庫實際上是很麻煩的,由於爲了作到可靠
,必然會有不少冗餘的數據或者冗餘的操做來保證可靠
,但做爲一個成熟的產品,還須要考慮到產品的性能
,因此,如何既可靠
又性能
優良,就變成了一個衡量數據庫好壞的標準,固然,在這兩點上,目前沒人能幹過oracle了。
數據庫如此之複雜,咱們如何對他進行瘦身 ,來實現一個最小的數據庫系統呢?咱們能夠從另一個角度想一想,就是咱們拿數據庫是幹什麼的?那就是存儲和查詢數據
,若是這麼來想的話,就能簡單很多。
首先,咱們知道數據庫最重要的功能就是存儲數據,那麼底層的存儲部分是不能少的,其次,存儲的數據要提供查詢功能,否則存了就沒意義了,這也是不能少的,第三,須要提供一個對外的接口能夠和用戶交互,否則就既不能存也不能查了。
因此,一個最最基本的數據庫至少應該包含數據層,查詢層(引擎層)和UI(用戶接口)層三層,那麼咱們就用幾個簡單的文件來實現這三層,完成一個最小的數據庫吧。
數據庫的基本單位是列
,再上一級的基本單位就是表
了,並且咱們在建表的時候都會指定列的名稱
,類型
,長度
這三個最基本的屬性,若是全部列都有這三個屬性,那麼其實咱們是知道每一行數據最多有多少字節的,因此,咱們能夠設定沒一行數據的長度都是定長的,那麼整個表的長度也是定長的了,這樣查詢的時候能夠根據行的長度進行快速定位數據,因此,咱們的最底層數據就是一個定長的表格了,每一列存儲的時候就像下面這樣,而後有個meta信息來存儲列的屬性
這個看上去很簡單吧?也容易實現吧,其實不少數據庫也基本上確實是這麼實現的,並不難理解吧?稍微注意一下的是每一列存儲的時候,每一個字段的前四個字節保存的是這個字段的實際長度,而後纔是字段的實際內容,若是長度小於建表時的設定長度,那麼有一部分空間是浪費掉的,雖然是浪費了,但仍是值得的,由於可讓查詢的時候省很多事。
這麼下來,每行記錄就是一個定長的,而一個數據庫的表就是一個二進制文件了,但僅僅是這樣仍是不夠的,由於這樣結構,不管什麼查詢都須要掃描全表,依次進行判斷,而咱們在建表的時候都會創建索引,爲了創建索引,咱們還得實現一個B+樹
來存儲索引,而B+樹
基本上是全部數據庫的索引保存的數據結構,這裏咱們也有實現,若是對B+樹
感興趣,能夠看我以前的一篇文章,那篇有詳細的B+樹的實現方式,文章後有那篇文章的連接。
總之,數據底層咱們就用了一個定長的二進制文件和幾棵B+樹,再加上一個meta信息文件來實現了一個數據庫的底層數據層,很簡單哈,但基本上包括了數據庫真實的底層,雖然真正的數據庫比這複雜多了,但也跑不掉這幾個數據結構,整個看下來,數據層的數據結構大致上長這樣子。
固然,數據層實現完了之後,還須要對上提供幾個簡單的接口,好比
建表接口
CreateTable( []FieldInfo ),參數是每一個字段的信息,包括字段的名稱,長度,類型
數據插入接口
AddData(map[string]string) ,參數是一個map,key是字段名稱,value是字段內容
單字段查詢接口
Find(fieldname,fieldvalue,op),參數是字段名稱,字段值,操做類型(大於,小於,等於)
數據獲取接口
GetData(docid),參數是docid,用來計算在文件中的偏移
底層已經有了,接下來就是上面的查詢層(引擎層)了,這裏我沒用引擎
兩個字,是由於最小數據庫的實現上,實在算不上一個引擎系統,咱們實現最簡單的基本查詢SQL(建表sql,插入數據sql,單表查詢sql
)的解析,在實際中,SQL的解析是一個異常複雜的工程,涉及到語法分析,預處理,優化查詢等幾個大的部分,由於SQL實際上是一門編程語言,要解析一門編程語言,那麼編譯原理那一套基本上都會用獲得。
這裏咱們換條路子,由於只實現三種簡單的SQL語句,那麼咱們直接用正則和字符串的匹配來對SQL進行解析,解析完成之後變成一個個數據層的對外接口,建表和插入數據都比較簡單,解析了SQL之後直接調用上面的第一
和第二接口
就好了。
數據查詢的時候,對查詢SQL的WHERE
以後的部分,用了個小算法,就是逆波蘭表達式來對WHERE
以後的語句進行解析,變成一個棧結構來存儲查詢的內容,而後經過彈棧的方式一個一個調用接口三
,而且對結果進行求交和求並的操做,最後獲得結果之後,再依次調用接口四
獲取最後的結果,若是對逆波蘭表達式不瞭解,那麼請自行百度一下,很簡單的,主要用在對四則運算的優先級的解析中。
查詢層的輸入輸出很簡單,他對外實際上只提供一個接口。ExecSqlSentence( Sql ) string
,都是字符串,輸入是一條條的sql語句,輸出是數據。
對於用戶的接口層就更加簡單了,咱們只須要提供一個TCP服務就好了,用;分號
來分割每次用戶的輸入,也就是說,咱們telnet上咱們這個數據庫,而後輸入sql,數據庫就會返回數據了。
我在github上創建了一個新的工程叫SparrowSys
,麻雀工程,意思很明顯,這是一個後端的麻雀,是最簡單的後端輪子,目前我也已經提交了一部分代碼,數據庫的尚未寫完,後面會補上的。
數據庫的部分在src
下的SparrowDB
裏面,很明顯的看到裏面有DataLayer
,EngineLayer
,NetLayer
,對應的就是上面的三層,每層裏面有一到兩個文件,都很簡單,目前DataLayer基本完成了,後面會把EngineLayer和NetLayer補上,後面的文章會說說使用,utils
文件夾中是一些公共的東西,後面的其餘輪子會用到的,好比B+樹
就在utils
裏面。
目前這個工程裏面東西很少,不建議看,後面我補全之後會說明,歡迎你們提交你的實現來代替個人。接受任何pull request
。
十天沒有更新了,主要是代碼沒時間寫,因此沒有測試結果可看,原本準備等代碼都寫完了再來更新文章,但最近實在是太忙了,沒時間寫代碼,那先放出文章,等代碼補充完整了再說說測試效果吧。
代碼地址:https://github.com/wyh267/SparrowSys
若是你以爲不錯,歡迎轉發給更多人看到,也歡迎關注個人公衆號,主要聊聊搜索,推薦,廣告技術,還有瞎扯。。文章會在這裏首先發出來:)掃描或者搜索微信號XJJ267
或者搜索西加加語言
就行