日常吐槽
国外文章也不是都是好文章啊,不要见到英文就觉得高大上了……
前言
越来越多的关系型数据库底层选择基于KV构建,例如TiDB的TiKV(RocksDB),cockroach的levelDB,MySQL的tokudb,以及被苹果墙掉的FoundationDB。本文抢救出一篇FoundationDB的参考文章。
苹果买下FoundationDB后,FoundationDB的所有公开数据均被删除,包括Github,pypi,twitter等。
序
本文包含如下内容:
如何将关系型数据库的数据存入KV数据库
能否直接用KV数据库的接口读取数据
能否对KV数据库直接写数据,并用SQL读出修改后的数据
CockroachDB是如何做的
TiDB是如何做的
如何将关系型数据库的数据存入KV数据库
简单来说,FoundationDB的SQL层将数据库的元数据(metadata)作为键,将对应的数据作为值存入KV数据库。数据库表的有三种序列化方式,默认是foundationDB的tuple方式,当然,也可以选择使用protobuf序列化,或者使用column_keys的格式进行序列化,本文也只介绍foundationDB原生的序列化tuple
序列化方式。
KV数据库中的键是有序排列的,所有的库、表、列甚至索引对应的元数据由对应的目录层
在KV数据库中存储成类似
etcd中的“目录结构”的形式。目录层对数据库中的每个库、表、列生成对应的二进制字符串,该字符串在将关系型的数据映射到KV数据库时作为区分库、表、列的前缀。同时,目录层还可以将表的元数据转换到该二进制前缀。
如下实例解释文中提到的目录层
如何工作。
CREATE TABLE schema_a.table_name_1(id INT PRIMARY KEY, c CHAR(10));
CREATE TABLE schema_a.table_name_2(id INT PRIMARY KEY);
默认有一个根目录,目录名叫sql
,包含所有SQL层用到的所有键,下层目录叫做data,用于区数据和元数据。再下层目录叫做table,用于区分表内容和序列数据(sequence data)。table目录中的所有下一级的目录均为逻辑库(schema)的目录,逻辑库的下一级目录为逻辑表(table)的目录。
Directory | Tuple | Raw Key |
---|---|---|
sql / | (9) | x15x09 |
sql / data / | (3) | x15x03 |
sql / data / table / | (31) | x15x1F |
sql / data / table / schema_a / | (228) | x15xE4 |
sql / data / table / schema_a / table_name_1 / | (215) | x15xD7 |
sql / data / table / schema_a / table_name_2 / | (247) | x15xF7 |
当使用tuple序列化方式时,一行的数据被存储为一个键值对,键由上面提到的目录的“二进制前缀”,table在Table-Group
中的位置和主键组成,值便是由这条记录所有的列的进行序列化后的值。
例如:对上面两张表插入几条数据,对应的SQL和对应的键值对如下:
INSERT INTO schema_a.table_name_1 VALUES (1, 'hello'), (2, 'world');
INSERT INTO schema_a.table_name_2 VALUES (5);
二进制键 | tuple形式的键 | 二进制值 | tuple形式表示的值 |
---|---|---|---|
x15xD7x15x01x15x01 | (215, 1, 1) | x15x01x02hellox00 | (1, ‘hello’) |
x15xD7x15x01x15x02 | (215, 1, 2) | x15x02x02worldx00 | (2, ‘world’) |
x15xF7x15x01x15x05 | (247, 1, 5) | x15x05 | (5) |
能否直接用KV数据库的接口读取数据
简单回答:能。
能否对KV数据库直接写数据,并用SQL读出修改后的数据
安全性上来说,不能。FoundationDB数据不仅仅包含数据层,修改目录层的数据,很容易就会导致系统异常,例如缺少索引,缺少约束,缺少数据可元信息的验证。
CockroachDB是如何做的
CockroachDB中每个表都必须有主键,如果没有的话,默认也要生成一个。和FoundationDB一样,所有的表都会被映射为KV数据库中的键前缀。
每一列或者列族(column family)在KV数据库中,都会被序列化成一个值,并且作为KV数据库中的后缀。
例如:
在mydb下面创建表customers,包含两个列,一个列是name,一个列是URL,cockroach会在数据库中存储如下的schema信息:
Key | Values |
---|---|
/system/databases/mydb/id | 51 |
/system/tables/customer/id | 42 |
/system/desc/51/42/address | 69 |
/system/desc/51/42/url | 66 |
数据库mydb的id是51,表customer的id是42,列address的id是69,列url的id是66。
和FoundationDB不通,cockroachDB中一个键值对存储的是一条记录中某一列的值。
Key | Values |
---|---|
/51/42/Apple/69 | 1 Infinite Loop, Cupertino, CA |
/51/42/Apple/66 | http://apple.com/ |
前缀/51/42
,表示mydb
库的customer
表,/Apple
表示主键值为Apple
,/66
和/69
表示对应的列。
TiDB是如何做的
TiDB没有给出具体的方案,但是给出了一个大概的方案:
INSERT INTO user VALUES (1, "bob", "huang@pingcap.com");
INSERT INTO user VALUES (2, "tom", "tom@pingcap.com");
键存储的是表名+索引,值存储的是该条记录所有列的内容。
总结
RDBMS映射到KV比较简单,基本为:
KV中的键:唯一ID,通常能定位到一条记录,或者一条记录中的一个字段。但是,通常会映射、压缩。
KV中的值:对应序列化后的一条记录,或者一条记录中的一个字段。
另外,KV数据库中不仅仅存储着表的内容,还会存储着优化后的索引等许多东西。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。