读书笔记:《七周七数据库》

概述

核心问题:哪种数据库或数据库组合最好地解决了你的问题?

  • 关系型数据库:PostgreSQL
    • 以集合理论为基础,实现为具有行和列的二维表;
    • 使用结构化查询语言编写查询;
    • 表可以连接并转化为新的、更复杂的表。
  • 键值型数据库:Riak & Redis
    • 类似于映射(哈希表)。
  • 多列型数据库:HBase
    • 一个给定列的数据存放在一起,和按行保存信息的关系型数据库不同;
    • 允许表保持稀疏,而不会产生空值的存储成本。
  • 文档型数据库:MongoDB & CouchDB
    • 存储文档,文档可以包含嵌套的结构,表现出了高度的灵活性,允许有可变域。
  • 图数据库:Neo4j
    • 包含节点及节点之间的关系;节点和关系可以用一些属性来存储数据;
    • 善于处理高度互联的数据,能够快速在节点和关系之间移动,找到相关数据。

多持久并存:使用多种数据库,利用它们各自的长处,创建一个生态系统,比其各部分的功能总和更强大、更全面、更健壮。

在讲究开发速度的初期,选择快速上手的工具,并设计好屏蔽底层实现的接口,便于后期的技术调优。

如果不要求 ACID 和事务处理,大胆尝试 NoSQL!

  • PostgreSQL:其他结构化数据(适用于 ACID,事务处理)
  • Riak:Web 服务(适用于键值存储的 Web 服务)
  • HBase:日志管理系统(适用于大数据分析)
  • MongoDB:应用驱动数据集(适用于经常基于应用变更模型)
  • CouchDB:订单管理,手机端和电脑端数据同步保存(适用于健壮存储,多端存储)
  • Neo4j:社交网络(适用于关系查询,图遍历查询)
  • Redis:数据快速访问缓存(适用于高速缓存)

关系型数据库:PostgreSQL

如果关系数据模式是规范的,那么查询就可以很灵活。

关系、CRUD 和连接

关系代数 <- 元组关系演算:

  1. 选择(Where)
  2. 投影(Select)
  3. 笛卡尔积(Join)

关系为表;属性为列;元组为行。

索引:

  1. 主键索引
  2. 唯一索引
  3. 自定义索引
    • 哈希索引用于匹配查询
    • B 树索引用于范围查询

高级查询、代码和规则

  1. 聚合函数
  2. 分组:Distinct = Group By 但不聚合
  3. 窗口函数:Partition By
  4. 事务:psql 中执行的每条命令都隐式地包裹在事务中
  5. 存储过程
  6. 触发器
  7. 视图(普通视图和物化视图)
  8. 规则
  9. 联表分析(数据透视表)

关系型数据库使用原则:

  1. 外键的约束可以在业务层解决;
  2. 数据库成为瓶颈时,把存储过程的业务逻辑放在业务层,否则使用存储过程加速;
  3. 触发器的逻辑移到业务层,否则不好维护;
  4. 视图的选择和存储过程相同,在数据库瓶颈和数据库加速之间作权衡;
  5. 连接的使用和存储过程相同,在数据库瓶颈和数据库加速之间作权衡;
  6. 规则的使用和存储过程相同,在数据库瓶颈和数据库加速之间作权衡;
  7. 联表分析的使用和存储过程相同,在数据库瓶颈和数据库加速之间作权衡。

数据库是瓶颈,还是可以提供一定的加速能力?事实上业务层的加速效率远远大于数据库

全文检索和多维查询

除了模糊查询之外的高级搜索还是让业务层来实现吧。

模糊搜索

  1. like, ilike 和正则匹配
  2. 字符串相似比较:levenshtein
  3. 三连词

全文搜索

  1. 词素:TSVector 和 TSQuery
  2. 词素索引
  3. 发音码:metaphone

多维超立方搜索

cube 扩展模块是 PostgreSQL 特有的模块,不是 SQL 标准定义的内容。

总结

关系型数据库对于灵活查询是一个很好的选择。虽然 PostgreSQL 需要提前设计数据,但它不假设如何使用这些数据。只要数据模式设计相当规范,没有数据重复并且不存储可被计算出来的值(冗余),基本上就可以应付所有可能需要的查询。如果使用了合适的模块,调优,索引,只需要消耗极少的资源就能惊人地处理TB级别的数据。但是由于不擅长分区和水平扩展,为了避免数据库成为系统瓶颈,ACID 之外的功能还是交给业务层来实现吧。


键值型数据库:Riak

Riak 适用于 Web 和分布式,并提供了 HTTP REST 接口。

CRUD、连接和 MIME

CRUD <-> REST:

  1. Post <-> Create
  2. Get <-> Read
  3. Put <-> Update
  4. Delete <-> Delete

URL: http://Server:Port/riak/bucket/key(包含桶和键)

连接

讲一个键关联到其他键的元数据。连接是单向的,可以通过连接进行遍历。

MIME 类型

Riak 将任何数据都另存为二进制编码的值。MIME 类型的意义在于赋予二进制数据上下文。

上传图片时,需要使用 curl 的 data-binary 标志将图片上传到服务器,并指定 MIME 类型为 image/jpeg,然后添加到照片资源的连接。

MapReduce 和服务器集群

  1. 可存储的函数
    • 桶内可以存储函数,类似存储过程
  2. 内置函数
  3. 归约
  4. 键过滤器
    • 预处理键,避免加载不需要的键
  5. 链接遍历
    • 查询时包含链接

关于持久性和一致性的读写设置:

  • n_val:副本的个数
  • N:在调用返回前,等待复制到N个节点
  • W:在写操作成功前,必须成功写入W个节点
  • R:在读操作成功前,必须成功读出R个节点
  • DW:在调用返回前,至少有DW个节点写入磁盘(Riak 的写入未必是持久化的)

一致性的权衡方案:

  1. W=1, R<N:写入更快,但是容易读出脏数据
  2. W=N, R=1:读取简单,但是降低了写入的性能
  3. W=1, R=N:读操作会变慢
  4. W>N/2, R>N/2:分担读写之间的延时,是确保数据一致性的方法中开销最小的

解决冲突和扩展 Riak

向量时钟解决冲突

使用时间戳保持时钟同步很困难,也以为着单点故障的可能性。

向量时钟对每个键值事件(创建、更新或者删除)做标记,标记包含两项内容:

  1. 哪个客户端更新了数据
  2. 以哪种顺序更新

通过向量时钟得以决定谁在冲突中胜出。(和Git之类的版本控制系统解决两人修改同一文件的版本冲突的本质相同)

Riak 可以在存储对象之前或者之后,通过钩子程序转化数据。但是为了防止数据库成为瓶颈,还是放在业务端实现吧,例如:Gorm 的钩子。

扩展 Riak

  1. Riak 搜索扩展
  2. Riak 二级索引扩展
    • 在索引数据的同时保持存储的任意数据正交

总结

  • 优点:高可用性。如果需要超出 HTTP 处理能力的速度,可以通过 Protobuf 等二进制编码和传输协议进行通信。
  • 缺点:如果你需要简单的查询能力、复杂的数据结构或者严格的模式,又或者你不需要节点服务器进行横向扩展,那么 Riak 可能不是你的最佳选择。如果你想获得扩展的能力或者在 Web 上提供数据,请考虑使用 Riak。

多列型数据库:HBase

HBase 的特点是一致性和伸缩性。还具有版本管理(处理历史数据)、压缩、垃圾回收(对于超前的数据),以及内存表等特性,使得其成为在线分析处理系统的基石。常常用作后台日志和查询系统。

CRUD 和表管理

映射表是一个键值对,更准确地说是嵌套许多映射表的映射表。

  • 键可以是行键,每个键映射到一行数据。
  • 行本身也是一个映射表,其中包含列键。
  • 列按照列族进行分组,列的完全名称包括列族名称和列限定符。

行键和列表(包括列族和限定符)组合的三元组,形成了定位数据的地址:行键/列族:列限定符

在构造表结构的时候,安全可以把一行的所有数据都放在一个列族中。但是这样就丧失了细粒度性能调优的机会,每个列族的性能选项都是独立配置的,而这些设置影响到读和写的速度,以及磁盘空间的占用。

处理大数据

  • 压缩:对于包含大块文本内容的列族,可以启用压缩和快速查找,支持Gzip(GZ)和Lempel-Ziv-Oberhumer(LZO)压缩算法,其中LZO因为不兼容Apache许可证,所以需要额外的配置。
  • Bloom 过滤器:快速确定键是否已存在。确定行键是否存在,确定某行是否存在某列。
    • 对于不存在的判断,不会误报;
    • 对于存在的判断,可能会误报;根据饱和程度,误报率是可预测的。
  • 预写式日志:HBase 利用预写式日志来防止节点故障,也可以禁用 WAL,牺牲灾难恢复的保护,来换取性能的提升。
  • 分区:如果表的数据足够大,HBase 会将表分成多个区域,在分布式环境下,它们会分包到不同的区域服务器。
    • .META. 表的唯一目的是追踪所有的用户表,以及哪个区域服务器负责为这些表的各个区域服务。我们可以利用 .META. 表的信息,来查找某一行应该在哪个区域和哪个服务器。
    • .META. 表也是分区存储的,要找到哪个服务器存储 .META. 表的哪一部分,需要扫描 -ROOT-。
  • 稀疏存储:每一行可以有不同的列,而不需要存储额外的空列(列族数目相同)。

放入云端

HBase 支持多种客户端连接协议:

  1. Shell 直接连接
  2. Java API 直接连接
  3. Thrift 二进制连接
  4. REST HTTP 连接

Thrift 客户端应用 <-> Apache Whirr <-> Amazon EC2 HBase 远程集群

总结

  • 优点:健壮可伸缩,内置的版本控制和压缩功能。
  • 缺点:
    1. 可伸缩性,但是不能缩小,5 个节点是最小配置,大的生态环境难以管理;
    2. 除了行键之外没有索引,按行键之外的查询都需要遍历表,或自己维护索引;
    3. 缺失数据类型的概念,所有的字段值都存储为字节数组。

文档型数据库:MongoDB

MongoDB 被设计成为一个可伸缩的数据库,性能和易于存储数据是其核心设计目标。作为文档数据库,允许数据以嵌套的状态保存,而且允许以任意的方式查询嵌套的数据,不强制使用模式。MongoDB 适用于保存大规模的数据,又能满足自由定义的查询。

CRUD 和嵌套

  • MongoDB 的自动 ID 生成,使得分布式部署也不会冲突。
  • MongoDB 的母语是 JavaScript,可以使用 JavaScript 作为查询语言。

高级搜索

  1. elemMatch
  2. 布尔操作符
  3. JavaScript 函数查询

MongoDB 借助 JavaScript 和其嵌套的存储模式,可以执行很灵活的查询。MongoDB 的灵活性是有代价的,一旦你写错了一个字母,程序可能会朝着意想不到的方向发展。

引用

MongoDB 不支持连接操作,但是可以通过引用查询,使用数据驱动的方式可以达到联合查询的效果。

索引、分组和 MapReduce

索引

MongoDB 采用了一些最好的数据结构来实现索引,如B树,还有很多其他方法。在大型集合创建索引非常耗时,在流量低峰期手动完成,不要设置自动建立索引。

可以使用 explain()(测试环境)或者 setProfilingLevel()(生产环境)对搜索的性能进行分析。

聚合查询

  1. count:将结果聚合成文档的计数
  2. distinct:将结果聚合成一个数组
  3. group:灵活的分组查询

MapReduce

可以将 MapReduce 的结果存储到物化视图。(通过 out 参数设置集合的名称)

副本集、分片、地理空间和 GridFS

  • 副本集:数据分片和冗余存储。
  • 分片:mongos 服务器作为单点入口,连接 mongoconfig 配置服务器,获取存储的分片信息,再去相应的 mongod 分片执行查询。
  • 地理空间查询:能快速执行地理空间的查询,不仅能找到具体的值或者范围,还能搜索附近的值。
  • GridFS:为分布式系统提供一个一致性的文件系统,MongoDB 自己的分布式文件系统。

总结

  • 优点:处理大量数据;灵活的数据模型;灵活的查询条件。
  • 缺点:没有模式约束的灵活性有时候反而会带来问题(输错了也没有bug);复杂的集群难以管理(不如 Riak 易于管理)。

文档型数据库:CouchDB

CouchDB 是基于 JSON 和 REST 的面向文档的数据库,考虑 Web 的无数缺陷,CouchDB 是非常健壮的数据库,可以容忍很大的网络错误。只支持追加的存储模式,使用增量式映射归约生成的索引视图进行查询。

CRUD、Futon 与 cURL Redux

  • Futon:自带实用的 Web 图形化接口进行 CRUD 操作。
  • cURL:可以通过 REST 接口(GET®,POST©,PUT(U),DELETE(D))进行 CRUD 操作。

更新时必须匹配 rev(版本号),先到先得,否则会返回版本冲突。删除和更新只是新建了新文档和版本号,源文档仍然保留。

创建/查询视图

视图是访问文档的主要方式,通过创建临时视图来执行查询,视图包含映射与归约函数。在生产环境中,应把临时视图存储为设计文档,相当于物化视图。

进阶视图、Changes API 以及复制数据

  • 进阶视图:可以通过 Map,或者 MapReduce 创建视图。CouchDB 会保存映射器与归约器的中间值,直到文档发生变化时,增量地运行 MapReduce。因为 CouchDB 不丢弃中间数据值,所以 MapReduce 作为了主要的索引机制。
  • Changes API:监控 DB 的变化,有三种:轮询、长轮询以及连续。
  • 复制数据:CouchDB 每个节点都是主节点,拥有全部的数据(想使用分片可以使用 BigCouch)。CouchDB 会自动解决冲突(随机选择),并保存了冲与之突的版本号,因为保存了所有数据,所以可以事后人工处置。

总结

  • 优点:尽可能分散数据存储的健壮性。
  • 缺点:基于映射归约的视图索引,没有自由的查询;没有分片,增加节点不能分散数据,只是增加了读写操作的吞吐量。

图数据库:Neo4j

图数据库的重点是数据间的关系,而非数据集合间的共性。

图、Groovy 和 CRUD

  • Neo4j 提供了 Web 管理接口
    • UI 交互操作
  • Gremlin/Groovy 接口
    • 使用 pipe 而非循环来遍历图
    • Gremlin 作为特定于查询图数据库领域的一种语言,可以使用特定领域语言查询
  • Cypher 接口
    • 基于模式匹配,语法类似 SQL
  • REST 接口
    • Post 不止用于创建数据,还可以上传查询条件执行各种查询操作
    • 发送 Gremlin 命令执行查询
  • 整合 lucene 全文搜索
    • elasticsearch 是对 lucene 的封装

Gremlin 适合复杂的查询,而 REST 适合于部署以及灵活性。

索引与算法

索引

  • 键值索引
  • 全文搜索倒排索引

算法

  • Kevin Bacon 算法:搜索两个节点之间凭借某种关系的最短距离
  • 随机遍历
  • 中心度(Google: PageRank):是对全图中单个节点的度量。基于每个节点到其他所有节点的距离,度量其重要性。

分布式高可用性

  • 支持 ACID 事务
  • 支持分布式集群(基于 Zookeeper 的分布式协调服务,基配置中心(TCC))
  • 提供全备份和增量备份

总结

  • 优点:没有类型和模式,对数据关联的方式没有限制。图遍历不同于 MapReduce,是常量时间复杂度的。
  • 缺点:无法顶点指回自身。无法划分子图(导致了图的大小有所限制)。

键值型数据库:Redis

Redis(Remote Dictionary Service, 远程字典服务)不只是一个超快的键值对存储系统,还可以存储复杂数据类型并进行功能的细致处理。

CRUD 与数据类型

CRUD 只有命令行接口和高级语言的接口封装。

复杂数据类型:

  • 哈希表
  • 列表
    • 阻塞列表
  • 集合
    • 有序集合
    • 范围搜索
    • 并集

Redis 可以设置键值对的过期时间,适用于数据的快速访问缓存

高级用法、分布

  • Redis 原生支持发布-订阅模式
  • Redis 可以并不持久,仅用于缓存,也可以设置定期保存快照,并不能存储要求持久性的数据

总结

  • 优点:快速,可以存储比键值更复杂的数据结构
  • 缺点:过期机制,存储于内存(不支持虚拟内存),使其持久性不高

总结

类型的抉择

  1. 关系型
  • 适合:提前知道数据的结构,需要为结构的复杂性付出代价,以实现随后查询的灵活性。你可能不知道事后将会如何查询数据,但是数据本质是规范的
  • 不适合:难以存储高度可变的,或者多层次的数据结构
  1. 键值型
  • 适合:数据相关性不高,例如:用户会话或告诉缓存
  • 不适合:不支持灵活的查询,只有基础的 CRUD
  1. 多列型
  • 适合:适合处理大数据,自带压缩和版本控制
  • 不适合:需要预先知道数据的格式,并依此设计数据库模式
  1. 文档型
  • 适合:高度可变领域,可以很好地映射到面向对象编程的模型
  • 不适合:难以执行联合查询
  1. 图型
  • 适合:适合网络应用,表征节点和关系
  • 不适合:无法网络分区

适用性

在讲究开发速度的初期,选择快速上手的工具,并设计好屏蔽底层实现的接口,便于后期的技术调优。

如果不要求 ACID 和事务处理,大胆尝试 NoSQL!

  • PostgreSQL:其他结构化数据(适用于 ACID,事务处理)
  • Riak:Web 服务(适用于键值存储的 Web 服务)
  • HBase:日志管理系统(适用于大数据分析)
  • MongoDB:应用驱动数据集(适用于经常基于应用变更模型)
  • CouchDB:订单管理,手机端和电脑端数据同步保存(适用于健壮存储,多端存储)
  • Neo4j:社交网络(适用于关系查询,图遍历查询)
  • Redis:数据快速访问缓存(适用于高速缓存)

易用性

  • 提供 Web 管理接口:
    • CouchDB
    • Neo4j
  • 提供 REST 查询接口:
    • Riak
    • MongoDB
    • CouchDB
    • Neo4j
  • 提供高级语言查询接口:
    • PostgreSQL:
      • SQL
    • Riak:
      • JavaScript
    • HBase
    • MongoDB
      • JavaScript
    • CouchDB
    • Neo4j:
      • Gremlin/Groovy
      • Cypher
    • Redis::暂无

功能性

  • 支持 ACID 事务:
    • PostgreSQL
    • HBase(仅打开时)
    • Neo4j
    • Redis
  • 支持全文搜索:
    • PostgreSQL
    • Neo4j
  • 支持 MapReduce
    • Riak
    • HBase
    • MongoDB
    • CouchDB

搜索灵活性

  • PostgreSQL:很灵活
  • Riak:支持 MapReduce 查询
  • HBase:支持 MapReduce 查询
  • MongoDB:支持 MapReduce 查询,很灵活
  • CouchDB:支持 MapReduce 视图查询,不太灵活
  • Neo4j:图遍历较为灵活,且图遍历不同于 MapReduce,是常量时间复杂度的
  • Redis:仅键查询,不灵活

CAP 的权衡

分布性,可用性和一致性。分布性必不可少,可用性和一致性之间的选择:如果节点保持可用,可能会提供不一致的数据,但是可以追求最终一致性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:程序猿惹谁了 设计师:我叫白小胖 返回首页
评论

打赏作者

嘿哈哈哈

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值