您是否担心系统数据库的高流量几乎占满了CPU,每天的CPU居高不下?你是否纠结于各种NoSQL,你应该选择哪一个?今天你是我昨天,这就是我写这篇文章的原因。图片来自Pexels作为互联网从业者,我们要知道关系型数据库(MySQL、Oracle)并不能满足我们所有的存储需求。因此,底层存储的选择和对各个存储引擎的理解非常重要。同时,由于过去一段时间的工作经历,让我对这篇文章有了更多的思考,想写这篇文章,通过自己的总结分享给大家。结构化数据、非结构化数据和半结构化数据文章开头先说说结构化数据、非结构化数据和半结构化数据。由于数据特性的不同,从技术上会直接影响到存储引擎的选择。.首先是结构化数据。根据定义,结构化数据是指通过二维表结构进行逻辑表达和实现的数据。它严格遵循数据格式和长度规范。也称为行数据。数据代表一个实体的信息,每一行数据的属性都是相同的。例如:因此,关系数据库非常适合结构化数据的特点,关系数据库也是关系数据最重要的存储和管理引擎。非结构化数据是指不规则或不完整的数据结构,没有任何预定义的数据模型,不方便用二维逻辑表表示的数据,如办公文档(Word)、文本、图片、HTML、各种班级报告、视频和音频等。介于结构化数据和非结构化数据之间的数据是半结构化数据。它是一种结构化数据形式。虽然不符合二维逻辑的数据模型结构,但是包含了切分的相关标签。语义元素和分层记录和字段。常见的半结构化数据包括XML和JSON,例如:张三1812345也用到这种结构成为自描述结构。关系型数据库形式存储的架构演进首先我们看一下关系型数据库的使用方式,一个企业系统发展的几个阶段的架构演进(由于本文写的是SQL和NoSQL,所以只介绍存储方式)作为入口点,不涉及MQ、ZK等中间件):阶段1是企业刚刚发展的阶段,最简单的,一个应用服务器配一个关系型数据库,每隔一段时间读写一次数据库时间。Phase2无论你使用MySQL、Oracle还是其他关系型数据库,数据库通常不会首先成为性能瓶颈。通常,随着企业规模的扩大,应用服务器无法处理上游流量,应用服务器就会产生单点故障问题。所以,增加一个应用服务器,在流量入口使用Nginx做一层负载均衡,保证流量均匀的发往应用服务器。阶段三:随着企业规模的不断扩大,此时由于读写都在同一个数据库上,数据库的性能出现了一定的瓶颈。这个时候简单的做一层读写分离,每次写主库,读备库,主备之间通过Binlog同步数据,可以很大程度上解决现阶段的数据库性能问题.Stage4企业发展越来越好,业务越来越大,读写分离后数据库的压力还在不断增加。这个时候怎么办?一个库搞不定,还是分成几个吧,分库分表,垂直分表,水平分库。以扩容数据库为例,扩容两个数据库,有一定的序号(比如交易序号)和一定的规则(比如取模)。取模2为0的事务号丢给数据库1,取模2为1的事务号丢给数据库2,这样写数据库的流量就平均分配给了两个数据库。一般分库分表会采用Shard的方式,通过一个中间件,方便连接管理和数据监控,客户端不需要感知数据库IP。关系型数据库的优点上面的方法看似能解决问题(其实也能解决很多问题)。关系型数据库做个读写分离+分库分表,支持1W+的读写QPS不大,还是个问题。但是,受关系数据库本身的限制,这种架构方案仍然存在明显的不足。下面先分析一下使用关系数据库方式的存储方案的优点,然后在后面分析缺点。充分了解优缺点是技术选型的前提。①易于理解因为行+列的二维表逻辑是一个非常接近逻辑世界的概念,所以关系模型比其他模型如mesh、hierarchy更容易理解。②操作简单通用的SQL语言使得操作关系型数据库非常方便,支持Join等复杂查询。③数据一致性支持ACID特性,可以保持数据之间的一致性,这是使用数据库很重要的原因之一。比如银行转账,张三转100元给李四,张三扣除100元,李四加100元,必须同时成功或同时失败,否则用户资产将受到损害。④数据稳定性数据持久化到磁盘,无数据丢失风险,支持海量数据存储。⑤服务稳定最常用的关系型数据库产品MySQL和Oracle服务器,性能优良,服务稳定,通常很少出现异常宕机。关系型数据库的缺点接下来我们来看看关系型数据库的缺点,也是比较明显的。①高并发IO压力下,大数据按行存储。即使只计算其中一列,也会将整行数据从存储设备读入内存,造成高IO。②索引维护成本高。为了提供丰富的查询能力,通常热表都会有多个二级索引。一旦有了二级索引,数据的添加必须伴随着所有二级索引的添加。数据的更新也必然伴随着所有二级索引的更新,这必然会降低关系数据库的读写能力,而且索引越多,读写能力越差。如果有机会,你可以看看你公司的数据库。除了数据文件不可避免的占用空间外,索引占用的空间其实也不少。③维护数据一致性的成本大数据一致性是关系型数据库的核心,但是维护数据一致性的成本也非常高。我们都知道SQL标准为事务定义了不同的隔离级别。从低到高分别是读未提交、读已提交、可重复读、序列化。事务隔离级别越低,可能出现的并发异常越多,但一般来说,能提供的并发能力越强。那么,为了保证事务的一致性,数据库需要提供两种技术:并发控制和故障恢复。前者用于减少并发异常,后者可以保证系统异常时事务和数据库状态不被破坏。对于并发控制,核心思想是加锁。不管是乐观锁还是悲观锁,只要提供的隔离级别越高,读写性能必然越差。④横向扩张带来的各种问题难以处理。上面说了,随着企业规模的扩大,一种方式就是对数据库进行划分。2数据库),跨库Join(订单数据中有用户数据,两个数据不在同一个数据库),分布式事务处理都是需要考虑的问题,尤其是分布式事务处理,目前业界没有特别好的解法。⑤表结构扩展不方便由于数据库存储的是结构化数据,表结构Schema是固定的,不方便扩展。如果需要修改表结构,需要执行DDL(数据定义语言)语句修改,这会导致修改时锁表,部分服务不可用。⑥全文检索功能弱。比如像“%Chinaissogreat%”,只能搜索“2019年中国好伟大,爱祖国”,但不能搜索“中国好伟大%”这样的文字,即没有分词功能。而like查询在“%中国真的很棒”这样的搜索条件下是无法命中索引的,会大大降低查询效率。写了这么多,我理解的核心还是前三点。它反映的问题之一是关系型数据库在高并发下存在容量瓶颈。尤其是频繁写/更新的情况下,瓶颈的结果是数据库CPU高,SQL执行慢,客户端报数据库连接池不够用。扣除存货。可能有朋友会说,数据库在高并发下的能力有瓶颈。我们公司有钱,可以加CPU,换固态硬盘,继续买服务器和数据库做分库。问题是这是一种性价比非常低的方法。达到效果需要1000万,但用其他100万的方法说不定能达到。不考虑人员和服务器投入产出比的领导是不合格的领导。而且关系型数据库的方式受限于自身的特点,花钱也未必能达到预期的效果。至于花100万的方法是什么,达到花1000万的效果?大家可以继续往下看,这就是我们要讲的NoSQL。结合NoSQL存储架构的演进如上分析,数据库作为关系数据的存储引擎,存储的是关系数据。它既有优点,也有明显的缺点。因此,当企业规模不断扩大时,不会一味期望通过增强数据库的能力来解决数据存储问题,而是会引入其他存储,也就是我们所说的NoSQL。NoSQL的全称是NotOnlySQL,泛指非关系型数据库,是对关系型数据库的补充。特别注意补充这两个词,意思是NoSQL和关系型数据库不是对立的。两者各有优缺点,取长补短才是在合适的场景下选择合适的存储引擎的正确方法。比较简单的NoSQL就是缓存:对于那些读多于写的数据,引入一层缓存。每次读取都是从缓存中读取的。如果在缓存中读取不到,则从数据库中取出,取出后写入。说到缓存,数据的失效机制通常没有大问题。一般来说,缓存是性能优化的首选,也是最有效的解决方案。但是缓存通常是KV类型的存储,容量有限(基于内存),不能解决所有问题,所以为了进一步优化,我们继续引入其他NoSQL:数据库、缓存和其他NoSQL并行工作,充分发挥每个NoSQL的特点。当然,虽然NoSQL在性能上比关系型数据库好很多,但也往往伴随着一些特性的缺失,比较常见的是事务功能的缺失。下面我们就来看看常用的NoSQL及其代表产品,分析一下各个NoSQL的优缺点和适用场景,让大家熟悉各个NoSQL的特点,方便技术选型。KV-typeNoSQL(代表:Redis)KV-typeNoSQL,顾名思义,是以键值对的形式存储的非关系型数据库。它是NoSQL中最简单、最容易理解、最熟悉的一类,因此速度相对较快。Redis和MemCache是??其中的代表,Redis是KV型NoSQL中应用广泛的NoSQL。以Redis为例,KV数据库,我总结最大的优势有两点:数据基于内存,读写效率高。KV类型数据,时间复杂度为O(1),查询速度快。因此,KV型NoSQL最大的优势就是高性能。使用Redis自带的BenchMark作为基准测试,TPS可以达到10万级别,性能很强。同样,Redis也有所有KV型NoSQL都有的明显缺点:V只能根据K查,K不能根据V查。查询方式单一,只有KV方式,不支持条件查询.多条件查询的唯一途径就是数据冗余,但这会极大地浪费存储空间。内存有限,无法支持海量数据存储。同样,由于KV型NoSQL的存储是基于内存的,因此存在数据丢失的风险。总结一下,最适合KV型NoSQL的场景就是缓存场景:读远多于写。阅读能力强。不需要持久化,数据丢失可以容忍。反正丢了可以查询写入。比如根据用户id查询用户信息,每次根据用户id在缓存中查询,找到则直接返回数据,如果找不到则根据中的id查询数据关系数据库并写入缓存。搜索型NoSQL(代表:ES)传统关系型数据库主要通过索引来达到快速查询的目的,但是在全文搜索的场景下,索引就无能为力了。首先like查询不能满足所有的模糊匹配需求,其次使用限制过大,使用不当容易导致查询慢。搜索型NoSQL是为解决关系型数据库全文搜索能力弱的问题而诞生的。ElasticSearch是搜索型NoSQL的代表产品。全文搜索的原理是倒排索引。让我们看看什么是倒排索引。说倒排索引,我们先来看看什么是正向索引。传统的正向索引是文档-->关键词的映射。例如,“Tomismyfriend”这句话会被分成“Tom”、“is”、“my”、“friend”四个词。搜索时,扫描文档,找出符合条件的词。.这种方法的原理很简单,但是由于其检索效率太低,基本没有实用价值。倒排索引则完全相反。就是关键字-->文档的映射。我将其显示在表格中以使其更清楚:这意味着我现在有“汤姆是汤姆”、“汤姆是我的朋友”、“谢谢你,贝蒂”、“汤姆是贝蒂的丈夫”四个句子。搜索引擎会按照一定的切分规则将这句话分成N个关键词,在关键词的维度上维护关键词在每篇文本中出现的次数。这样,下次搜索“Tom”时,由于“Tom是Tom”、“Tom是我的朋友”、“Tom是Betty的丈夫”这三个句子中都出现了Tom这个词,所以这三个记录都会被检索出来.而由于“Tom”在“TomisTom”这句话中出现了两次,所以这条记录对于“Tom”这个词的匹配度最高,排在最前面。这就是搜索引擎倒排索引的基本原理。假设某个关键词出现在某个文档中,那么倒排索引中有两部分:documentid。它在本文档中出现的位置。你可以推论一下。当我们搜索“BettyTom”这两个词时也是如此。搜索引擎将“BettyTom”分为“Tom”和“Betty”两个词。根据开发者指定的满意率,比如满意率=50%,那么只要记录中出现这两个词之一,就会根据匹配度来检索并显示记录。搜索型NoSQL以ElasticSearch为例。它的优点是:支持分词场景和全文搜索,这是区别于关系型数据库的最大特点。支持条件查询,支持聚合操作,类似于关系型数据库的GroupBy,但功能更强大,适用于数据分析。写入文件无数据丢失风险,在集群环境下可轻松横向扩展,可承载PB级数据。高可用性,自动发现新节点或故障节点,重组和重新平衡数据,确保数据安全和可访问。同样,ElasticSearch也有明显的缺点:①性能完全依赖于内存,这也是使用时最重要的一点。消耗硬件资源和内存,64G+SSD基本是大数据量的标配。爱马仕在数据库中也是如此。为什么我需要特别提到内存,因为内存是非常宝贵的。同样的配置,两倍的内存,一个月就要多花几百块钱。至于ElasticSearch内存用在哪里,大概有以下几种:IndexingBuffer:ElasticSearch基于Luence。Lucene的倒排索引首先在内存中生成,然后以SegmentFile的形式周期性的刷盘。每个SegmentFile其实就是一个全倒排索引。段内存:前面说过,倒排索引是基于关键字的。Lucene4.0之后,所有的关键字都会以FST数据结构的形式全量加载到内存中,以加快查询速度。官方建议至少为Lucene保留一半的系统内存。各种缓存:FilterCache、FieldCache、IndexingCache等,用于提高查询分析性能。例如FilterCache,用于缓存使用过的Filter的结果集。ClusterStateBuffer:ElasticSearch是为了让每个Node都能响应用户的请求而设计的,所以每个Node的内存中都包含了集群状态的一份副本,大规模集群的状态信息可能会非常庞大??。②读写之间有延时,写入的数据1s左右会被读出,属于正常现象。写的时候自动加那么多索引,肯定会影响性能。③数据结构的灵活性不高。对于ElasticSearch,字段一旦创建,类型就不能修改了。如果创建的数据表中某个字段没有全文索引,想添加,那么只能删除整个表,重建。因此,搜索型NoSQL最适合的场景是条件搜索,尤其是全文搜索,作为关系型数据库的替代方案。此外,搜索数据库还有一个特别重要的应用场景。我们可以想一想,一旦数据库分库分表,原来可以在单表中做的聚合统计操作就失效了吗?比如我把订单表分成16个数据库,1024张表,那么订单数据就会分散在1024张表中,我想统计昨天浙江省单笔交易金额最高的订单。怎么做?我想对昨天的所有订单进行排序并展示如何做?这是基于文档的NoSQL的另一大优势起作用了,我们可以将分表后的数据放到基于文档的NoSQL中,利用基于文档的NoSQL的搜索和聚合能力来完成全量的查询数据的。至于为什么放在KV-typeNoSQL后面作为secondwrite,因为通常search-typeNoSQL也会作为一层pre-cache来保护关系型数据库。ColumnarNoSQL(代表:HBase)ColumnarNoSQL是大数据时代的代表技术之一,以HBase为代表。列式NoSQL基于列式存储,那么什么是列式存储呢?列式SQL与关系数据库具有相同的主键概念。不同的是关系型数据库是按照行来组织数据的:看到每一行都有name,phone,address三个字段,这是行存储的方式,可以观察到id=2的数据,即使没有电话领域,它仍然占用空间。列式存储完全是另一种方式。它是按每一列组织的数据:这样做有什么好处?大致有以下几点:查询时,只会读取指定的列,不会读取所有列。节省存储空间,Null值不会存储,有时一列会出现很多重复的数据(尤其是枚举数据,性别,状态等),这类数据可以压缩,压缩行数据库的压缩比通常在3:1~5:1之间,列式数据库的压缩比一般在8:1~30:1左右。列数据组织在一起,一次磁盘IO可以一次性将一列数据读入内存。第二点是关于数据压缩。这是什么意思?以比较常见的字典表压缩方式为例:仔细看图理解,应该就明白了。然后继续说优缺点,列式NoSQL,以HBase为代表,优点是:无限存储海量数据,PB级数据随便存,底层基于HDFS(Hadoop文件系统),以及数据持久化。读写性能还是不错的,只要没有滥用造成的数据热点,读写基本是随便的。水平扩展是关系数据库和非关系数据库中最方便的扩展之一。只需要增加新的机器就可以实现数据容量的线性增长,而且可以用在便宜的服务器上,节省成本。它没有单点故障和高可用性。可以存储结构化或半结构化数据。列数理论上没有限制,HBase本身只需要列族数,推荐1~3个。说了这么多HBase的优点,该说说HBase的缺点了:HBase是Hadoop生态的一部分,所以是一个比较重的产品,依赖的Hadoop组件很多。还是有点复杂。KV风格不支持条件查询,或者说条件查询非常非常弱。当Scan扫描一批数据时,HBase仍然提供了前缀匹配的API。条件查询,除非为数据冗余定义了多个RowKey。不支持分页查询,因为无法统计数据总数。因此,HBase更适合KV类型的数据增长无法预测未来的场景。另外,HBase的使用还是需要一定的经验,主要体现在RowKey的设计上。基于文档的NoSQL(代表作:MongoDB)坦白说,根据我的工作经验,我对基于文档的NoSQL的使用经验还比较浅,所以这部分只能结合以前的使用和网上的文章来介绍。什么是基于文档的NoSQL?基于文档的NoSQL是指将半结构化数据存储为文档的NoSQL。DocumentNoSQL通常以JSON或XML格式存储数据,因此DocumentNoSQL没有Schema。由于没有Schema特性,我们可以随意存储和读取数据,所以文档型NoSQL的出现就是为了解决关系型数据库表结构扩展不方便的问题。MongoDB是基于文档的NoSQL的代表产品,也是所有NoSQL产品中的明星产品之一,所以这里以MongoDB为例。按照我的理解,MongoDB作为一个文档型的NoSQL,是一个完全对标关系型数据库的产品。从存储的角度来看,我们可以看到关系型数据库对每个字段的存储都是按部就班的。在MongDB中,它是一个JSON字符串来存储。关系数据可以为姓名和电话创建索引。MongoDB还可以使用createIndex命令为列创建索引。建立索引后,查询效率可以大大提高。其他方面,就大的基本概念而言,两者基本相似:因此,对于MongDB,我们只需要将其理解为一个Free-Schema关系型数据库就可以了。其优缺点一目了然。优点:没有预定义字段,扩展字段容易。与关系型数据库相比,读写性能更胜一筹。命中二级索引的查询不会比关系数据库慢,非索引字段的查询是全能赢家。缺点是:不支持事务操作。MongoDB4.0虽然号称支持事务,但效果还有待观察。不支持多表关联查询(虽然有嵌入文档的方式),Join查询还是需要多次操作。它占用了很多空间。这是MongDB的设计问题。空间预分配机制+删除数据后空间不释放。只有在使用db.repairDatabase()修复后才能释放。目前还没有找到成熟的运维工具,比如MySQL的Navicat等MongoDB的关系型数据库。总而言之,MongDB的使用场景很大程度上可以类比于关系型数据库,但是它更适合处理没有Join,没有强一致性要求,表schema变化频繁的数据。小结:最后一部分数据库和NoSQL的比较,各种NoSQL,做一个小结。本文归结为两个主题:什么时候选择关系型数据库,什么时候选择非关系型数据库。Chooseanon-relationaldatabase,使用哪个非关系数据库。首先第一个话题,关系型数据库和非关系型数据库的选择,在我的理解中,无非是两个方面的考虑:第一点,不用多解释,应该明白。非关系型数据库牺牲ACID特性来获得更高的性能,假设两张表之间有比较强的一致性要求,那么这类数据就不适合存储在非关系型数据库中。第二点,核心数据没有使用非关系型数据库,比如用户表、订单表,但是有一个前提,就是这类核心数据会有多种查询方式。比如user表有ABCD四个字段,可以按照AB,AC,D来查。假设是核心数据,但是是KV的形式,比如用户聊天记录,那么HBase会做一次被储存了。从这几年的工作经验来看,非核心数据,尤其是日志、管道等中间数据,一定不要写在关系型数据库中。这类数据通常有两个特点:写入量远高于读写量一旦Huge使用关系型数据库作为存储引擎,关系型数据库的容量将大幅缩减,核心服务QPS低对于正常的读写会被这种类型的数据读写拖累。那么第二个问题来了。如果我们使用非关系型数据库作为存储引擎,如何选择类型呢?其实上面几篇文章基本都写好了,这里只是总结一下(所有的缺点都不会体现事务的重点,因为这是所有NoSQL数据库相对于关系型数据库的共同问题):但是这里特别说明的是选择一定要根据实际情况,而不是照本宣科。以架构为基础,拿出大而全的技术方案。对于一些条件查询比较多的数据,更适合使用ElasticSearch来存储,减轻关系型数据库的压力,但是公司的成本有限。在这种情况下,您可以尝试继续使用关系数据库来存储此类数据。有一种数据格式比较简单,就是KV类型,增长速度比较大。但是公司没有HBase人才,运维可能比较困难。考虑到实际情况,可以先使用关系型数据库一段时间。所以,如果不考虑实际情况,有些存储引擎虽然确实比较合适,但是强行使用会适得其反。总之,适合自己的才是最好的。