本文由 简悦 SimpRead) 转码, 原文地址 mp.weixin.qq.com)

2. 大器晚成 NoSQL

2009 年在亚特兰大的一次重要会议上对 NoSQL 提出了个文雅 & 响亮的口号:

  1. select fun, profit from real_world where relational=false;

本质上 NoSQL 是一类数据库的泛称,具体的可以分为以下几种:

图解|什么是高并发利器NoSQL - 图1

本文只介绍键值对 key-value 型数据库,先看下 NoSQL 名字的来源和含义的几种解读吧:

  • 解读一
    NoSQL 意为 “No SQL” 翻译为 SQL 已死。潜台词是这类数据库没有 SQL 语句,摒弃了老大哥 MySQL 的路子,让它退休。
    嚯,好家伙,口气不小,事实证明,这种 “SQL 已死 “ 的自信确实有点扯犊子了。
  • 解读二
    NoSQL 意为 “Not Only SQL“,显然没有那么嚣张了,倒添了几分谦虚,不仅仅是 SQL,除了继承了老大哥 MySQL 的一些功能,还增加了新东西,听起来还不错的样子。
  • 解读三
    NoSQL 本质上是非关系型数据库,我们都知道关系型数据库一般缩写为 RDS 或者 RDB,所以有人觉得 NoSQL 应该称为 “No Relational Database“,简称” 没关系数据库

综上,我们更倾向于 NoSQL 为 “Not Only SQL“,作为关系型数据库的补充而存在的一种新形式的数据库。

3. 给 NoSQL 一首歌的时间

工欲善其事 必先利其器,大家都这么忙,必须要给个学习 NoSQL 的理由。

  • 场景一
    Leader 大熊给小黑一个需求,这个需求在几千万行的 MySQL 中加个字段,由于是生产环境需要找 DBA 手动执行,好家伙排队大半天才给执行,给小黑气的,太耽误事了太不方便了。

图解|什么是高并发利器NoSQL - 图2

  • 场景二
    Leader 大熊又给了小黑一个需求,让他存储一个数据包,数据包其实是 Json 的串,里面有很多字段,但是现在也不明确 PM 要怎么用,字段是否会调整,所以用 MySQL 存储的话,字段还不能确定,于是小黑加了个 extra 字段来存储后续的扩展,暂时解决了。
    图解|什么是高并发利器NoSQL - 图3
  • 场景三
    Leader 大熊让小黑设计一个并发访问大一些的服务,来完成用户账号体系查询,目前差不多有 4 亿账号 ID,大熊搞了 C++ 服务 & 存储选择了 MySQL,性能怎么也提不上去,真是一筹莫展。

图解|什么是高并发利器NoSQL - 图4

MySQL 横行江湖数十载,无人匹敌,尤其在事务、数据一致性、关联查询等场景具有绝对的统治力,确实是数据库蓝波万。

图解|什么是高并发利器NoSQL - 图5

科技进步和新常用新形式的出现也让 MySQL 有些捉襟见肘,毕竟 MySQL 不是万金油,要保住地位必须与时俱进才行。

在很多场景中我们并不需要事务、强数据一致性、多表关联等特性,所以我们需要一类更轻快的数据库,它就是 NoSQL。

4. MySQL vs NoSQL

我们有必要将 MySQL 和 NoSQL 进行一番对比,来加深印象:

  • MySQL 是高度组织化结构化的数据存储,NoSQL 无结构化存储
  • MySQL 使用结构化查询语句,NoSQL 无查询语言
  • MySQL 需要定义字段和模式,NoSQL 自由扩展
  • MySQL 海量数据时读写性能劣于 NoSQL
  • MySQL 扩展性较差

上面这些好像全是 diss 老大哥 MySQL 的,但是并不是说 MySQL 很弱,相反是 MySQL 非常强悍。

高并发 & 高可用 & 高可扩展的新要求成就了 NoSQL,NoSQL 之所以可以应对这些新场景,和它的设计思想有很大的关系。

或许可以用葡萄来说明为啥 NoSQL 更适用于高并发场景。

  • NoSQL 是一粒一粒的葡萄,存取都非常方便,读写速度快
  • MySQL 是一串葡萄,每一粒都是相互关联的,存取较为麻烦,读写速度慢
    图解|什么是高并发利器NoSQL - 图6

两类数据库的对比就说这么多,我们来看看几款大白在实际工作中用过的 NoSQL 吧!

5. NoSQL 明星项目

开源的 NoSQL 非常多,大白按照层次挑几个典型的代表来和大家分享一下。

NoSQL 可以是单机的,也可以是分布式的,可以根据自己的目的来使用。

今天要介绍的几款数据库:RedisPikaSSDBRocksDBLevelDB

其中 LevelDB 是谷歌开发的,RocksDB 是 Facebook 在 LevelDB 的基础上增加新特性开发的,Redis 则不用多说,SSDB 和 Pika 则是国内开源的类 Redis 的数据库,也非常棒。

图解|什么是高并发利器NoSQL - 图7

接下来看看这几款数据库的特点、联系、底层原理等有趣的东西。

5.1 谷歌出品 LevelDB

LevelDB 是谷歌的 Sanjay Ghemawat 和 Jeff Dean 使用 C++ 开发的单进程 / 单机版持久化的 key-value 数据库,于 2011 年 7 月开源,可以说是重磅产品。

LevelDB 支持了最基础的 key-value 操作:Get/Put/Delete,但是并没有封装其他的东西,严格意义上来说只是 NoSQL 存储引擎。图解|什么是高并发利器NoSQL - 图8

一般来说,机械磁盘最害怕的就是随机读写,磁盘呼噜噜转起来就意味着读写效率在下降。

图解|什么是高并发利器NoSQL - 图9

LevelDB 具有很高的随机写,顺序读 / 写性能,因此 LevelDB 很适合应用在写多读少的场景,真让人好奇高性能的随机写怎么做到的。

5.1.1 LSM 树

很多数据在逻辑上相近,但是在物理存储上却可能相隔很远,这样就会造成大量的随机读写问题,从而降低性能。

LevelDB 实现高性能随机写的秘密武器在于使用 LSM 树存储结构,LSM 树又称为日志结构合并树 (Log-Structured Merge-Tree),它并不是具体的数据结构,而是一种设计思想。

LSM 树对于每次写入操作,并不是直接将最新的数据驻留在磁盘中,而是将数据先放在内存。

当内存数据达到一定的阈值,再将这部分数据真正刷新到磁盘文件中,从而将磁盘随机写转换为内存顺序写,因而获得了极高的写性能,但是这种机制会降低读的性能,总体来说降低部分读性能来大幅提升写性能是值得的

图解|什么是高并发利器NoSQL - 图10

5.1.2 LevelDB 整体架构

图解|什么是高并发利器NoSQL - 图11

LevelDB 存储结构主要由六个部分组成:

  • MemTable:内存数据结构,使用 SkipList 实现,新的数据修改会首先在这里写入,并且有容量限制。
  • Immutable MemTable:待落盘的数据库内存结构,当 MemTable 的大小达到设定的阈值时,会变成 Immutable MemTable,只接受读操作,不再接受写操作,后续会 Flush 到磁盘上。
  • SST Files:Sorted String Table Files,磁盘数据存储文件,分为 Level0 到 LevelN 多层,每一层包含多个 SST 文件,文件内数据有序。
  • Manifest Files:leveldb 元信息清单文件。Manifest 记录 SST 文件在不同 Level 的分布,相当于 SST 文件的索引。
  • Current File:当前正在使用的文件清单文件。

LevelDB 的读写过程和上述的整体架构关系密切,也是先内存后磁盘,一层层读取搜索数据的。

5.2 脸书出品 RocksDB

青出于蓝而胜于蓝。

RocksDB 在 LevelDB 的基础上进行了改进和优化,也成为后续很多 NoSQL 所选择的存储引擎。

RocksDB 仍然是采用 C++ 开发的,并且完全向后兼容了 LevelDB 的接口,可以说是个平滑升级。

图解|什么是高并发利器NoSQL - 图12

5.2.1 RocksDB 提升点

来看看 RocksDB 做了哪些优化和提升:

  • RocksDB 支持一次获取多个 Key,还支持 Key 范围查找,LevelDB 只能获取单个 Key。
  • RocksDB 支持多线程合并,而 LevelDB 是单线程合并的,多核时代前者效率更高。
  • RocksDB 增加了合并时过滤器,对不符合条件的 Key 进行丢弃。
  • RocksDB 可采用多种压缩算法,除了 LevelDB 用的 snappy,还有 zlib、bzip2。
  • RocksDB 支持增量备份和全量备份
  • RocksDB 支持管道式的 Memtable,使用多个 Memtable,LevelDB 只有一个 Memtable。

图解|什么是高并发利器NoSQL - 图13

5.2.2 RocksDB 整体架构

Rocksdb 中引入了 Column Family(列族的概念,所谓列族也就是一系列 kv 组成的数据集,所有的读写操作都需要先指定列族。

每个 ColumnFamily 有自己的 Memtable, SST 文件,所有 ColumnFamily 共享 WAL、Current、Manifest 文件。图解|什么是高并发利器NoSQL - 图14

如果说 LevelDB 是个平民版的 NoSQL 存储引擎,那么 RocksDB 绝对是尊享版,所以很多优秀的 NoSQL 成品都是基于 RocksDB 来封装上层协议和代理支持完成的

5.3 ideawu 的 SSDB

SSDB 是基于 SSD 作为底层存储介质的类 Redis 数据库。

Redis 过于迷人和好用,但是又太昂贵了,所以我们幻想着有一款支持 Redis 数据结构且容量没限制的数据库。

这种数据库将 Redis 的数据结构优势和廉价磁盘介质联合起来,着实让人着迷。

图解|什么是高并发利器NoSQL - 图15

没错,SSDB 就是这样一款 NoSQL 数据库。

图解|什么是高并发利器NoSQL - 图16

目前 SSDB 的维护者并不是特别多,并且在集群化等方面还存在一些问题,不过也算是非常优秀的开源 NoSQL 了。

来简单看下 SSDB 的基本架构

图解|什么是高并发利器NoSQL - 图17

简单来说,SSDB 在 LevelDB 和 Redis 协议之间做了一层转换,从而实现命令和数据的切换,这就是使用存储引擎之前封装附加部分,从而形成完整的 NoSQL。图解|什么是高并发利器NoSQL - 图185.4 360 出品 Pika

其实 SSDB 和 Pika 很有渊源,SSDB 的作者曾经在 360 工作,并且 SSDB 当时在 360 的生产环境中使用广泛,Pika 数据库是 360 基于 RocksDB 开发的集群化高性能 NoSQL。

Pika 是 360DBA 团队和基础架构团队使用 C++ 语言联合开发的类 Redis 存储系统,所以完全支持 Redis 协议

Pika 是一个可持久化的大容量 Redis 存储服务,解决 Redis 由于存储数据量巨大而导致内存不够用的容量瓶颈,并且支持全同步和部分同步。

Pika 还提供了迁移工具,实现 Redis 数据到 Pika 的平滑迁移,Pika 的定位目标并不是取代 redis, 而是作为 redis 的补充,在性能上肯定会低于 Redis。

图解|什么是高并发利器NoSQL - 图19

从整体架构可知,Pika 是多线程实现的,因此对多核使用效率更高,虽然单线程性能不如 Redis,但是多个线程一起上性能也还不错。

Pika 目前在 360 内部使用非常广泛,在一些其他的互联网公司也有使用。

说到底 NoSQL 是一个实践类的项目,本文也不能讲述太多,否则很空洞,意义不大。

5.5 其他 NoSQL

基本上很多互联网公司都会基于 LevelDB 或者 RocksDB 开发一款自己的 Key-Vaule 数据库,有的只支持简单的 string 结构,有的完全兼容 Redis 协议和客户端。

大都是解析 Redis 协议、使用 Redis 的客户端、增加一层命令解析和数据格式解析层、再有可能有多线程支持 & 主从化 & 集群化等。

自己开发一款简单的 NoSQL,可以极大提升自己对于 NoSQL 的理解,在 github 上有很多这样的项目,可以参考学习下。

6. 本文小结

本文粗浅阐述了 NoSQL 的一些相关知识点,以及一些笔者实践过的 NoSQL。

大致原理本质上差不多,但是要打造一款高性能 & 高可用的工业级 NoSQL 是非常困难的

往期精彩:

图解 | 为什么 HTTP3.0 使用 UDP 协议

图解 | 通用搜索引擎背后的技术点

图解 | 什么是一致性哈希算法

图解 | 什么是缓存系统三座大山

图解 | 什么是缺页错误 Page Fault

图解 | 什么是 RSA 算法