zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

NoSQL到底怎么用?

2023-04-18 12:42:28 时间

存储服务一般从两方面改造:

  • 提升读写性能,尤其读性能,朋友圈、微博和淘宝,都是读QPS>>写QPS(读写分离)
  • 增强扩展能力,以满足大数据量存储需求(分库分表)

但之后仍有问题待解决: 比如朋友圈关系的数据量达到千亿,即使分成1024个库表,单表数据量也达到亿级,且关系数据量还在极速增加,即使你分成再多库表,数据量也会很快到达瓶颈。 传统DB难以彻底解决该问题,因为扩展性很弱。这时,就可以利用NoSQL,天生分布式,能提供优秀的读写性能,补充了传统关系型数据库短板。那么它是如何做到的呢? NoSQL,不同于传统关系型数据库的其他数据库系统的统称,不使用SQL作为查询语言,提供优秀的横向扩展能力和读写性能,非常契合互联网项目高并发大数据的特点。 Redis、LevelDB这样的KV存储,相比于传统DB,有极高读写性能,对性能有比较高的要求的场景都会使用。

Hbase、Cassandra列式存储数据库,适于一些离线数据统计场景。 MongoDB、CouchDB这种文档型数据库,Schema Free(模式自由),表中字段可任意扩展,比如说电商系统中的商品有非常多的字段,并且不同品类的商品的字段也都不尽相同,使用关系型数据库就需要不断增加字段支持,而用文档型数据库就简单了。

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

但在业务开发的场景下还是需要利用SQL查询及传统数据库事务和灵活的索引等功能,NoSQL只能作为一些场景的补充。

使用NoSQL提升写入性能 数据库系统大多使用机械磁盘,机械磁盘访问方式有两种

  • 随机IO 随机IO就需花费时间做昂贵磁盘寻道,读写效率比顺序IO小两到三数量级,想要提升写入性能就要尽量减少随机IO。
  • 顺序IO MySQL更新binlog、redolog、undolog都是在做顺序IO。而更新datafile和索引文件则是在做随机IO,为减少随机IO,关系DB做了很多优化,比如写入时先写入内存,然后批量刷盘,但还是会产生随机IO。

索引在InnoDB引擎中是B+树,MySQL主键是聚簇索引(数据与索引数据在一起),在数据插入或更新时,需找到要插入位置,再把数据写到特定位置,这就产生了随机IO。一旦发生页分裂,就会做数据移动,极大损耗写性能。

而很多NoSQL使用基于LSM树的存储引擎,LSM树(Log-Structured Merge Tree)牺牲一定读性能换取写入数据的高性能,Hbase、Cassandra、LevelDB都是用这种算法作为存储的引擎。 数据首先会写入到MemTable内存结构,在MemTable中数据按写入的Key排序。为防止MemTable数据因为机器掉电或者重启而丢失,一般会写Write Ahead Log将数据备份在磁盘。

MemTable在累积到一定规模时,会被刷新生成一个新的文件,叫SSTable(Sorted String Table)。 当SSTable达到一定数量时,会将这些SSTable合并,减少文件数量,因为SSTable有序,所以合并快。

当从LSM树里面读数据时,我们首先从MemTable中查找数据,如果数据没有找到,再从SSTable中查找数据。因为存储的数据都是有序的,所以查找的效率是很高的,只是因为数据被拆分成多个SSTable,所以读效率低于B+树索引。

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

适用场景

除了提升性能,NoSQL还可在某些场景下弥补传统关系型DB的不足,假设要设计商品搜索功能,需支持按照商品的名称模糊搜索到对应的商品。 这不就是:

此类语句并非总能走索引,只有后模糊匹配语句才走索引。比如

就没有走name索引,而

使用了“name”上的索引。而一旦没有使用索引就会扫描全表数据。

于是发现Elasticsearch支持搜索,基于“倒排索引”来实现,将记录中的某些列做分词,然后形成的分词与记录ID之间的映射关系。 比如电商项目有以下记录:

将商品名称做分词,然后建立起分词和商品ID的对应关系:

用户搜索电冰箱,就可以给他展示商品ID为1和3的两件商品。 而Elasticsearch提供了分布式的全文搜索服务,这在传统的关系型数据库中使用SQL语句是很难实现。所以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中。

负载均衡

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

NoSQL内置的扩展性可以让我们不再需要对数据库做分库分表和主从分离,也是对传统数据库一个良好的补充。 课程小结 1.在性能方面,NoSQL数据库使用一些算法将对磁盘的随机写转换成顺序写,提升了写的性能; 2.在某些场景下,比如全文搜索功能,关系型数据库并不能高效地支持,需要NoSQL数据库的支持; 3.在扩展性方面,NoSQL数据库天生支持分布式,支持数据冗余和数据分片的特性。