6

日常吐槽

国外文章也不是都是好文章啊,不要见到英文就觉得高大上了……

前言

越来越多的关系型数据库底层选择基于KV构建,例如TiDB的TiKV(RocksDB),cockroach的levelDB,MySQL的tokudb,以及被苹果墙掉的FoundationDB。本文抢救出一篇FoundationDB的参考文章

苹果买下FoundationDB后,FoundationDB的所有公开数据均被删除,包括Github,pypi,twitter等。

本文包含如下内容:

  1. 如何将关系型数据库的数据存入KV数据库

  2. 能否直接用KV数据库的接口读取数据

  3. 能否对KV数据库直接写数据,并用SQL读出修改后的数据

  4. CockroachDB是如何做的

  5. 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");

clipboard.png

键存储的是表名+索引,值存储的是该条记录所有列的内容。

总结

RDBMS映射到KV比较简单,基本为:

KV中的键:唯一ID,通常能定位到一条记录,或者一条记录中的一个字段。但是,通常会映射、压缩。
KV中的值:对应序列化后的一条记录,或者一条记录中的一个字段。

另外,KV数据库中不仅仅存储着表的内容,还会存储着优化后的索引等许多东西。


大舒
7k 声望815 粉丝

define TRUE FALSE