数据存储与检索

173 阅读10分钟

1 关系模型与非关系模型

  • 关系型数据库:源于商业数据处理,以前用于商业数据的事务处理和批处理,现在广泛用于各种软件
    • 关系模型:集合,即任意元素组合的若干有序偶对
    • 关系代数:对关系作运算的抽象查询语言
    • SQL:一种DSL,方便人类阅读的关系代数表达形式
  • NoSQL:Not Only SQL,非关系型数据库
    • 主要包括文档数据库、列数据库、文档数据库、图形数据库
    • 文档数据库的应用场景:数据通常是自我包含的,而且文档之间的关系非常稀少。
    • 图形数据库用于相反的场景:任意事物都可能与任何事物相关联。

与关系型数据库相比,NoSQL的优点在于

  • 更好的可扩展性
  • 能够支撑大数据集和高吞吐量
  • 支持一些特殊查询操作
  • 能使用更具多态性和表现力的数据模型
  • 关系数据库的对象关系不匹配(阻抗不匹配)
    • SQL和编程语言之间需要一层转换层,模型之间不连贯
    • 对象关系映射(ORM)框架可以减少这个转换层所需代码的数量,但不能完全隐藏两个模型之间的差异
  • 文档数据库通常存储JSON文档,更适合描述一对多关系,且拥有更好的局部性

目前,关系数据库和文档数据库在相互补充,两者愈发相似。

1.1 数据查询语言

声明式查询语言中,我们只需要指定所需的数据的模式以及结果必须符合的条件,即可获取数据库中想要的信息。数据库系统的查询优化器决定使用哪些索引和哪些连接方法,以及何种顺序执行。声明式查询语言比命令式API更简洁容易,并且隐藏了数据库引擎的实现细节,这使得数据库系统可以在无需对查询做任何更改的情况下,进行性能的提升。声明式语言往往适合并行执行。

MapReduce是一个由Google推广的编程模型,用于在多台机器上批量处理大规模的数据。一些NoSQL数据存储(包括MongoDB和CouchDB)支持有限形式的MapReduce,作为在多个文档中执行只读查询的机制。

1.2 图数据模型

关系模型可以简单处理多对多关系,但是随着连接变得更加复杂,就应该将数据建模为图形。

一个图由两种对象组成:顶点(vertices)(也称为节点(nodes) 或实体(entities)),和边(edges)( 也称为关系(relationships)或弧 (arcs) )

例如公路或铁路网:顶点是交叉路口,边代表之间的路线

属性图

在属性图模型中,每个顶点(vertex) 包括:

  • 唯一的标识符
  • 一组 出边(outgoing edges)
  • 一组 入边(ingoing edges)
  • 一组属性(键值对)

每条 边(edge) 包括:

  • 唯一标识符
  • 边的起点/尾部顶点(tail vertex)
  • 边的终点/头部顶点(head vertex)
  • 描述两个顶点之间关系类型的标签
  • 一组属性(键值对)

由于SQL中的图查询比较困难,诞生了Cypher这种属性图的声明式查询语言,避免了SQL繁琐的递归进行图查询。

三元组储存和SPARQL

三元组储存大体上与属性图模型相同,用不同的词来描述相同的想法。

在三元组储存中,所有信息都以简单的三部分表示形式存储(主语、谓语、宾语)

三元组的主语相当于图中的一个顶点。而宾语是下面两者之一:

  1. 原始数据类型中的值,例如字符串或数字。在这种情况下,三元组的谓语和宾语相当于主语顶点上的属性的键和值。例如,(lucy, age, 33)就像属性{“age”:33}的顶点lucy。
  2. 图中的另一个顶点。在这种情况下,谓语是图中的一条边,主语是其尾部顶点,而宾语是其头部顶点。例如,在(lucy, marriedTo, alain)中主语和宾语lucyalain都是顶点,并且谓语marriedTo是连接他们的边的标签。

SPARQL是一种用于三元组储存的面向RDF数据模型的查询语言,与Cypher看起来很相似。

列存储

在某些特定的情况下,只需要获取库表几百列中的几列,这时典型的数据仓库查询需要将每一行读入内存而带来不必要的开销。

面向列存储不将所有来自一行的值存在一起,而是将每一列的所有值存储在一起,从而节省上述情况时的工作时间。

列压缩:面向列的存储通常很适合压缩,通过压缩数据可以进一步降低对磁盘吞吐量的需求。

内存带宽和向量处理:

  • 读取大量数据时,从磁盘获取数据到内存的带宽是一个巨大的瓶颈。
  • 分析数据库的开发人员需要有效利用主存储器带宽到CPU缓存中的带宽,避免CPU指令处理流水线中的分支错误预测和泡沫。
  • 面向列的存储布局可以有效利用CPU周期,比如查询引擎可以将大量压缩的列数据放在CPU的L1缓存中,然后再紧密的循环中循环。

列存储中的排序顺序:在列存储中,存储行的顺序不一定重要,且每列独自排序是没有意义的。排序顺序的好处是可以帮助压缩列,当一个值连续重复多次时可以进行长度编码的压缩。

2 存储与检索

2.1 驱动数据库的数据结构

一个最简单的数据库只需要一个get和set。

  • set的性能很高,因为只是尾部追加写入。因此许多数据库的日志文件也是仅追加文件。
  • get的性能在数据量变大时很低,所以需要索引进行高效查找。
    • 索引是从主数据衍生的附加(additional)结构,删除索引只会影响查询性能。
    • 维护索引会导致写入时的开销,因为索引使得数据库不是简单的追加写入。

索引的类型

哈希索引
  • 将key和位置的哈希映射放在内存中以实现快速索引
  • 编程语言中的 字典(dictionary) 通常就是用哈希表和散列映射实现的
SSTables和LSM树
  • 排序字符串表,键值对按照键排序,且每个键只在每个合并的段文件中出现一次。LSM树在内存中组织这些数据,保持Key在内存中有序
  • 构建和维护SSTables可以使用红黑树或AVL
  • 用SSTables制作LSM树
B - Tree
  • B树页保持了按键排序的键值对,同时B树将数据库分解成固定大小的块或页面,并且一次只能读入一个页,这样对硬件更友好。
  • 每个页面都可以使用地址或位置来标识,这允许一个页面可以在磁盘中引用另一个页面,类似于指针,但在磁盘而不是在内存中。
  • B树通常会有预写式日志(WAL, write-ahead-log),也称重做日志(redo log)。这是一个仅追加的文件,每个B树修改都可以应用到树本身的页面上。
比较B树和LSM树

尽管B树实现通常比LSM树实现更成熟,但LSM树由于其性能特点也非常有趣。

通常LSM树的写入速度更快,而B树的读取速度更快。 LSM树上的读取通常比较慢,因为它们必须在压缩的不同阶段检查几个不同的数据结构和SSTables。

其他索引结构
  • 将值存储在索引中
  • 多列索引
  • 全文搜索和模糊索引
  • 在内存中存储一切

2.2 OLTP和OLAP

随着需求的不断上升,诞生了OLTP用于与数据库交互,OLAP用于分析数据。

属性在线事务处理 OLTP在线分析处理 OLAP
主要读取模式查询少量记录,按键读取在大批量记录上聚合
主要写入模式随机访问,写入要求低延时批量导入(ETL),事件流
主要用户终端用户,通过Web应用内部数据分析师,决策支持
处理的数据数据的最新状态(当前时间点)随时间推移的历史事件
数据集尺寸GB ~ TBTB ~ PB

OLTP系统通常面向用户,为了处理大量请求,程序通常只访问每个查询中的少部分记录。程序使用某种键来请求记录,存储引擎使用索引来查找所请求的键的数据。磁盘寻道时间往往是其瓶颈。

OLAP主要由业务分析人员使用,它的查询量少但是每个查询的开销都高昂,需要在短时间内扫描数百万条记录。磁盘带宽往往是瓶颈,列式存储是这种工作负载越来越流行的解决方案。

数据仓库

数据仓库是一个独立的数据库,分析人员可以查询他们想要的内容同时不影响OLTP操作。数据仓库可以针对分析访问模式进行优化,索引算法在OLTP上能很好的工作,但回答分析查询不是很好。

由于企业规模扩大,所需要的OLTP变多且要求高可用与低延迟,同时分析人员运行OLAP会造成巨大开销而妨碍事务性能,所以渐渐引入了数据仓库。小型企业不需要数据仓库,少量的数据可以在传统SQL数据库中查询。

OLTP数据库与数据仓库之间的分歧

数据仓库的数据模型通常是关系型的,因为SQL很适合分析查询。一个数据仓库与一个关系OLTP数据库看起来相似是因为他们都有SQL查询接口,然而他们的内部完全不同,它们针对非常不同的查询模式进行了优化。现在许多数据库供应商都将重点放在支持事务处理或分析工作负载上,而不是两者都支持。

例如Microsoft SQL Server和SAP HANA,支持在同一产品中进行事务处理和数据仓库。但是,它们正在日益成为两个独立的存储和查询引擎,这些引擎正好可以通过一个通用的SQL接口访问。

分析的模式:星型模式和雪花模式

星型模式即当关系可视化时,事实表在中间由维表包围,这些表的连接就像星星的光芒。事实被视为单独的事件(例如交易记录表),事实表的列中有很多对其他表(称为维表)的外键引用,这样的表被称为星型模式。

雪花模式是星型模式的变体,维表被进一步分解,即维表中也有许多外键引用。雪花模式比星形模式更规范化,但是星形模式通常是首选,因为分析师使用它更简单