到底选择SOL还是NoSQL?看这里!
Posted 51CTO技术栈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了到底选择SOL还是NoSQL?看这里!相关的知识,希望对你有一定的参考价值。
你是否在为系统的数据库来一波大流量就几乎打满 CPU,日常 CPU 居高不下烦恼?你是否在各种 NoSQL 间纠结不定,到底该选用哪种最好?今天的你就是昨天的我,这也是我写这篇文章的初衷。
图片来自 Pexels
作为互联网从业人员,我们要知道关系型数据库(mysql、Oracle)无法满足我们对存储的所有要求,因此对底层存储的选型,对每种存储引擎的理解非常重要。
同时也由于过去一段时间的工作经历,对这块有了一些更多的思考,想通过自己的总结把这块写出来分享给大家。
结构化数据、非结构化数据与半结构化数据
文章的开始,聊一下结构化数据、非结构化数据与半结构化数据,因为数据特点的不同,将在技术上直接影响存储引擎的选型。
首先是结构化数据,根据定义结构化数据指的是由二维表结构来逻辑表达和实现的数据,严格遵循数据格式与长度规范,也称作为行数据,特点为:数据以行为单位,一行数据表示一个实体的信息,每一行数据的属性是相同的。
因此关系型数据库很好契合结构化数据的特点,关系型数据库也是关系型数据最主要的存储与管理引擎。
非结构化数据,指的是数据结构不规则或不完整,没有任何预定义的数据模型,不方便用二维逻辑表来表现的数据,例如办公文档(Word)、文本、图片、html、各类报表、视频音频等。
介于结构化与非结构化数据之间的数据就是半结构化数据了,它是结构化数据的一种形式,虽然不符合二维逻辑这种数据模型结构,但是包含相关标记,用来分割语义元素以及对记录和字段进行分层。
<person>
<name>张三</name>
<age>18</age>
<phone>12345</phone>
</person>
这种结构也被成为自描述的结构。
以关系型数据库的方式做存储的架构演进
阶段一
企业刚发展的阶段,最简单,一个应用服务器配一个关系型数据库,每次读写数据库。
阶段二
无论是使用 MySQL 还是 Oracle 还是别的关系型数据库,数据库通常不会先成为性能瓶颈,通常随着企业规模的扩大,一台应用服务器扛不住上游过来的流量且一台应用服务器会产生单点故障的问题。
因此加应用服务器并且在流量入口使用 nginx 做一层负载均衡,保证把流量均匀打到应用服务器上。
阶段三
随着企业规模的继续扩大,此时由于读写都在同一个数据库上,数据库性能出现一定的瓶颈。
此时简单地做一层读写分离,每次写主库,读备库,主备库之间通过 Binlog 同步数据,就能很大程度上解决这个阶段的数据库性能问题。
阶段四
企业发展越来越好了,业务越来越大了,做了读写分离数据库压力还是越来越大,这时候怎么办呢?
一台数据库扛不住,那我们就分几台吧,做分库分表,对表做垂直拆分,对库做水平拆分。
以扩数据库为例,扩出两台数据库,以一定的单号(例如交易单号),以一定的规则(例如取模)。
交易单号对 2 取模为 0 的丢到数据库 1 去,交易单号对 2 取模为 1 的丢到数据库 2 去,通过这样的方式将写数据库的流量均分到两台数据库上。
一般分库分表会使用 Shard 的方式,通过一个中间件,便于连接管理、数据监控且客户端无需感知数据库 IP。
关系型数据库的优点
上面的方式,看似可以解决问题(实际上确实也能解决很多问题),正常对关系型数据库做一下读写分离+分库分表,支撑个 1W+ 的读写 QPS 还是问题不大的。
但是受限于关系型数据库本身,这套架构方案依然有着明显的不足,下面对利用关系型数据库方式做存储的方案的优点先进行一下分析,后一部分再分析一下缺点,对某个技术的优缺点的充分理解是技术选型的前提。
①易理解
因为行+列的二维表逻辑是非常贴近逻辑世界的一个概念,关系模型相对网状、层次等其他模型更加容易被理解。
②操作方便
通用的 SQL 语言使得操作关系型数据库非常方便,支持 Join 等复杂查询。
③数据一致性
支持 ACID 特性,可以维护数据之间的一致性,这是使用数据库非常重要的一个理由之一。
例如同银行转账,张三转给李四 100 元钱,张三扣 100 元,李四加 100 元,而且必须同时成功或者同时失败,否则就会造成用户的资损。
④数据稳定
数据持久化到磁盘,没有丢失数据风险,支持海量数据存储。
⑤服务稳定
最常用的关系型数据库产品 MySQL、Oracle 服务器性能卓越,服务稳定,通常很少出现宕机异常。
关系型数据库的缺点
数据按行存储,即使只针对其中某一列进行运算,也会将整行数据从存储设备中读入内存,导致 IO 较高。
②为维护索引付出的代价大
为了提供丰富的查询能力,通常热点表都会有多个二级索引,一旦有了二级索引,数据的新增必然伴随着所有二级索引的新增。
数据的更新也必然伴随着所有二级索引的更新,这不可避免地降低了关系型数据库的读写能力,且索引越多读写能力越差。
有机会的话可以看一下自己公司的数据库,除了数据文件不可避免地占空间外,索引占的空间其实也并不少。
③为维护数据一致性付出的代价大
数据一致性是关系型数据库的核心,但是同样为了维护数据一致性的代价也是非常大的。
我们都知道 SQL 标准为事务定义了不同的隔离级别,从低到高依次是读未提交、读已提交、可重复读、串行化,事务隔离级别月底,可能出现的并发异常越多,但是通常而言能提供的并发能力越强。
那么为了保证事务一致性,数据库就需要提供并发控制与故障恢复两种技术,前者用于减少并发异常,后者可以在系统异常的时候保证事务与数据库状态不会被破坏。
对于并发控制,其核心思想就是加锁,无论是乐观锁还是悲观锁,只要提供的隔离级别越高,那么读写性能必然越差。
④水平扩展后带来的种种问题难处理
前文提过,随着企业规模扩大,一种方式是对数据库做分库,做了分库之后,数据迁移(1 个库的数据按照一定规则打到 2 个库中)、跨库 Join(订单数据里有用户数据,两条数据不在同一个库中)、分布式事务处理都是需要考虑的问题,尤其是分布式事务处理,业界当前都没有特别好的解决方案。
⑤表结构扩展不方便
由于数据库存储的是结构化数据,因此表结构 Schema 是固定的,扩展不方便,如果需要修改表结构,需要执行 DDL(data definition language)语句修改,修改期间会导致锁表,部分服务不可用。
⑥全文搜索功能弱
例如 like "%中国真伟大%",只能搜索到"2019年中国真伟大,爱祖国",无法搜索到"中国真是太伟大了"这样的文本,即不具备分词能力。
且 like 查询在"%中国真伟大"这样的搜索条件下,无法命中索引,将会导致查询效率大大降低。
结合 NoSQL 的方式做存储的架构演进
比较简单的 NoSQL 就是缓存:
通常来说,缓存是性能优化的第一选择也是见效最明显的方案。但是,缓存通常都是 KV 型存储且容量有限(基于内存),无法解决所有问题,于是再进一步的优化,我们继续引入其他 NoSQL:
KV 型 NoSQL(代表:Redis)
数据基于内存,读写效率高。
KV 型数据,时间复杂度为 O(1),查询速度快。
只能根据 K 查 V,无法根据 V 查 K。
查询方式单一,只有 KV 的方式,不支持条件查询,多条件查询唯一的做法就是数据冗余,但这会极大的浪费存储空间。
内存是有限的,无法支持海量数据存储。
同样的,由于 KV 型 NoSQL 的存储是基于内存的,会有丢失数据的风险。
读远多于写。
读取能力强。
没有持久化的需求,可以容忍数据丢失,反正丢了再查询一把写入就是了。
搜索型NoSQL(代表:ES)
文档 id。
在该文档中出现的位置情况。
支持分词场景、全文搜索,这是区别于关系型数据库的最大特点。
支持条件查询,支持聚合操作,类似关系型数据库的 Group By,但是功能更加强大,适合做数据分析。
数据写文件无丢失风险,在集群环境下可以方便横向扩展,可承载 PB 级别的数据。
高可用,自动发现新的或者失败的节点,重组和重新平衡数据,确保数据是安全和可访问的。
①性能全靠内存来顶,也是使用的时候最需要注意的点,非常吃硬件资源、吃内存,大数据量下 64G+SSD 基本是标配,算得上是数据库中的爱马仕了。
为什么要专门提一下内存呢,因为内存这个东西是很值钱的,相同的配置多一倍内存,一个月差不多就要多花几百块钱。
至于 ElasticSearch 内存用在什么地方,大概有如下这些:
Indexing Buffer:ElasticSearch 基于 Luence,Lucene 的倒排索引是先在内存里生成,然后定期以 Segment File 的方式刷磁盘的,每个 Segment File 实际就是一个完整的倒排索引。
Segment Memory:倒排索引前面说过是基于关键字的,Lucene 在 4.0 后会将所有关键字以 FST 这种数据结构的方式将所有关键字在启动的时候全量加载到内存,加快查询速度,官方建议至少留系统一半内存给 Lucene。
各类缓存:Filter Cache、Field Cache、Indexing Cache 等,用于提升查询分析性能,例如 Filter Cache 用于缓存使用过的 Filter 的结果集。
Cluter State Buffer:ElasticSearch 被设计为每个 Node 都可以响应用户请求,因此每个 Node 的内存中都包含有一份集群状态的拷贝,一个规模很大的集群这个状态信息可能会非常大。
②读写之间有延迟,写入的数据差不多 1s 样子会被读取到,这也正常,写入的时候自动加入这么多索引肯定影响性能。
③数据结构灵活性不高,ElasticSearch 这个东西,字段一旦建立就没法修改类型了,假如建立的数据表某个字段没有加全文索引,想加上,那么只能把整个表删了再重建。
列式 NoSQL(代表:HBase)
查询时只有指定的列会被读取,不会读取所有列。
存储上节约空间,Null 值不会被存储,一列中有时候会有很多重复数据(尤其是枚举数据,性别、状态等),这类数据可压缩,行式数据库压缩率通常在 3:1~5:1 之间,列式数据库的压缩率一般在 8:1~30:1 左右。
列数据被组织到一起,一次磁盘 IO 可以将一列数据一次性读取到内存中。
海量数据无限存储,PB 级别数据随便存,底层基于 HDFS(Hadoop 文件系统),数据持久化。
读写性能好,只要没有滥用造成数据热点,读写基本随便玩。
横向扩展在关系型数据库及非关系型数据库中都是方便的之一,只需要添加新机器就可以实现数据容量的线性增长,且可用在廉价服务器上,节省成本。
本身没有单点故障,可用性高。
可存储结构化或者半结构化的数据。
列数理论上无限,HBase 本身只对列族数量有要求,建议 1~3 个。
HBase 是 Hadoop 生态的一部分,因此它本身是一款比较重的产品,依赖很多 Hadoop 组件,数据规模不大没必要用,运维还是有点复杂的。
KV 式,不支持条件查询,或者说条件查询非常非常弱吧,HBase 在 Scan 扫描一批数据的情况下还是提供了前缀匹配这种 API 的,条件查询除非定义多个 RowKey 做数据冗余。
不支持分页查询,因为统计不了数据总数。
文档型 NoSQL(代表:MongoDB)
没有预定义的字段,扩展字段容易。
相较于关系型数据库,读写性能优越,命中二级索引的查询不会比关系型数据库慢,对于非索引字段的查询则是全面胜出。
不支持事务操作,虽然 MongoDB 4.0 之后宣称支持事务,但是效果待观测。
多表之间的关联查询不支持(虽然有嵌入文档的方式),Join 查询还是需要多次操作。
空间占用较大,这个是 MongDB 的设计问题,空间预分配机制+删除数据后空间不释放,只有用 db.repairDatabase() 去修复才能释放。
目前没发现 MongoDB 有关系型数据库例如 MySQL 的 Navicat 这种成熟的运维工具。
总结:数据库与 NoSQL 及各种 NoSQL 间的对比
何时选用关系型数据库,何时选用非关系型数据库。
选用非关系型数据库,使用哪种非关系型数据库。
写远高于读
写入量巨大
企业发展之初,明明一个关系型数据库就能搞定且支撑一年的架构,搞一套大而全的技术方案出来。
有一些数据条件查询多,更适合使用 ElasticSearch 做存储降低关系型数据库压力,但是公司成本有限,这种情况下这类数据可以尝试继续使用关系型数据库做存储。
有一类数据格式简单,就是这个 KV 类型且增长量大,但是公司没有 HBase 这方面的人才,运维上可能会有一定难度,出于实际情况考虑,可先用关系型数据库顶一阵子。
直播时间:2019 年 8 月 15 日 20:00
报名方式:请长按识别下方二维码。
编辑:陶家龙、孙淑娟
精彩文章推荐:
以上是关于到底选择SOL还是NoSQL?看这里!的主要内容,如果未能解决你的问题,请参考以下文章