按照慣例,我在這里先介紹一下什么是BSon。
BSon是Binary JSON的簡稱,是一種類Json的一種二進制形式的存儲格式。目前Bson主要被MongoDB(目前一個較為流行的非關系數據模型的開源數據庫)使用於存儲數據和網絡數據交換。
下面我們進入正題,介紹一下BSon是怎么把一個個MongoDB的文檔轉換成二進制形式進行存儲的,在此之前讀者需要從BSon官網上大致了解一下其解釋的規則,鏈接會在下面的參考資料中給出。
至於介紹的形式,當然是給出例子,為大家講解每個轉換的步驟。
在介紹轉換步驟之前,讀者需要了解BSon中有四種基本類型
名稱 | 存儲大小 | 說明 |
byte | 1 byte (8-bits) | 最小單位,只占有一個字節,一般對應於一個字符(用ASCII表示) |
int32 | 4 bytes (32-bit signed integer) | 32位整型占用4個字節 |
int64 | 8 bytes (64-bit signed integer) | 64位整型占用8個字節 |
double | 8 bytes (64-bit IEEE 754 floating point) | 雙精度浮點型占8個字節,需要用IEEE 754的浮點型規則進行表示 |
下面給出例子
例1:
1 { 2 "Name": "DataResearchLab", 3 "IsGreat":true, 4 "Feilds":["CloudComputing","NoSQL","BigData"]
5 }
先來說明一下這個例子,這個就是非關系型數據庫中存放數據的形式,在BSon上的解釋就是相當於一個Document。如果要用關系型數據庫進行解釋的話,這個就相當於一個記錄,但是這個記錄有自己的列名。繼續解釋上面的記錄,最外面的"{}"相當於變成語言中"{}" 的作用,即表示一個范圍,也就是說這里面的數據是作為一個整體需要進行存儲的。里面的":"左面可以理解為是鍵值,即一個數據庫中只有唯一的一個鍵值。順便提一下,在BSon所有的鍵值都被視為cstring 類型(BSong存儲的基本類型中的一種)。":"的右面是左面鍵值對應的值,這個值可以有很多形式,如布爾型,字符串,32位整型,浮點型,甚至是數組,JavaScript的代碼,正則表達式或是在嵌套一個Document等等,具體有什么讀者可以查看BSon官網的介紹。
再返回來看這個例子,照之前的解釋,相當於用戶想要在數據庫中存儲一個數據,這個數據包括三個數據項。其中鍵值"Name"對應存儲的數據是字符串型的"DataResearchLab ",鍵值"IsGreat"對應存儲的數據是布爾型數據true,鍵值"Feilds"對應存儲的是一個數組型的數據"["CloudComputing","NoSQL","BigData"]"。
接下來進入正題,看看運用BSon是怎么把該數據轉換成二進制形式在非關系數據庫中存儲的。數據被作為一個Document跟據BSon的規則(具體規則這里不給出,請查看BSon官網的規則說明)需要進一步進行分解為三項int32,e_list, "\x00"。
先來介紹一下int32。int32需要占用四個字節,但是這個有兩點需要特別注意。第一點就是,Document解釋成的int32是用來計算該Document的長度的,但這個長度包含本身自己int32四個字節的長度。以本例來算,總共的Document需要102個字節,這102個字節包括本身int32所占的四個字節和后面所解釋出的所有的所需的字節長度,當然102個字節在一開始是沒法算出來的,只有將該Document都解釋完才會算出這個數據。第二點要說明的是,存儲長度本身占有四個字節,而這四個字節需要高位存儲。至於什么是高位存儲,這里只給一個簡單的解釋,就是四個字節中的最低位字節對應到存儲器的最高位,最高為的字節對應到存儲器的最低位。本例中需要102個字節,對應到十六進制是"\x66\x00\x00\x00",而不是"\x00\x00\x00\x66"(這種存儲形式是低位存儲)。
接下來說一下"\x00"。這個對應過來的十六進制相當於是一個結束符,有點類似於程序設計語言中字符串類型最后的"\0"。表示該部分解釋的結束。
下面介紹關鍵的部分e_list。e_list也只是BSon規則中的一個中間形式,即e_list需要進行進一步的解釋。e_list的進一步解釋成兩部分。第一部分是element,第二部分是e_list或是""。先說一下第二部分,在此時第一步解釋出的e_list需要將第二部分解釋成e_list。為什么呢,因為例子中需要存儲的並不只有"Name"一項,在其后面還有"IsGreat"和"Feilds"需要解釋,換句話說e_list這一步需要進行遞歸,繼續循環解釋。那么什么是后e_list解釋成""呢?當解釋到最后一項"Fields"時,后面沒有需要存儲的數據項時,需要將其第二部分解釋成"",這還需要注意一點的是""並不占用存儲空間,也就是說它是沒有長度的,可以理解為""在BSon的解釋最后的形式中並不存在。
回來說第一部分element。需要說明的是element也是作為一個中間形式需要繼續往下解釋,element也是BSon中最豐富的解釋,也就是說它可以被解釋成很多的形式。而解釋成哪種形式需要針對於存儲數據的類型進行選擇。注意這里存儲數據的類型指的不是鍵值,因為前面已經說過BSon把所有的鍵值只解釋成為cstring一種類型,這里說的存儲數據類型是指與鍵值一一對應的數據的類型。這里"Name"所存儲的類型是BSon基本存儲類型中的"UTF-8 string"。后面"IsGreat"和"Fields"分別對應的是BSon基本存儲類型中的"Boolean "true""和"Array"。
繼續說"Name"部分的解釋。前面一段已經說過其對應的是基本存儲類型中的"UTF-8 string"。那么根據BSon的規則element需要解釋成"\x02",e_name,string三部分。第一部分對應的是最后的存儲形式,它代表了這種選擇的類型,即"\x02"。第二部分e_name需要進一步唯一解釋成cstring,即鍵值的類型。而cstring也需要進行唯一解釋成(byte*)和"\x00"。(byte*)代表存儲的是鍵值本身,即"Name"(這里不包括雙引號"")。而"\x00"和之前的一樣,代表這部分解釋的結束。string跟上,需要唯一解釋成int32,(byte*)和"\x00"三部分。下面來看這三部分。先來解釋第二部分和第三部分,這兩部分的含義和以前出現這兩部分的含義是相似的,也就是說(byte*)代表存儲了"DataResearchLab"(之前代表的是鍵值,這里代表的是數據值)。"\x00"代表了這部分的結束。第一部分這里也有一個int32的類型,但是絕對要注意的是這與Document中解釋出的int32是不一樣的。在Document中解釋出來的int32用加粗字體說了兩個注意事項,這里要說明stirng解釋的int32和Document相同和不相同的地方。string解釋出的int32同樣是進行高位存儲,但是不同點在於算出后面解釋出二進制的長度不包括int32本身的四個字節。這個int32存儲的是"\x10\x00\x00\x00",這包括"DataResearchLab"15個字節和結束符號"\x00"一個字節。
其實,到這里,對於"Name"鍵值這部分的存儲已經介紹完畢,但是對於整個例子的存儲還遠沒有結束。
讀者應該還記得前面提到的e_list,它是遞歸循環的。第二個e_list同樣的被解釋成兩部分,第一部分是element,第二部分是e_list。第一部分的element跟據數據類型是布爾型的"true"對應到BSon的規則解釋成為"\x08",e_name,"\x01"。和之前介紹的這部分的類似"\x08"代表了這種對應關系的類型,而最后的"\x01"則代表為true(如果是false的布爾類型這部分對應的是"\x00")。e_name同樣的被唯一解釋,這里和"Name"部分一樣,不再多說,請讀者自己思考。第二部分是e_list同樣的被解釋成為兩部分element和""(因為后面已經沒有數據項了)。
對於element,由於對應的數據類型是"Array",將其解釋成"\x04",e_name和Document。"\x04"和e_name的分析也請讀者按照之前的講述自己進行分析。對於后面出現的Document,完全是類似於例子最開始部分的那個Document進行解釋,讀者可以自行分析。但是這里出現了一個問題,因為數組中的數據項並沒有相應的鍵值與其對應。其實不然,BSon是將數組元素的下標(從0開始)作為其鍵值的。這部分就轉換成一個嵌套的Document,形式如下
1 { 2 "0":"CloudComputing", 3 "1":"NoSQL", 4 "2":"BigData" 5 }
接下來的步驟請讀者自己分析。需要注意的一點是BSon對於數據的順序是有嚴格區分的,數組中的順序是有區別的。
最后這里給出本例子的解釋過程和最后解釋的二進制形式如下圖所示,希望讀者可以自己分析完后對照一下。
由於本人也是初步接觸這部分內容,歡迎各位讀者一塊進行討論。
另外,還要感謝和我一起學習這方面知識的同學們以及老師。
參考資料:
BSon官網解釋規則:http://bsonspec.org/#/specification