什么是NoSQL?
NoSQL(NoSQL = Not Only SQL ),即 “不仅仅是SQL”,指的是非关系型的数据库。是对不同于传统的关系型数据库管理系统的统称。
关系型数据库
关系型数据库,是指采用了关系模型来组织数据的数据库。1970年由IBM的研究员E.F.Codd博士首先提出的,在之后的几十年中,关系模型的概念得到了充分的发展并逐渐成为主流数据库结构的主流模型。
关系模型中常用的概念:
- 关系:可以理解为一张二维表,每个关系都具有一个关系名,就是通常说的表名。
- 元组:可以理解为二维表中的一行,在数据库中经常被称为记录。
- 属性:可以理解为二维表中的一列,在数据库中经常被称为字段。
- 域:属性的取值范围,也就是数据库中某一列的取值限制。
- 关键字:一组可以唯一标识元组的属性,数据库中常称为主键,由一个或多个列组成。
- 关系模式:指对关系的描述。其格式为:关系名(属性1,属性2, … … ,属性N),在数据库中成为表结构。
优点
- 简单:二维表结构是非常贴近逻辑世界的一个概念,关系模型相对网状、层次等其他模型来说更容易理解。
- 方便:SQL语言使得操作关系型数据库非常方便。
- 易维护:数据完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了数据冗余和数据不一致的概率。
缺点
- 关系型数据库通过存储行来储存数据,无法存储复杂的数据结构(5.7 开始支持存储 Json)。例如一个医生有多个患者,在医生这张表里面关系型数据库一般不好存储对应所有患者的Id,需要额外建立一张表来存储关联关系。
- 关系数据库的 schema 扩展很不方便。当生产环境单表数据量达到百万千万以上对表进行 DDL 操作可能导致长时间(几分钟到几十分钟)的锁表,影响线上的业务。
- 关系数据库在大数据场景下 I/O 较高,关系型数据库在做一个统计,计算等场景处理时需要将整行数据读入内存,性能比较差。
- 全文搜索能力不行,关系型数据库一般只能使用 like 前缀匹配搜素,遇到复杂的全文搜索无法使用索引,只能一行行的扫描匹配。
- 关系型数据库是很难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,数据库却没有办法像 Web server 和 App server 那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。
非关系型数据库
K-V 数据库
- 键值(Key-Value)存储数据库,这是一种 NoSQL(非关系型数据库)模型,其数据按照键值对的形式进行组织、索引和存储。KV 存储非常适合不涉及过多数据关系业务关系的业务数据,同时能有效减少读写磁盘的次数,比 SQL 数据库存储拥有更好的读写性能。
- Redis 是 K-V 数据库的典型代表,它支持丰富的数据结构,包括string、hash、list、set、sorted sorted set、bitmap 和 hyperloglog。Redis 已经被大量公司广泛于多种场景的生产环境。接下来介绍 Redis 的一些关键特性。
Redis 特性
- 速度快:Redis 具有非常高的性能,使用 Redis 自带的 benchmark 工具在个人电脑测试得出 QPS 大约在 8W 左右。为什么 Redis 的速度如此之快,简单归纳为以下四点:
- Redis 所有的数据都是放到内存中的,内存的速写速度远远高于硬盘。
- Redis 是 C 语言实现的,一般来说 C 语言更加接近底层的操作系统,所以执行速度比较快。
- Redis 使用了单线程的架构,避免了多线程的竞争问题。
- Reids 代码质量高,作者对于 Redis 源代码可谓是精打细磨。
- 基于键值对的数据结构:几乎所有的编程语言都支持键值的数据结构,例如 Java 中的 Map,Python 里面的 Dict。与其他键值数据库不同的是 Redis 键值中的值不仅仅是字符串,还可以是复杂的数据结构,这样不仅能满足多种业务场景而且能提高开发效率。
- 丰富的功能: Redis 不经能提供丰富的数据结构,还具备其他丰富的功能。
- 键过期:可以用于实现缓存系统。
- 订阅发布:可以用于实现消息系统。
- Lua 脚本:利用 Lua 创造新的 Reids 命令。
- 事务:一定程度保证事务的部分特性。
- Pipeline:批量执行命令,减少网络开销。
- 简单稳定:早起版本的 Redis 代码只有 2 万行,3.0版本以后加入的结群特性代码增长到 5 万行左右,相对于其他很多 NoSQL 数据库代码量少很多,使得运维何研同学吃透和研究要容易。另外 Redis 是单线程模型,不仅使服务端处理模型变得简单,而且也是客户端的开发同样简单。 Reids 不但简单而且稳定,生产患者很少出现因为 Reids 本身的 Bug 导致宕机的情况。
- 客户端语言多:Reids 提供了简单的 TCP 协议,很多编程语言接入到 Reids, Reids 的客户端几乎覆盖了主流的编程语言,例如 Java,PHP,Python,C,C++,Nodejs 等。
- 持久化:内存数据是不安全的,一旦发生断电或者机器故障,数据可能会丢失。Redis 提供了 RDB 和 AOF 两种持久化方式,提高了数据安全性。
- 主从复制:Redis 提供了复制功能,主从复制是分布式 Redis 的基础。
- 高可用:Redis 从 2.8 提供了高可用的实现 Reids Sentinel,能够保证故障发现和转移。Redis 从 3.0 版本提供了真正的分布式实现 Redis Cluster。
Redis 缺点
- Redis 的主要缺点是无法支持和关系型数据库一样的事务能力(ACID),Redis 的事务支持的不是完整事务,Redis 的事务只能保证隔离性和一致性(I 和 C),无法支持原子性和持久性(A 和 D)。所以 Redis 适用那种并非一定需要完整事务的业务场景。
文档数据库
- 文档数据库(也称为文档型数据库)是一种 NoSQL 数据库,旨在将半结构化数据存储为文档。文档数据库通常以 JSON 或 XML 格式存储数据。文档数据库相比于关系型数据库却别就是文档数据库最大的特点就是 no-schema,可以存储和读取任意的数据。
- no-schema 能为业务发开带来以下几个明显的优势:
- 字段的调整简单,无需像关系型数据库那样执行 DDL 语句调整字段,程序代码调整即可。
- 历史数据没有问题。
- 新增字段历史数据没有,读取返回空即可。
- 处理复杂数据结构简单
- JSON 是一种强大的描述语言,能够描述复杂的数据结构。比如我们设计一个商品系统,商品的属性有ID,类目,状态,名称,以及 SKU(一个商品可能有多个 SKU)。SKU 是一个列表,SKU 的属性有ID,名称,价格,渠道,属性等。其中 SKU 的渠道和属性也是列表。这样复杂的数据结构使用关系型数据库需要多张表才能描述,使用文档数据库一个 JSON 就可以描述。
{
"id": 106570,
"majorCategoryId": 17,
"minorCategoryId": 17006,
"status": 1,
"name": "手写处方",
"description": "",
"intro": "",
"productPic": "",
"thumbnail": "",
"skus": [
{
"id": 106570,
"name": "手写处方",
"price": 0,
"originPrice": 0,
"storage": -1,
"status": 1,
"channels": [
{
"channeld": 1,
"info": {
"price": 0,
"alias": "手写处方"
}
}
],
"properties": []
}
]
}
- 文档数据库的代表是 MongoDB,MongoDB 由 C++ 编写,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它具有如下特点:
- 模式自由 :可以把不同结构的文档存储在同一个数据库里
- 面向集合的存储:适合存储 JSON风格文件的形式,
- 完整的索引支持:对任何属性可索引,
- 复制和高可用性:支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制。复制的主要目的是提供冗余及自动故障转移。
- 自动分片:支持云级别的伸缩性:自动分片功能支持水平的数据库集群,可动态添加额外的机器。
- 丰富的查询:支持丰富的查询表达方式,查询指令使用JSON形式的标记,可轻易查询文档中的内嵌的对象及数组。
- 快速就地更新:查询优化器会分析查询表达式,并生成一个高效的查询计划。
- 高效的传统存储方式:支持二进制数据及大型对象(如照片或图片)。
缺点
- 事务:文档型数据库对于事务的处理不如关系型数据库。
- 一般来说单个文档的操作是原子的,然而涉及多个文档的操作,通常被作为一个 “事务” 不是原子性的。因为文档可以是相当复杂并且包含多个嵌套文档,单文档的原子性对许多实际用例提供了支持。尽管单文档操作是原子性的,在某些情况下,需要多文档事务。
- 使用 MongoDB 来存储商品库存,系统创建订单的时候首先需要减扣库存,然后再创建订单。这是一个事务操作,用关系数据库来实现就很简单,但如果用 MongoDB 来实现,就无法做到事务性。异常情况下可能出现库存被扣减了,但订单没有创建的情况。
- 新版的 MongoDB 能够支持类似关系型数据库的事务。MongoDB 联合创始人以及 CTO Eliot Horowitz 在其官方博客上宣布 MongoDB 即将在 4.0 版本中支持跨文档事务,也就是说,MongoDB 即将成为唯一一款同时具备速度、灵活性和支持 ACID 事务特性的文档数据库。
列数据库
-
关系数据库针对存储数据行进行了优化,通常用于事务性应用程序,而列式数据库针对快速检索数据列进行了优化,通常用于分析应用程序。适用于数据库表的列式存储是分析查询性能的一个重要组成部分,因为它极大地降低了整体磁盘 I/O 要求,并减少了您需要从磁盘载入的数据量。与其他 NoSQL 数据库一样,列式数据库旨在利用低成本硬件的分布式群集进行横向扩展,进而提高吞吐量,从而使其适用于数据仓库和大数据处理。
-
列数据库和行数据库简单比较:
-
在只需要根据某几列来聚合数据的时候按列的数据组织方式更有效。因为这样只需要读取一部分数据,要比读取全部数据更快.
-
当只需要修改某一列值的时候按列的数据组织方式更有效。因为可以直接找到某列数据并修改,而与行中的其他列无关。
-
当需要某行的多列数据的时候按行的数据组织方式更有效。当行中数据不是太多的情况下一次硬盘寻址就可以获得该行的所有数据。
-
在新增行数据的时候,如果各列都有值,那么按行的数据组织方式会更有效,因为只需要一次硬盘寻址就可以写入整行的全部数据.
以上我们得知列数据库在特定的场景性能比行数据库更好,典型的场景就是海量数据分析统计。比如我们要统计用户的体重超过 50kg 的人员数据,使用列数据会更好,只需要统计体重这个列即可,如果是行数据库则需要先将用户所用列取出来,然后在查询体重这一列,I/O 开销更大。除此之外,列数据库具有更好的数据压缩比例,普通的行式数据库一般压缩率在 3:1 到 5:1 左右,而列式数据库的压缩率一般在 8:1 到 30:1 左右。 -
列式数据库的代表包括:HBase、SAP HANA、Amazon Redshift、Sybase IQ、ParAccel、Sand/DNA Analytics、Vertica、Aster Data Systems和greenplum等。
缺点
- 不适合扫描小量数据。
- 不适合随机的更新。
- 批量更新情况各异,有的优化的比较好的列式数据库(比如Vertica)表现比较好,有些没有针对更新的数据库表现比较差。
- 不适合做含有删除和更新的实时操作。
全文搜索
- 关系型数据库通过索引加快检索的效率,但是在全文搜索的场景下,关系型数据库往往无法满足业务场景。
- 全文搜索的存在检索的字段可能很多,多个字段组合检索的场景,使用关系型数据库的的索引会导致索引数据过多,空间占用大,I/O 操作多,影响写入性能等问题。
- 全文搜索的模糊匹配关系型数据库只能使用 like 处理,like 只能解决通过前缀匹配能够使用索引,其他情况只能全表扫描,影响查询性能。
全文搜索原理:倒排索引
“倒排索引” 是一种索引方法,其基本原理是建立单词到文档的索引。与之相对的 “正排索引” 的基本原理是建立文档到单词的索引。倒排索引适用于根据关键词来查询文档内容, 主流的搜索引擎 Google,百度的搜索业务场景非常适用倒排索引。
Elasticsearch
全文搜索的典型代表是 Elasticsearch。
- Elastcisearch 是分布式的文档存储方式。它能存储和检索复杂的数据结构——序列化成为 JSON 文档——以实时的方式。
- 在 Elasticsearch 中,每个字段的所有数据都是默认被索引的。即每个字段都有为了快速检索设置的专用倒排索引。而且,不像其他多数的数据库,它能在相同的查询中使用所有倒排索引,并以惊人的速度返回结果。
总结
关系型和 NoSQL 数据库的选型。考虑几个指标,数据量、并发量、实时性、一致性要求、读写分布和类型、安全性、运维性等。根据这些指标,软件系统可分成几类。
- 1.管理型系统,如运营类系统,首选关系型。
- 2.大流量系统,如电商单品页的某个服务,后台选关系型,前台选内存型。
- 3.日志型系统,原始数据选列式,日志搜索选倒排索引。
- 4.搜索型系统,指站内搜索,非通用搜索,如商品搜索,后台选关系型,前台选倒排索引。
- 5.事务型系统,如库存、交易、记账,选关系型+缓存+一致性协议,或新型关系数据库。
- 6.离线计算,如大量数据分析,首选列式,关系型也可以。
- 7.实时计算,如实时监控,可以选时序数据库,或列式数据库。