11 | NoSQL:在高并发场景下,数据库和NoSQL如何做到互补?

之前介绍了如何将传统的关系型数据库改造成分布式存储服务,以抵抗高并发和大流量冲击。

对于存储服务来说,我们一般会从两个方面对它做改造:
1.提升它的读写性能,尤其是读性能,因为我们面对的多是一些读多写少的产品。
2.增加它在存储上的扩展能力,从而应对大数据量的存储需求。
之前学习的读写分离和分库分表就是从这两方面出发,改造传统的关系型数据库的,但是仍有一些问题无法解决。

比如,在微博项目中,关系的数据量大到了千亿,即使分隔成1024个库表,每张表的数据量也达到了亿级别,并且关系的数据量还在以极快的速度增加,即使你分割成再多的库表,数据量也会很快增加到瓶颈。这个问题用传统数据库很难根本解决,因为它在扩展性方面是很弱的,这时就可以利用NoSQL,因为它有着天生分布式的能力,能够提供优秀的读写性能,可以很好的补充传统关系型数据库的短板,那么它是如何做到的?

带你掌握如何用NoSQL数据库和关系型数据库互补,共同承担高并发和大流量的冲击。

什么是NoSQL?

NoSQL指的是不同于传统的关系型数据库的其他数据库系统的统称,它不使用SQL作为查询语言,提供优秀的横向扩展能力和读写性能,非常契合互联网项目高并发大数据的特点。所以一些大厂很倾向使用它

NoSQL数据库发展到现在,出现了多种类型:

  • Redis,levelDB这样的KV存储。这类存储相对于传统的数据库的优势是极高的读写性能,一般对性能有比较高的要求的场景会使用。
  • Hbase、Cassandra这样的列式存储数据库。这种数据库的特点是数据不像传统数据库以行为单位来存储,而是以列来存储,适用于一些离线数据统计的场景。
  • MongoDB、CouchDB这样的文档型数据库。这种数据库的特点是Schema Free(模式自由),数据表中的字段可以任意扩展,比如电商系统中的商品有非常多的字段,并且不同品类的商品的字段也都不尽相同,使用关系型数据库就需要不断增加字段支持,而用文档型数据库就简单很多了。

在NoSQL数据库刚刚被应用时,它被认为是可以替代关系型数据库的,也许是因为以下几个方面的原因:

  • 弥补了传统数据库在性能方面的不足;
  • 数据库变更方便,不需要更改原先的数据结构;
  • 适合互联网项目常见的大数据量的场景;

不过这种看法是个误区,因为慢慢的我们发现在业务开发的场景下还是需要利用SQL语句的强大的查询功能以及事务索引等功能。NoSQL只能作为一些场景的补充。

NoSQL数据库是如何做到与关系型数据库互补的?

**使用NoSQL提升写入性能

数据库系统大多使用的是传统的机械硬盘,对于机械硬盘的访问方式有两种:一种是随机IO;另一种是顺序IO。随机IO就需要花费时间做昂贵的磁盘寻道,一般来说,它的读写效率要比顺序IO小两到三个数量级,所以我们想要提升写入的性能就要尽量减少随机IO。

以MySQL的InnoDB存储引擎来说,更新binlog,redolog,undolog都是在做顺序IO,而更新datafile和索引文件则是在做随机IO,而为了减少随机IO的发生,关系型数据库已经做了很多的优化,比如说写入时先写入内存,然后批量刷新到硬盘上,但是随机IO还是会发生。

索引在InnoDB引擎中是以B+树方式来组织的,二MySQL主键是聚簇索引(索引类型,数据与索引数据放在一起),既然数据和索引数据放在一起,那么在数据插入或者更新的时候,我们需要找到要插入的位置,然后把数据写到特定的位置上,这样就产生了随机的IO。而且一旦发生了页分裂,就不可避免的做数据的移动,也会极大地损耗写入性能。

NoSQ数据库是怎么解决这个问题的?
它们有多种解决方式,这里讲一种最常见的方案,就是很多NoSQL数据库都在使用基于LSM树的存储引擎,这种算法使用最多。

LSM树牺牲了一定的读性能来换取写入数据的高性能,Hbase,Cassandra,LevelDB都是用这种算法作为存储引擎。
它的思想很简单,数据首先会写入到一个叫做MemTable的内存结构中,在MemTable中数据是按照写入的key来排序的。为了防止MemTable里面的数据因为机器掉电或者重启而丢失,一把会通过写Write Ahead Log的方式将数据备份在磁盘上。
MemTable在积累到一定规模时,它会被刷新生成一个新的文件,我们把这个文件叫做SSTable(Sorted String Table)。当SSTable达到一定的数量时,我们会将这些SSTable合并,减少文件的数量,因为SSTable都是有序的,所以合并的速度也很快。
当从LSM树里面读数据时,我们首先从MemTable中查找数据,如果数据没有找到,再从SSTable中查找数据。因为存储的书都是有序的,所以查找的效率是很高的,只是因为数据被拆分成了多个SSTable,所以读取的效率会低于B+树索引。


image.png

和LSM树类似的算法有很多,比如说TokuDB使用的名为Fractal tree的索引结构,它们的核心思想就是将随机IO变成顺序的IO,从而提升写入的性能。

场景补充
除了可以提升性能之外,NoSQL数据库还可以在某些场景下作为传统关系型数据库的补充,来看一个具体例子。
假设某一天,项目有新需求:需要支持按照商品的名称模糊搜索到对应的商品。
不就是在数据库里执行类似“LIKE ‘%商品%’”的语句么?实际执行中,你会发现这类语句并不是都能使用到索引,只有后模糊匹配的语句才能使用索引。比如语句“name LIKE '%电冰箱%'”就没有使用到字name”上的索引,而“name LIKE '索尼%'”就使用了name上的索引。而一旦没有使用索引就会扫描全表的数据,这是无法接受的。

通过骨骼搜索发现,大家都在使用开源组件Elasticsearch来支持搜索的请求,它本身是基于"倒排索引"来实现的。什么是倒叙索引呢?

倒排索引是指将记录中 的某些列做分词,,然后形成的分词与记录ID之间的映射关系。比如说,电商项目里有以下记录:

image.png

那么,我们将商品名称做简单的分词,然后建立起分词和商品id的对应关系,就像下面的这样:
image.png

这样如果用户搜索电冰箱,就可以给他展示商品ID为1和3的两件商品了。
而Elasticsearch作为一种常见的NoSQL数据库,就以倒排索引作为核心技术原理,为你提供了分布式的全文搜索服务,这在传统的关系型数据库中使用SQL语句是很难实现的。所以NoSQL可以在某些业务场景下代替传统数据库提供数据存储服务。

提升扩展性

很多NoSQL数据库对于扩展性也有着先天的优势。以电商系统为例,你已经为你的电商系统增加了评论系统,开始你的评估比较乐观,觉得电商系统的评论量级不会增长很快,所以就为它分了8个库,每个库查分成16张表。

但是评论系统上线后,存储量级增长迅猛,你不得不将数据库拆分成更多的库表,而数据也要重新迁移到新的库表中,过程痛苦而且容易出错。

这时你考虑是否可以使用NoSQL数据库来彻底解决扩展性的问题,比如像MongoDB就有三个扩展性方面的特性

  • 其一是Replica,也叫做副本及,可以理解为主从分离,也就是通过将数据拷贝成多份来保证当主挂掉后数据不会丢失。同时Replica还可以分担请求。Replica中有主节点来承担请求,并且把对数据变动记录到oplog里(类似于binlog);从节点接收到oplog后就会修改自身的数据以保持和主节点的一致。一旦主节点挂掉,MongoDB会从从节点中选取一个节点成为主节点,可以继续提供写数据服务。
  • 其二是shard,也叫做分片,可以理解为分库分表,将数据按照某种规则拆分成多份,存储在不同的机器上。MongoDB的Sharding特性一般需要三个角色来支持,一个是Shard Server,它是实际存储数据的节点,是一个独立的Mongod进程;二是Config Server,也是一组Mongod进程,主要存储一些元信息,比如说哪些分片存储了哪些数据等;最后是Route Server,它不实际存储数据,仅仅作为路由使用,它从Config Server中获取元信息后,将请求路由到正确的Shard Server中。


    image.png
  • 其三是负载均衡,就是当MongoDB发现Shard之间数据分配不均匀,会启动Balancer进行对数据做重新的分配,最终让不同的Shard Server的数据可以尽量的均衡。当我们的Shard Server存储空间不足需要扩容时,数据会自动被移动到新的Shard Server上,减少了数据迁移和验证的成本。

可以看到,NoSQL数据库中内置的扩展性方面的特性可以让我们不再需要对数据库做分库分表和主从分离,,也是对传统数据库一个良好的补充。

就木器啊来看,NoSQL只能作为传统关系型数据库的补充而存在,弥补关系型数据库在性能、扩展性和某些场景下的不足,所以使用时要结合自身的场景灵活的运用。

你可能感兴趣的:(11 | NoSQL:在高并发场景下,数据库和NoSQL如何做到互补?)