cockroachdb mysql_CockroachDB学习笔记——[译]CockroachDB中的SQL:映射表中数据到键值存储...
CockroachDB學習筆記——[譯]CockroachDB中的SQL:映射表中數據到鍵值存儲
原文標題:SQL in CockroachDB: Mapping Table Data to Key-Value Storage
原作者:Peter Mattis , Tamir Duberstein
原文日期:Sep 16, 2015
譯:zifeiy
CockroachDB中的SQL:映射表中數據到鍵值存儲
SQL?我覺得CockroachDB是一個鍵值存儲?!?
在過去,我們將CockroachDB描述成一個分布式的、事物一致性的、鍵-值存儲數據庫。
我們知道我們想要提供的并不僅僅是一個鍵-值模型的API,我們想要設計的是一個更高級的結構化數據API,讓他能夠支持數據庫中的表(table)和索引(index)。
除了支持這樣的豐富結構外,我們還期望最終支持SQL來操縱和訪問這種結構化數據。
經過一番絞盡腦汁之后,我們排除了所有使用SQL的不可能性,并以SQL作為我們結構化數據層的核心,全速前進。
在一個SQL中有很多組建需要設計,僅僅據一些例子:查詢解析(query parsing),查詢分析(query analysis),查詢規劃(query planning),查詢執行(query execution),事務(transactions),持久存儲(persistent storage)。
CockroachDB的SQL系統建立在內部的CockroachDB 鍵-值存儲之上,并利用整體排序的鍵值映射來存儲所有SQL表數據和索引。
本文將重點討論CockroachDB在SQL數據和鍵-值存儲中的映射關系,并且展示這種映射如何磅數實現SQL功能。
未來的文章將討論查詢分析、規劃和執行。
一個SQL表(table)是由一系列的行(row)組成的,每一行包含若干列(column)。
每一列都有它關聯的類型(bool, int, float, string, bytes)。
表還具有關聯索引,允許從表中高效地檢索行的范圍。
這聽起來根本不像是一個將字符串映射到字符串的鍵-值API呀。如何將SQL表映射到K-V(鍵-值)存儲呢?
首先,一個入門:CockroachDB內部的鍵-值 API支持大量的操作,但是在這篇文章中我們只需要知道其中的一部分:
ConditionalPut(key, value, expected-value) - 如果expect-value對應的條件成立,則我們將value的值賦給key。
Scan(start-key, end-key) - 在區間[start-key,end-key)對應的區間(左閉右開區間)內檢索所有的鍵。
在CockroachDB中,鍵和值都可以包含不限制字節數的字符串。
Ok!讓我們繼續!
鍵編碼(Key Encoding)
將SQL表中的數據到鍵和值的基本問題是將有類型之分的列數據編碼為字符串。
比如,給定一組數值<1, 2.3, "four”>,我們會將其編碼為一個字符串,而這個字符串看上去像是這樣的:
/1/2.3/"four”
我們使用反斜杠作為值之間的視覺分隔符,盡管這僅僅是出于可讀性的目的。
我們可以把一篇完整的博客文章獻給這些編碼。(意思是:說到編碼我們可以將一大堆咯,大家就先按照我這么說的理解有可以了)
為了簡單起見,這里只討論了它們的性質,而不是它們的實現。
被編碼的鍵們是排序的,因此鍵的每一個字段都被認為是分開的:
/1/2.3/"four”
/2/3.1/"six”
/10/4.6/"seven”
如果你按照原始的方法天真的為這些字符串排序,你會發現/10/...是排在/2/...之前的。
編碼工作可以變得更神奇一點如果你之前沒有碰到過類似情況。
如果你對編碼的細節感興趣可以在util/encoding中查看{Encode,Decode}{Varint,Float,Bytes,Null}。
(譯者注:原鏈接https://github.com/cockroachdb/cockroach/tree/master/util/encoding已失效,譯者找到了新的鏈接https://github.com/cockroachdb/cockroach/tree/master/pkg/util/encoding)
有了這個編碼工具,我們就可以窺視SQL表數據的編碼。
在CockroachDB中,每個表在創建時都有一個唯一的64位整數ID分配給它。
此表ID用作與該表關聯的所有鍵的前綴。
現在讓我們考慮如下的表和數據:
CREATE TABLE test (
key INT PRIMARY KEY,
floatVal FLOAT,
stringVal STRING
)
INSERT INTO test VALUES (10, 4.5, "hello”)
CockroachDB中的每一張表都必須有一個主鍵。
主鍵由一個或多個列組成;在上面的測試表中,它由單個列組成。
CockroachDB將每個非主鍵列存儲在由主鍵作為前綴并且由列名作為后綴的單獨密鑰中。
因此,行<10, 4.5, "hello">將以如下形式存儲在我們的test表中:
Key
Value
/test/10/floatVal
4.5
/test/10/stringVal
"hello”
在這個描述中,我們使用/test/作為表ID(table ID)的占位符,
使用/floatVal/和stringVal作為列ID(column ID)的占位符。
(表中的每個列都具有表中唯一的ID。)
注意,在我們的編碼中主鍵緊跟在表ID(table ID)之后。
這是CockroachDB SQL實現中索引描述(index-scans)的基礎。
如果我們揭開現象看本質的話,我們將看到表的元數據如下:
test Table ID
1000
key Column ID
1
floatVal Column ID
2
stringVal Column ID
3
在數字表單中,我們的表的鍵值對看起來像:
Key
Value
/1000/10/2
4.5
/1000/10/3
"hello”
在這篇文章的剩余部分,我們將以這種符號的形式來描述鍵。
【你也許會想在每一個鍵前面都添加一個公共的前綴(/1000/10)太浪費存儲空間了,但是我們的底層存儲引擎RocksDB,
通過將密鑰的前綴壓縮幾乎消除了所有開銷。】
精明的讀者會注意到,在主鍵中存儲列的鍵值對是不必要的,因為這些列的值已經在密鑰本身中編碼。
事實上,CockroachDB很喜歡這樣。
注意,因為主鍵前綴的緣故,對于某一個特定行的所有列將彼此相鄰存儲(鍵和值存儲在CockroachDB中的一個已排序的整體映射(Map)中,所以這個屬性是免費的)。
這允許使用前綴掃描來檢索特定行的值。這正是CockroachDB內部所做的。
查詢:
SELECT * FROM test WHERE key = 10
將會被翻譯成:
Scan(/test/10/, /test/10/Ω)
這個操作將只會檢索行的兩個鍵。
Ω表示最后一個可能的鍵的后綴。
然后,查詢執行引擎將解碼密鑰以重構行。
空列值(Null Column Values)
這個故事有一個小小的轉折:
除非特別指定了NOT NULL,不然列的值時可能為NULL的。
CockroachDB并不使用NULL值,而是使用一對鍵值對的缺席來標記空值。
細心的同學可能已經看到了這里的一個問題了:按照這種分析,如果一行數據里面的所有非主鍵的列是NULL的,那么我們將不會存儲這組數據了。
為了解決這個問題,CocoroachDB始終為那些有主鍵但是沒有列后綴的行數據添加一個前哨鍵(sentinal key)。
以行<10, 4.5, "hello”>,他的前哨鍵將是/test/10。啊哈!
二級索引(Secondary Indexes)
到目前為止,我們忽略了次要索引。讓我們糾正這種疏忽:
CREATE INDEX foo ON test (stringVal)
這句SQL在列stringVal上創建了一個二級索引。
我們沒有聲明索引是唯一的,所以允許重復的值。
類似于表的行,我們將把所有索引數據存儲在由表ID作為前綴的鍵中。
但是我們希望從行數據中分離索引數據。
我們通過引入索引ID來實現這一點,該索引對于表中的每個索引都是唯一的,包括主鍵索引(對不起,我們之前說謊了!):
/tableID/indexID/indexColumns[/columnID]
我們用上面的例子得到的鍵稍長一些:
Key
Value
/test/primary/10
?
/test/primary/10/floatVal
4.5
/test/primary/10/stringVal
"hello”
并且現在我們對于我們的索引foo也有一個單獨的鍵:
Key
Value
/test/foo/”hello”/10
?
您可能想知道為什么我們用主鍵值(/10)作為這個編碼的后綴。
對于像foo樣的非唯一索引,這是必要的,以便允許相同的值在多行中發生。
因為根據定義表的主鍵是唯一的,所以將其作為后綴添加到一個非唯一的鍵后面將會形成一個唯一的鍵。
一般而言,對于一個非唯一索引,CockroachDB將包含在主鍵中但是不包含在索引中的所有列追加到值后面。
現在讓我們來看當我們把<4, NULL, "hello”>插入到我們的表中會發生什么:
Key
Value
/test/primary/4
?
/test/primary/4/stringVal
"hello”
/test/foo/"hello”/4
?
所有的表中的數據合到一起看起來是這樣的:
Key
Value
/test/primary/4
?
/test/primary/4/stringVal
"hello”
/test/primary/10
?
/test/primary/10/floatVal
4.5
/test/primary/10/stringVal
"hello”
/test/foo/"hello”/4
?
/test/foo/"hello”/10
?
次級索引被用在 SELECT 中用于掃描一組小的集合的鍵。考慮:
SELECT key FROM test WHERE stringVal = "hello”
這個查詢的規劃師(query planner)會注意到在StringVal上有一個索引,并將此查詢翻譯成:
Scan(/test/foo/”hello”/, /test/foo/”hello"/Ω)
它將檢索以下的鍵:
Key
Value
/test/foo/”hello”/4
?
/test/foo/”hello”/10
?
注意到這些鍵不僅包括索引列stringVal,而且還包括主鍵列所對應的鍵(譯者注:4和10)。
CockroachDB將會注意到主鍵列上的鍵并且避免對整行數據進行不必要的查找。
最后,讓我們看一下如何對唯一索引進行編碼。
除了我們早先創建的索引foo,我們在創建一個uniqueFoo:
CREATE UNIQUE INDEX uniqueFoo ON test (stringVal)
與非唯一索引不同,唯一索引的鍵僅由索引的一部分組成。
鍵中存儲的值是主鍵組成的列中去除掉索引中的列之后剩下的那些列。
我們的test表中的兩行數據將被編碼成:
Key
Value
/test/uniqueFoo/"hello”
/4
/test/uniqueFoo/"hello”
/10
我們使用ConditionalPut函數,嘗試寫入數據之前檢測這個鍵是否早已存在了,已檢測是否違反了唯一性約束。
這就是CockroachDB如何簡單地將SQL數據映射到鍵值存儲中的方式。
請密切關注接下來關于查詢分析(query analysis)、計劃(planning)和執行(execution)的文章。
將SQL映射為鍵值存儲的電子并不是在CockroachDB中獨有的。這本質上也是MySQL on InnoDB、Sqlite4和其他很多數據庫的設計。
總結
以上是生活随笔為你收集整理的cockroachdb mysql_CockroachDB学习笔记——[译]CockroachDB中的SQL:映射表中数据到键值存储...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux c 封装redis,封装hi
- 下一篇: 用程序同步mysql数据库表_初次用Ja