如何高效地將SQL資料對映到NoSQL儲存系統中
通常來說,我們都知道:
- SQL資料庫只限在單機上執行,但它提供了更強的事務管理、schema與查詢功能。
- NoSQL資料庫為了伸縮性與容錯性的目的,放棄了事務管理與schema。
而FoundationDB的SQL層結合了這兩個方面:它首先是一個開源的SQL資料庫,能夠線性地伸縮與提升容錯性,並且還具有真正的ACID事務功能。曾經互不相容的兩種特性,現在已融合在一個統一的系統中。
對於處於以下幾種情況的公司來說,這一特性是非常重要的:
- 新的專案要為大規模的伸縮性進行計劃。
- 現有的專案遇到了資料庫伸縮性的瓶頸。
- 現有的許多專案希望能用一個唯一的、容錯性強的資料庫抽象層統一工作模式。
在本文中,我將為讀者介紹FoundationDB,並解釋FoundationDB的SQL層是怎樣將SQL資料對映到FoundationDB中的鍵-值儲存後臺系統中的。
NoSQL資料庫 ——FoundationDB的鍵-值儲存系統
FoundationDB是一個分散式的鍵-值儲存系統,支援全域性ACID事務操作,並且效能出眾。在安裝系統時,可以指定資料分發的級別。資料分發為容錯性提供了支援:當某個伺服器或網路的某部分產生故障時,資料庫仍然可以正常操作,你的應用也不會受到影響。
鍵-值與SQL架構
我們開發的這套架構能夠在鍵-值儲存系統上支援多個層,每個層都能夠在FoundationDB的基礎上提供一套不同的資料模型,例如SQL資料庫、文件資料庫或圖形資料庫。許多使用者也自行建立了自定義的層。
下圖中列出架構中的了關鍵部分。處於最底層的是FoundationDB叢集,無論叢集的實際大小如何,對它的操作與一個單獨的邏輯資料庫並沒有分別。SQL層則以一種無狀態的中間層方式執行在鍵-值儲存系統之上。這一層通過SQL與應用程式進行通訊,並使用FoundationDB的客戶端API與鍵-值儲存系統進行通訊。由於SQL層是無狀態的,因此可以並行地執行任意資料的SQL層。
SQL層為鍵-值儲存系統帶來了如Google的F1般的能力
SQL層是對SQL與鍵-值儲存API進行轉換的一套邏輯嚴密的層。首先,SQL層會從一條SQL語句開始,將其轉換為最高效地鍵-值操作。這種方式類似於編譯器將程式碼轉換為低階別的執行格式。並且,這種轉換是完全符合ANSI SQL 92標準的。開發者可以將該功能與ORM、REST API進行接合,或者直接使用SQL層的命令列介面進行呼叫。從程式碼的角度來說,SQL層與鍵-值儲存是完全分離的,它是通過FoundationDB的Java繫結方式與鍵-值儲存進行通訊的。感興趣的讀者可以檢視FoundationDB的SQL層在GitHub上的程式碼庫,其程式碼是完全開源的。眼下唯一能夠和這套系統進行比較的是Google的F1,後者是一套基於該公司的Spanner技術所建立的SQL引擎。
如以下的簡單圖例所示,SQL層是由一系列元件所組成的。應用程式通過某種受支援的SQL客戶端向SQL層傳送查詢語句,在解析之後轉換為一棵計劃節點樹。優化器(Optimizer)會計算最佳的執行計劃,並以一棵操作符樹的方式表現出來,隨後由執行框架(Execution Framework)執行。在執行階段,對資料的請求將被髮送到儲存虛擬(Storage Abstraction)層,這一層通過使用Java的鍵-值API在資料與FoundationDB叢集之間進行傳輸。資料庫模型將存放在Information Schema層中,這一層將被其它多個元件所呼叫。
將SQL資料對映到鍵-值儲存系統
SQL層需要管理兩種型別的資料,首先是資訊Schema的後設資料,它負責描述所建立的表與可用的索引。其次,它還需要儲存實際的資料,包括表內容、索引及序列。我們首先來描述一下這些資料是如何儲存在鍵-值儲存系統中的。
本質上講,每個鍵都是對應了某張表中的特定行的指標,而值則包含了該行的資料。鍵的分配是由Table-Group所決定的,它是包含了一個或多個表的組。稍後會對這個概念的細節進行更深入的講解。SQL層會通過使用鍵-值儲存目錄層為每個Table-Group建立一個目錄,儲存目錄層是為使用者管理鍵空間的一個工具,它為每個獨立的目錄分配一個簡短的位元組陣列,作為該目錄的唯一鍵。同時,它也維護著其它後設資料,以實現通過名稱進行查詢的功能。
下面這個例子演示瞭如何建立目錄的對映,通過以下語句分配鍵。
CREATE TABLE schema_a.table1(id INT PRIMARY KEY, c CHAR(10)); CREATE TABLE schema_a.table2(id INT PRIMARY KEY);
在鍵-值儲存系統中有一些預定義的目錄:
Directory |
Tuple |
Raw Key |
sql/ | (9) | \x15\x09 |
sql/data/ | (3) | \x15\x03 |
sql/data/table/ | (31) | \x15\x1F |
sql/data/table/schema_a/table1/ | (215) | \x15\xD7 |
sql/data/table/schema_a/table2/ | (247) | \x15\xF7 |
在儲存資料時,可以選擇使用以下三種格式中的一種:“元組(Tuple)”、“原始資料(Row_Data)”或者是“Protobuf”。如果使用預設的Tuple儲存格式,那麼每一行內容都將儲存為一個單獨的鍵-值對,鍵是通過連線以下字串所生成的元組:目錄字首、該表在Table-Group中的位置,以及主鍵。而值的內容則是由該行中的所有列所組成的一個元組。
舉例來說,以下程式碼對之前建立的表進行操作,產生對應的鍵與值。
INSERT INTO schema_a.table1 VALUES (1, 'hello'), (2, 'world'); INSERT INTO schema_a.table2 VALUES (5);
Raw Key |
Tuple Key |
Raw Value |
Tuple Value |
\x15\xD7\x15\x01\x15\x01 | (215, 1, 1) | \x15\x01\x02hello\x00 | (1, ‘hello’) |
\x15\xD7\x15\x01\x15\x02 | (215, 1, 2) | \x15\x02\x02world\x00 | (2, ‘world’) |
\x15\xF7\x15\x01\x15\x05 | (247, 1, 5) | \x15\x05 | (5) |
瞭解了鍵-值儲存系統中鍵的結構之後,你就能夠從儲存系統中直接讀取資料了。我們將使用FoundationDB的Python API來演示這一功能。在SQL層中,鍵與值是通過“.pack()”方法進行編碼,並通過“.unpack()”方法進行解碼的。下面的示例為你演示如何獲取並解碼資料。
import fdb fdb.api_version(200) db = fdb.open() directory = fdb.directory.open(db,('sql','data','table','schema_a','table1')) for key, value in db[directory.range()]: print fdb.tuple.unpack(key), ' --> ', fdb.tuple.unpack(value)
以上程式碼會輸出類似下面的結果:
(215, 1, 1) --> (1, u'hello') (215, 1, 2) --> (2, u'world')
現在讓我們再來近距離觀察一下Table-Group。每個獨立的表都屬於一個單獨的組,如果某張額外的表能夠建立一個對第一張表的“組外來鍵”引用,那麼它也能夠加入到同一個組中。當我們為某張表建立組外來鍵時,字表將與父表所在的目錄進行互動。字表將成為Table-Group的一部分,在源表之後進行命名。這兩張表的資料在將同一個目錄中進行互動,這保證了範圍掃描的高速,並且在Table-Group之內訪問物件及表連線的開銷極小。為了演示這一特性,我們將繼續之前的示例,這一次的SQL語句如下:
CREATE TABLE schema_a.table3(id INT PRIMARY KEY, id_1 INT, GROUPING FOREIGN KEY (id_1) REFERENCES schema_a.table1(id)); INSERT INTO schema_a.table3 VALUES (100, 2), (200, 2), (300, 1);
該語句將返回以下結果:
directory = fdb.directory.open(db,('sql','data','table','schema_a','table1')) for key, value in db[directory.range()]: print fdb.tuple.unpack(key), ' --> ', fdb.tuple.unpack(value) (215, 1, 1) --> (1, u'hello') (215, 1, 1, 2, 300) --> (300, 1) (215, 1, 2) --> (2, u'world') (215, 1, 2, 2, 100) --> (100, 2) (215, 1, 2, 2, 200) --> (200, 2)
由於第三張表的鍵都處於第一張表中各行的名稱空間範圍內,因此第三張表中所有插入的行都能夠與第一張表的行相關聯。鍵中的兩個額外的值分別對應了Table-Group中的位置以及第三張表中的主鍵。對錶1與表3通過引用鍵進行連線也無需通過標準的連線操作實現,直接通過線性掃描就語句了。這種排序方式比起傳統的關係型資料庫系統有著極大的優勢。
由於鍵都已經經過排序,因此索引可以直接利用這一點所帶來的便利性。所有的表索引只包含一個鍵值,其中包括兩部分內容。每個索引都建立於該表所屬的目錄之下,一個名為index的子目錄中,這是該鍵元組的第一部分內容。第二個部分是一個組合,首先是該索引所對應的各個列的值,之後則是指定這一行所必須的列的值。
舉例來說,我們可以為這張表的c列建立一個索引。
CREATE INDEX index_on_c ON schema_a.table1(c) STORAGE_FORMAT tuple;
接下來使用Python讀取這個索引的內容,我們需要在Python直譯器中加入以下內容:
directory = fdb.directory.open(db, ('sql', 'data', 'table', 'schema_a', 'table1', 'index_on_c')) for key, value in db[directory.range()]: print fdb.tuple.unpack(key), ' --> ', fdb.tuple.unpack(value)
這段程式碼會輸入類似於下圖中的內容,顯示了鍵的兩個組成部分:即該索引所在的目錄的位元組值,以及建立索引的c列的值加上主鍵的值。最後一個部分將被索引的值連結到某個特定的行,而該索引鍵所對應的值為空。
(20127, u'hello', 1) --> () (20127, u'world', 2) --> ()
如果要對SQL層的行為進行更多的控制調整,可以使用以下三種儲存格式:一是之前描述過的元組格式,一是列鍵格式,以及protobuf格式。列健格式會為某一行的每個列值建立一個獨立的鍵-值對。而protobuf儲存格式為會每一行建立一個protobuf訊息。
接下來還需要對後設資料進行儲存與組織。SQL層使用protobuf訊息與基於SQL的資料的結構進行通訊。這個結構是由schema、組、表、列、索引與外來鍵等物件共同組成的。
SQL與NoSQL的混合模式
如果在應用程式級別使用只讀的鍵-值API,那麼SQL層就能夠在客戶端進行直接訪問。可以通過鍵-值API直接訪問資料,但如果增加或改寫了SQL層所用的關鍵資料,那就很可能破壞系統的執行。這裡例舉一些可能會產生的問題:缺乏對索引的維護、缺乏應有的限定,以及忽略了對資料及後設資料的版本維護。而這種方式的好處,哪怕是在進行資料讀取時也並不明顯,因為SQL層本身的額外開銷就非常小。因此總的來說,效能的開銷主要取決於網路延遲。
結論
SQL與NoSQL的結合使用能夠相互利用兩者的優點。FoundationDB的鍵-值儲存系統為SQL層帶來的好處包括可伸縮性、容錯性及全域性ACID的事務屬性。你的應用程式同樣也能從中受益,因此趕緊嘗試一下吧!對應那些要執行大量的小批資料讀取及寫入的應用程式來說,FoundationDB提供了一個高伸縮並且安全的解決方案,並且可以任意使用SQL或NoSQL。
相關文章
- 如何將外部資料庫 匯入到系統的SQL中資料庫SQL
- mmap共享儲存對映(儲存I/O對映)系列詳解
- 檔案系統儲存與oracle資料庫儲存對比Oracle資料庫
- 如何高效地儲存與檢索大規模的圖譜資料?
- 將Oracle作為NoSQL文件儲存OracleSQL
- 如何將資料庫中json格式的列值對映到java物件的屬性中資料庫JSONJava物件
- Win10系統將OneDrive對映到本地的方法Win10
- 8 種 NoSQL 資料庫系統對比SQL資料庫
- 8種NoSQL資料庫系統對比SQL資料庫
- DTO、儲存庫和資料對映器在DDD中的作用 | Khalil Stemmler
- 大資料儲存系統對比:Ceph VS Gluster大資料
- 儲存系統實現-如何刪除資料
- win10系統下如何對映共享資料夾Win10
- 50億海量資料如何高效儲存和分析?
- 儲存與資料庫系統資料庫
- 實驗:將系統程式對映移到 Python 字典中Python
- 騰訊重磅開源分散式NoSQL儲存系統DCache分散式SQL
- 如何將資料匯入到 SQL Server Compact Edition 資料庫中SQLServer資料庫
- 如何在HDFS中進行資料壓縮以實現高效儲存?
- 分散式系統中的資料儲存方案實踐分散式
- 使用儲存過程(PL/SQL)向資料庫中儲存BLOB物件儲存過程SQL資料庫物件
- 面對海量資料儲存,如何保證HBase叢集的高效以及穩定
- 如何將Azure SQL 資料庫還原到本地資料庫例項中SQL資料庫
- 從舊版本SQL Server中重新儲存資料SQLServer
- 什麼時候該使用NoSQL儲存資料庫?SQL資料庫
- 自動備份SQL資料庫到雲端儲存StorageSQL資料庫
- 瞭解如何在 Neo4j 的 NoSQL 資料儲存中持久化物件和關係。SQL持久化物件
- ARM體系中儲存系統
- NoSQL 和資料倉儲SQL
- 將SQL Server賬戶對應到Windows系統賬戶SQLServerWindows
- 大資料檔案儲存系統HDFS大資料
- 谷歌打造新款資料儲存系統Mesa谷歌
- 使用Fidder將生成環境程式碼對映到本地(資料夾)
- 如何將csf ip 埠對映
- WIndows下將資料夾對映為磁碟Windows
- 如何系統地學習資料探勘
- Synology群暉NAS儲存如何對映共享資料夾的教程【江東網 JDX86.COM】
- pl/sql developer將excel資料匯入到資料庫中SQLDeveloperExcel資料庫