後端的輪子(二)— 資料庫

吳YH堅發表於2019-03-02

本篇趟個雷,把資料庫納入到輪子中了,前面說到了資料庫其實不算輪子,也說到了其實我寫不出來資料庫,這裡所說的資料庫嚴格來說是關係型資料庫,他比輪子複雜多了,是一個和作業系統差不多複雜度的東西,所以才能通過一個oralce養活一家全球50強的公司,其次,資料庫太複雜了,要寫出來實在是力所不能及,但是後來有想了一下,如果我們從另外一個角度來審視資料庫,那麼也有比較容易的實現辦法,那麼,這一篇,我們來造個資料庫吧,看吧,把關係型去掉了,因為,有了關係型幾個字,資料庫就變得複雜多了。

先來聊聊關係型資料庫

關係型資料庫(Relational Database)是一個偉大的發明,一般的資料庫的理論,大概會分成以下三個部分。

  • 首先,資料庫是建立在關係模型基礎上的,並且從理論上來講,是有完備的數學模型,也就是集合代數來做支撐的,他把我們真實世界中的聯絡和實體抽象成了關係模型,並用這個發展出了資料庫理論,這是資料庫的理論基礎。
  • 其次,也有人通過這個關係模型,發明了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語句,輸出是資料。

UI層(使用者介面層)

對於使用者的介面層就更加簡單了,我們只需要提供一個TCP服務就行了,用;分號來分割每次使用者的輸入,也就是說,我們telnet上我們這個資料庫,然後輸入sql,資料庫就會返回資料了。

具體實現

我在github上建立了一個新的工程叫SparrowSys,麻雀工程,意思很明顯,這是一個後端的麻雀,是最簡單的後端輪子,目前我也已經提交了一部分程式碼,資料庫的還沒有寫完,後面會補上的。

資料庫的部分在src下的SparrowDB裡面,很明顯的看到裡面有DataLayer,EngineLayer,NetLayer,對應的就是上面的三層,每層裡面有一到兩個檔案,都很簡單,目前DataLayer基本完成了,後面會把EngineLayer和NetLayer補上,後面的文章會說說使用,utils資料夾中是一些公共的東西,後面的其他輪子會用到的,比如B+樹就在utils裡面。

目前這個工程裡面東西不多,不建議看,後面我補全以後會說明,歡迎大家提交你的實現來代替我的。接受任何pull request

十天沒有更新了,主要是程式碼沒時間寫,所以沒有測試結果可看,本來準備等程式碼都寫完了再來更新文章,但最近實在是太忙了,沒時間寫程式碼,那先放出文章,等程式碼補充完整了再說說測試效果吧。

程式碼地址:github.com/wyh267/Spar…

B+樹文章


如果你覺得不錯,歡迎轉發給更多人看到,也歡迎關注我的公眾號,主要聊聊搜尋,推薦,廣告技術,還有瞎扯。。文章會在這裡首先發出來:)掃描或者搜尋微訊號XJJ267或者搜尋西加加語言就行

相關文章