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。

原文:http://www.nosqlcn.com/ShowArticle/16

转载于:https://www.cnblogs.com/JackQ/p/4587924.html

使用FoundationDB高效地将SQL数据映射到NoSQL存储系统中相关推荐

  1. 将CSV和SQL数据加载到Pandas中

    目录 介绍 从CSV加载数据 从SQLite加载数据 基本数据分析 总结 任何数据分析过程的第一步都是摄取数据集,评估数据集的清洁程度,并决定我们需要采取哪些措施来解决继承的问题. 下载CSV和数据库 ...

  2. mysql做kv数据库_如何将SQL数据映射到KV数据库

    日常吐槽 国外文章也不是都是好文章啊,不要见到英文就觉得高大上了-- 前言 越来越多的关系型数据库底层选择基于KV构建,例如TiDB的TiKV(RocksDB),cockroach的levelDB,M ...

  3. iBatis.Net(C#)SQL数据映射

    转载请注明 http://www.cnblogs.com/13590/archive/2013/03/01/2938126.html 摘要:本文探讨了iBatis.Net框架的XML数据映射文件各配置 ...

  4. cockroachdb mysql_CockroachDB学习笔记——[译]CockroachDB中的SQL:映射表中数据到键值存储...

    CockroachDB学习笔记--[译]CockroachDB中的SQL:映射表中数据到键值存储 原文标题:SQL in CockroachDB: Mapping Table Data to Key- ...

  5. 分布式存储系统中的数据高效缓存方法

    点击上方蓝字关注我们 分布式存储系统中的数据高效缓存方法 杨青霖, 吴桂勇, 张广艳 清华大学计算机科学与技术系,北京 100084 摘要:针对典型分布式存储系统存在的写放大.I/O路径过长.响应时延 ...

  6. 编程python怎么读-python怎么读sql数据?

    python中读取SQL数据的方法: python中可以使用游标cursor来读取SQL中的数据,游标cursor是由连接创建的对象,可以在游标中执行查询,并设置数据返回的格式. 当执行select语 ...

  7. python语法怎么读-python怎么读sql数据?

    python中读取SQL数据的方法: python中可以使用游标cursor来读取SQL中的数据,游标cursor是由连接创建的对象,可以在游标中执行查询,并设置数据返回的格式. 当执行select语 ...

  8. c#sql数据导出到excel

    这篇文章主要介绍了C#将Sql数据保存到Excel文件中的方法,文中的ExportExcel可起到将sql数据导出为Excel的作用,需要的朋友可以参考下 public string ExportEx ...

  9. 用python怎么读_python怎么读sql数据?

    python中读取SQL数据的方法: python中可以使用游标cursor来读取SQL中的数据,游标cursor是由连接创建的对象,可以在游标中执行查询,并设置数据返回的格式. 当执行select语 ...

最新文章

  1. 如何在Android Studio中查看方法信息?
  2. vue 悬浮按钮_Vue@哇!几行代码实现拖拽视图组件
  3. PHP - ReflectorClass 反射类的作用
  4. c语言 com组件,com组件 C语言基础.ppt
  5. springboot 直接转发调用_java springboot 引用openfeign 接口转发
  6. 软件项目估算概念、步骤、方法、戒律总览
  7. 数字内容产业的产业链结构
  8. php,表单+文本域,增加表单的文本域的html
  9. 计算机毕业设计Python+uniapp实验室考勤小程序(小程序+源码+LW)
  10. Lua:协程,coroutine.create,coroutine.resume, coroutine.yield
  11. 云原生下的开发测试实践
  12. cad怎么将图层后置_cad怎么把整个图层置于底层
  13. 神州战神TX6修改开机logo教程
  14. ModuleNotFoundError: No module named ‘common‘
  15. [web] request的Content-Type小结
  16. 不用分片也能扩展 10 倍性能?简单了解以太坊 Turbo-Geth 客户端
  17. 数据可视化之折线图让数据变得更直观易懂
  18. C语言中return的作用
  19. C语言:初识数组及数组的复制
  20. android 即时战略游戏,即时战略手游排行榜前十名 即时战略游戏推荐

热门文章

  1. 一次性无重复配置VS项目插件属性的方法
  2. bestcoder #71 1003 找位运算的最大生成树
  3. 关于unity的一个不错的插件平台
  4. java list 结构_Java中常见数据结构List之ArrayList
  5. html标签库jar包,struts2的s标签库jar包
  6. java能应聘哪些职位_应聘java专员职位的面试题
  7. 鸿蒙系统手机还会出吗,华为最强手机即将到来,可能还有华为鸿蒙系统加入!你期待吗?...
  8. java循环使用范围_Java循环流程控制语句
  9. python阿拉伯数字转中文_python中将阿拉伯数字转换成中文的实现代码
  10. python简易爬虫课程设计_python实现简单爬虫功能的示例