浅谈常见的NoSQL技术方案和选型,你可都知道?
前言
在互联网和大数据的背景下,越来越多的网站、应用系统需要支撑 海量数据存储、高并发请求、高可用、高可扩展性 等特性要求。传统的 关系型数据库 已经难以应对类似的需求,各种各样的 NoSQL(Not Only SQL)数据库因此而产生。
本文将分析 传统数据库 的存在的问题,以及几类 NoSQL 如何解决这些问题。在不同的 业务场景 下,作出正确的 数据存储 技术选型。
正文
1. 传统数据库缺点
2. NoSQL简介
NoSQL,泛指 非关系型 的数据库,可以理解为 关系型 数据库的一个有力补充。
NoSQL 在许多方面性能大大优于 非关系型 数据库的同时,往往也伴随一些特性的缺失。比较常见的是 事务功能 的缺失。 数据库事务正确执行的四个基本要素 ACID 如下:
针对传统 关系型数据库 的不足,下面介绍常见的 5 大类 NoSQL 解决方案:
3. 列式数据库
列式数据库 是以 列相关存储架构 进行数据存储的数据库,主要适合于 批量数据处理 和 即时查询。相对应的是 行式数据库,数据以 行相关的存储架构 进行空间分配,主要适合于 小批量 的 数据处理,常用于 联机事务型数据处理。
基于列式数据库的 列存储特性,可以解决某些特定场景下 关系型数据库 高 I/O 的问题。
3.1. 基本原理
传统关系型数据库是 按照行 来存储数据库,称为 行式数据库,而 列式数据库 是 按照列 来存储数据。
将表放入存储系统中有两种方法,而我们绝大部分是采用 行存储 的。行存储法是将 各行 放入 连续的物理位置,这很像传统的记录和文件系统。
列存储法 是将数据 按照列 存储到数据库中,与 行存储 类似,下图是两种存储方法的图形化解释:
3.2. 常见列式数据库
3.2.1. HBase
HBase 是一个开源的 非关系型分布式数据库(NoSQL),它参考了 谷歌 的 BigTable 建模,实现的编程语言为 Java。它是 Apache 软件基金会的 Hadoop 项目的一部分,运行于 HDFS 文件系统之上,为 Hadoop 提供类似于 BigTable 规模的服务。因此,它可以 容错地 存储 海量稀疏 的数据。
3.2.2. BigTable
BigTable 是一种 压缩的、高性能的、高可扩展性的,基于 Google 文件系统(Google File System,GFS)的数据存储系统,用于存储 大规模结构化数据,适用于云计算。
3.3. 相关特性
3.3.1. 优点
- 高效的储存空间利用率
列式数据库 针对不同列的 数据特征 而发明了 不同算法,使其比 行式数据库 高的多的 压缩率。普通的 行式数据库 一般压缩率在 3:1 到 5:1 左右,而 列式数据库 的压缩率一般在 8:1 到 30:1 左右。
比较常见的,通过 字典表 压缩数据:
下面才是那张表本来的样子。经过 字典表 进行 数据压缩 后,表中的 字符串 才都变成 数字。正因为每个字符串在 字典表 里只 出现了一次,所以达到了 压缩 的目的。
- 查询效率高
读取多条数据的 同一列效率高,因为这些列都是 存储在一起的,一次磁盘操作可以数据的 指定列 全部读取到 内存 中。 下图通过 一条查询 的执行过程说明 列式存储 (以及 数据压缩)的优点。
执行步骤如下:
- 去 字典表 里找到 字符串对应数字(只进行一次字符串比较)。
- 用 数字 去列表里匹配,匹配上的位置设为 1。
- 把 不同列 的 匹配结果 进行 位运算 得到符合所有条件的 记录下标。
- 使用这个 下标组装 出最终的 结果集。
- 适合做聚合操作
- 适合大量的数据而不是小数据
3.3.2. 缺点
- 不适合扫描 小量数据
- 不适合 随机的更新
- 不适合做含有删除和更新的 实时操作
- 单行数据 支持 ACID 的 事务操作,多行数据 的 事务操作,不支持事务的 正常回滚,支持 (Isolation)隔离性、(Durability)持久性,不能保证 (Atomicity)原子性、(Consistency)一致性。
3.4. 应用场景
列数据库的适用场景,以 HBase 为例说明:
- 适合 大数据量 (100TB 级数据),有 快速随机访问 的需求。
- 适合 写密集型 应用,每天写入量巨大,而 读数量相对较小 的应用,比如 IM 的 历史消息,游戏日志 等等。
- 适合不需要 复杂查询 条件来查询数据的应用。HBase 只支持基于 rowkey 的查询,对于 HBase 来说,单条记录 或者 小范围的查询 是可以接受的。大范围 的查询由于 分布式 的原因,可能在 性能 上有点影响。HBase 不适用于有 join,多级索引,表关系复杂 的数据模型。
- 对 性能 和 可靠性 要求非常高的应用。
- 由于 HBase 本身没有 单点故障,可用性 非常高。
- 适合 数据量较大,而且 增长量 无法预估的应用,需要进行优雅的数据扩展的应用。HBase 支持 在线扩展,即使在一段时间内,数据量呈 井喷式增长,也可以通过 HBase 横向扩展 来满足功能。
- 存储 结构化 和 半结构化 的数据。
4. K-V数据库
4.1. 基本概念
指的是使用 键值(key-value)存储的数据库,其数据按照 键值对 的形式进行 组织、索引 和 存储。
KV 存储非常适合不涉及过多 数据关系 业务的数据。它能够有效减少 读写磁盘 的次数,比 SQL 数据库存储 拥有更好的 读写性能,能够解决 关系型数据库 无法存储 数据结构 的问题。
4.2. 常见K-V数据库
4.2.1. Redis
Redis 是一个使用 ANSI C 编写的 开源、支持网络、基于内存、可选持久性 的 键值对存储 数据库。Redis 是目前最流行的 键值对存储 数据库之一。
4.2.2. Cassandra
Apache Cassandra(社区内一般简称为 C*)是一套 开源的分布式 NoSQL 数据库系统。它最初由 Facebook 开发,用于储存 收件箱 等简单格式数据,集 Google BigTable 的 数据模型 与 Amazon Dynamo 的 完全分布式 架构于一身。Cassandra 是一种流行的 分布式结构化 数据存储方案。
4.2.3. Memcached
Memcached 是一个 开放源代码、高性能、分配的 内存对象缓存系统。用于加速动态 web 应用程序,减轻关系型数据库负载。它可以应对 任意多个连接,使用 非阻塞的网络 IO。由于它的工作机制是在内存中开辟一块空间,然后建立一个 Hash 表,Memcached 自管理这些 Hash 表。
Memcached 简单而强大。它简单的设计促进 迅速部署,易于发现所面临的问题,解决了很多 大型数据缓存。
4.2.4. LevelDB
LevelDB 是一个由 Google 所研发的 键/值对(Key/Value Pair)嵌入式 数据库管理系统 编程库,以开源的 BSD 许可证发布。
4.3. 相关特性
K-V 数据库的相关特性,以 Redis 为例说明:
4.3.1. 优点
- 性能极高
Redis 单机最高能支持超过 10W 的 TPS。
- 丰富的数据类型
Redis 支持包括 String,Hash,List,Set,Sorted Set,Bitmap 和 Hyperloglog 等数据结构。
- 丰富的特性
Redis 还支持 publish/subscribe,通知,key 过期 等特性。
4.3.2. 缺点
- Redis 事务 不能支持 原子性 和 持久性(A 和 D),只支持 隔离性 和 一致性(I 和 C)。
4.4 应用场景
4.4.1. 适用场景
- 适合存储 用户信息(比如 会话)、配置文件、参数、购物车 等等。这些信息一般都和 ID 挂钩。
4.4.2. 不适用场景
- 不适合需要通过 值 来查询,而不是 键 来查询。Key-Value 数据库中根本没有通过 值查询 的途径。
- 不适合需要储存 数据之间的关系。在 Key-Value 数据库中不能通过 两个或以上 的键来 关联数据。
- 不适合需要支持 事务 的场景。在 Key-Value 数据库中 故障产生 时不可以进行 回滚。
5. 文档型数据库
5.1. 基本概念
文档数据库 用于将 半结构化数据 存储为 文档 的一种数据库。文档数据库通常以 JSON 或 XML 格式存储数据。
- 由于文档数据库的 no-schema 特性,可以 存储 和 读取 任意数据。
- 由于使用的 数据格式 是 JSON 或者 BSON,因为 JSON 数据是 自描述的,无需在使用前定义字段,读取一个 JSON 中 不存在的字段 也不会导致 SQL 那样的语法错误,可以解决关系型数据库 表结构 schema 扩展不方便 的问题。
5.2. 常见文档数据库
5.2.1. MongoDB
MongoDB 是一个基于 分布式文件存储 的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的 高性能 数据存储解决方案。
MongoDB 是一个介于 关系数据库 和 非关系数据库 之间的产品,是非关系数据库当中功能 最丰富,最像关系数据库的 NoSQL。
5.2.2. CouchDB
CouchDB 是用 Erlang 开发的 面向文档 的 分布式 数据库,用于存储 半结构化 的数据,比较类似 lucene 的 index 结构。
CouchDB 支持 RESTful API,它使用 JSON 作为 存储格式,JavaScript 作为 查询语言,MapReduce 和 HTTP 作为 API 的 NoSQL 数据库。其中一个显著的功能就是 多主复制 功能。除此之外,CouchDB 构建在强大的 B- 树储存引擎 之上。
[图片上传失败...(image-fe7dcf-1536933787428)]
5.3. 相关特性
文档型数据库 的相关特性,以 MongoDB 为例进行说明:
5.3.1. 优点
- 新增字段简单不需要像关系型数据库一样,先执行 DDL 语句 修改表结构,程序代码 直接读写 即可。
- 容易兼容 历史数据。对于历史数据,即使没有新增的字段,也不会导致错误,只会返回 空值,此时 代码兼容处理 即可。
- 容易存储复杂数据。JSON 是一种强大的 描述语言,能够描述复杂的 数据结构。
5.3.2. 缺点
相比 传统关系型数据库,文档数据库的缺点,主要是对 多条数据记录 的 事务支持较弱,具体体现如下:
- Atomicity(原子性):仅支持 单行/文档级原子性,不支持 多行、多文档、多语句原子性。
- Isolation(隔离性):隔离级别仅支持 已提交读(Read committed)级别,可能导致 不可重复读,幻读 的问题。
- 不支持 复杂查询。例如 join 查询,如果需要 join 查询,需要 多次操作数据库。
5.4. 应用场景
5.4.1. 适用场景
- 数据量 很大或者未来会变得很大。
- 表结构不明确,且 字段 在 不断增加,例如内容管理系统,信息管理系统。
5.4.2. 不适用场景
- 在不同的文档上需要添加 事务。Document-Oriented 数据库并不支持 文档间的事务。
- 多个文档之间需要 复杂的查询,例如 join 操作。
6. 全文搜索引擎
6.1. 基本概念
传统关系型数据库,主要通过 索引 来达到 快速查询 的目的。在 全文搜索 的业务下,索引 也无能为力,主要体现在以下几方面:
- 全文搜索的条件,可以随意 排列组合,如果通过索引来满足,则索引的 数量非常多。
- 全文搜索的 模糊匹配方式,索引 无法满足,只能用 like 进行查询,而 like 查询是 整表扫描,效率非常低。
全文搜索引擎的出现,正是解决关系型数据库 全文搜索较弱 的问题。
6.2. 基本原理
全文搜索引擎 的技术原理称为 倒排索引(inverted index),是一种 索引方法,其基本原理是建立 单词 到 文档 的索引。与之相对是,是 正排索引,其基本原理是建立 文档 到 单词 的索引。
- 现在有如下文档集合:
[图片上传失败...(image-695617-1536933787428)]
- 正排索引 得到索引如下:
[图片上传失败...(image-35c3a2-1536933787428)]
可见,正排索引 适用于根据 文档名称 查询 文档内容。
- 简单的 倒排索引 如下:
- 带有 单词频率 信息的 倒排索引 如下:
可见,倒排索引 适用于根据 关键词 来查询 文档内容。
6.3. 常见全文搜索引擎
6.3.1. ElasticSearch
ElasticSearch 是一个基于 Apache Lucene 的 搜索引擎。它提供了一个 分布式,多租户 对全文搜索引擎。ElasticSearch 是用 Java 开发的,对外提供 RESTful Web 接口。根据 DB-Engines 排名,ElasticSearch 是最受欢迎的 企业搜索引擎。
6.3.2. Solr
Solr 是 Apache Lucene 项目的 开源企业搜索平台。其主要功能包括 全文检索、命中标示、分面搜索、动态聚类、数据库集成,以及 富文本(比如 Word、PDF)处理等等。Solr 是高度 可扩展 的,并提供了 分布式搜索 和 索引复制。
6.4. 相关特性
全文搜索引擎,以 ElasticSearch 为例说明:
6.4.1. 优点
- 查询效率高,适用于对 海量数据 进行 近实时 的处理。
- 可扩展性
- 基于 集群 环境可以方便 横向扩展,可以承载 PB 级的数据。
- 支持 高可用,ElasticSearch 集群弹性灵活,可以发现新的或失败的节点,重组 和 重新平衡 数据,确保数据是 安全 和 可访问的。
6.4.2. 缺点
- 事务的 ACID 支持不足,单一文档 的数据是支持 ACID 的。对于 多个文档 的 事务操作,不支持事务的 正常回滚。支持(Isolation)隔离性(基于 乐观锁机制)和(Durability)持久性,不支持(Atomicity)原子性,(Consistency)一致性。
- 对类似数据库中,通过 外键 进行 多表关联的复杂操作支持较弱。
- 读写 有一定 延时,写入的数据,最快 1s 中能被检索到。
- 更新 性能较低,底层实现是 先删数据,再 插入新数据。
- 内存占用大,因为 Lucene 将 索引部分 加载到 内存 中。
6.5. 应用场景
6.5.1. 适用场景
- 分布式的 搜索引擎 和 数据分析引擎。
- 全文检索,结构化检索 以及 数据分析。
- 对海量数据进行 近实时 的处理,可以将 海量数据 分散到 多台服务器 上去 存储 和 检索。
6.5.2. 不适用场景
- 数据需要 频繁更新。
- 需要 复杂关联查询。
7. 图形数据库
7.1. 基本概念
图形数据库 应用 图形理论 存储 实体 之间的 关系信息。最常见例子就是 社会网络中人与人之间的关系。关系型数据库 用于存储这种 关系型数据 的效果并不好,其查询 复杂、缓慢、超出预期。
图形数据库 的独特设计弥补了这个缺陷,解决 关系型 数据库 存储 和 处理复杂关系型数据 功能较弱的问题。
7.2. 常见图形数据库
7.2.1. Neo4j
Neo4j 是一个 高性能的,NOSQL 图形数据库,它将 结构化数据 存储在 “图形网络上” 而不是 “表中”。它是一个 嵌入式的、基于磁盘的、具备完全的 事务特性 的 Java 持久化引擎。
Neo4j 也可以被看作是一个 高性能的图引擎。程序员工作在一个 面向对象的、灵活的网络结构 下而不是 严格、静态 的 表中。
7.2.2. ArangoDB
ArangoDB 是一个 原生多模型 数据库系统。数据库系统支持 三个 重要的 数据模型(键/值,文档,图形)。
ArangoDB 包含一个 数据库核心 和 统一查询语言 AQL(ArangoDB 查询语言)。查询语言是 声明性的,允许在 单个查询 中 组合 不同的 数据访问模式。ArangoDB 是一个 NoSQL 数据库系统,但 AQL 在很多方面与 SQL 都类似。
7.3. 基本原理
图形数据库,以 Neo4j 为例说明:
- Neo4j 使用 数据结构 中图(graph)的概念来进行 建模。
- Neo4j 中两个最基本的概念是 节点 和 边。节点 表示 实体,边 则表示 实体之间的关系。节点 和 边 都可以有自己的 属性。不同 实体 通过各种不同的 关系 关联起来,形成复杂的 对象图。
针对关系数据,两种数据库的 存储结构 分别如下:
在 Neo4j 中,存储节点 时使用了 index-free adjacency,即 每个节点 都有指向其 邻居节点 的 指针。这样就可以在 O(1) 的 复杂度 内找到 邻居节点。另外,按照官方的说法,在 Neo4j 中 边 s是最重要的,是 first-class entities,需要 单独存储。这有利于在 图遍历 的时候 提高速度,也可以很方便地以 任何方向 进行遍历。
7.4. 相关特性
7.4.1. 优点
- 高性能表现
图的遍历 是 图数据结构 所具有的独特算法,即从 一个节点 开始,根据其连接的 关系,可以快速和方便地找出它的 邻近节点。这种查找数据的方法不受 数据量大小 的影响,因为 邻近查询 始终查找的是 有限的局部数据,不会对 整个数据库 进行搜索。
- 设计的灵活性
数据结构 的自然伸展特性,以及其 非结构化 的 数据格式,让图数据库设计可以具有很大的 伸缩性 和 灵活性。因为随着需求的变化而增加的 节点、关系 及其 属性,并不会影响到 原来数据 的正常使用。
- 开发的敏捷性
数据模型 直接明了,从需求的讨论开始,到程序开发和实现,基本上不会有大的变化。
- 完全支持ACID
不像别的 NoSQL 数据库,Neo4j 还完全具有 事务管理特性,完全支持 ACID 事务管理。
7.4.2. 缺点
- 节点,关系 和它们的 属性 的数量被 限制。
- 不支持 拆分。
7.5. 应用场景
7.5.1. 适用场景
- 在一些 关系性强 的数据应用,例如 社交网络。
- 推荐引擎,将数据以 图的形式 表现,非常有益于推荐的制定。
7.5.2. 不适用场景
- 记录大量 基于事件 的数据,如日志记录、传感器数据。
- 对大规模 分布式数据 进行处理,类似于 Hadoop。
- 不适用于应该保存在 关系型数据库 中的 结构化数据。
- 二进制数据存储。
小结
关于 关系型数据库 和 NoSQL 数据库 的选型,往往需要考虑几个指标:
- 数据量
- 并发量
- 实时性
- 一致性要求
- 读写分布
- 数据类型
- 安全性
- 运维成本
常见的系统数据库选型参考如下:
系统类型 数据库选型 企业内部管理系统 例如运营系统,数据量少,并发量小,首选考虑 关系型数据库 互联网大流量系统 例如电商单品页,后台考虑选 关系型数据库,前台考虑选 内存型数据库 日志型系统 原始数据 考虑选 列式数据库,日志搜索 考虑选 倒排索引 搜索型系统 例如站内搜索,非通用搜索,商品搜索,后台考虑选 关系型数据库,前台考虑选 倒排索引 事务型系统 例如库存管理,交易,记账,考虑选 关系型数据库 + 缓存数据库 + 一致性型协议 离线计算 例如大量数据分析,考虑选 列式数据库 或者 关系型数据库 都可以 实时计算 例如实时监控,可以考虑选 内存型数据库 或者 列式数据库 设计实践中,要基于需求、业务驱动架构,无论选用 RDB/NoSQL/DRDB。一定是以需求为导向,最终数据存储方案,必然是考虑各种 权衡 的综合性设计。