dw/dbsel

发布 : 2019-08-11 浏览 :

Nosql数据库选型

需求

我们需要一个数据库,存储2.0/3.0/4.0及财务等各渠道各系统的数据,由于事务都在各系统中得到了保障,这个数据库对强一致性的事务要求并不高,这些特点我们可以肯定是的nosql无疑,关系型数据库单单大型数据存储一条就满足不了,还有,这个数据库需要具有高速读写特性,它应该是属于OLTP系统用的存储类型。综上我们需要一个对事务没有要求且可用于OLTP系统的数据库。

初步筛选

​ 任何像样规模的企业都会使用各种不同类型的数据存储技术,为应对各种不同类型的数据。”Martin Fowler认为,现实的情况是你没有足够的精力去学习更多的存储技术。幸运的是,选择越来越容易,因为市场主要围绕在三个NoSQL数据库上:MongoDB,Cassandra(主要由DataStax开发的,诞生于Facebook),和HBase的(和Hadoop紧密关联在一起,也被相同社区开发出来)。

​ 从数据库排名来看,前20名,除了关系型数据库,再排除一些特定场景的数据库:Elasticsearch用于全文搜索,redis用于高速缓存,Hive主要用于OLAP领域,也就剩下了MongoDB、Cassandra、Hbase、DynamoDB等几个nosql数据库可用于OLTP系统。DynamoDB是亚马逊的云数据库,由于我们是主要部署在阿里云上的,DynamoDB也可以被排除了。

MongoDb选择

模式

MongoDb是无模式的,但具有误导性,读数据的代码通常会采用某种结构因而存在某种隐形模式,这种模式只是不是由数据库强制执行而以。这种模式也叫读时模式,与写时模式(显式模式且数据库确保数据写入时检查)相对应。MongoDb虽然是说可以随意修改其模式,但其实它的模式依赖于应用程序,就给应用程序程序带来了较高的要求,如果一但应用程序失控(比如懂代码的程序员都离职了,没人看得懂的旧代码),由于MongoDb的无模式,想重写代码都会有较大的风险。 MongoDb也是多模式的,也就是说它不要求每个文档都具有完全相同的结构。对很多异构数据场景支持非常好,但对于项目组及开发人员带来了较高的要求,你需要有非常详细的开发文档支撑也需要开发人员完全读懂设计及正确地按文档实施下去。否则又是一团泥,跟据我们公司的现实情况,这无疑是具有风险的。

关系模型与文档模型:

MongoDB的文档模型。由于存储单位是一个文档,可以支持数组和嵌套文档。关系模型的关联不一定就是它的优势,而是关系模型的基础。 而在MongoDB里面,利用富文档的性质,很多时候,通过合理建模来避免做关联。文档模型与层次模型类似,它把所有的数据表示为嵌套在记录中的记录(树),就是json所表达的那个,不可否认json的无模式确实非常灵活,但与我们的需求确实不匹配。我们要选的存储不单存储我们规范化后的规则数据,还需要存储各系统各渠道同步过来的毛数据(ODS层),这些数据用于做规范化数据的存根和与原数据的比照。 这些数据的来源都是关系型数据库,它们的模型都是关系模型,这个明显的与MongoDb所表示的文档模型并不匹配。

性能:

跟据实际应用, MongoDb的数据量应该是TB级别。超过了这个级别,它的查询就会变慢。过往经验,在20亿数据量的聚合就较慢了。

Cassandra和Hbase对比

接下来我们看看Cassandra和Hbase的对比了,这个表是网上较为全面的指标对比表:

1565837876482

对比项 Cassandra Hbase
类型 列簇 列簇
复制 无主复制 主从复制
数据模型 关系模型(打宽表) 关系模型(打宽表)
CAP AP CP
一致性 最终一致性 强一致性

从主复制与无主复制

主从复制,每个保存数据库完整数据集的节点称之为副本,存储副本的节点我们认为是从节点。

1、指定某一副本为主副本,也就是主节点,写数据时先写主节点本地存储,

2、主节点的更改日志发送给从本点,用于应用于从副本,要严格保证顺序

3、主、从节点都可以接收读请求。只有主节点能接收写请求。

无主复制,允许任何副本直接接受来自客户端的写请求。其实最早的数据复制系统就是无主节点的,但后来关系数据库主导的时代,这个想法被选择性遗忘了。当亚马逊内部采用Dynamo系统后,无主复制又再次成为主流的数据库架构。Cassandra就是受Dynamo启发而设计的无主节点、开源的数据库系统,我们称这一类数据库称为Dynamo风格的数据库。

数据分区与数据复制

分区通常与复制结合使用 ,分区是指数据库按一定的规则把一份完整的数据分隔为多份小数据存储,这样就可以进行多节点分步式存储,每个分区如果保存了多个副本(见主从复制与无主复制),就可以达到很好的可用性(就是CAP中的A特性)。

如果分区规则不完善,就会导致分区的不均匀,就会出现某些分区节点比其它分区承担更多的数据量或查询负载,我们称之为倾斜。

Hbase采用的是基于关健字区间的分区策略。分区的边界可以由管理员手动确定或是数据库自动选择。每个分区可以按照关键字排序保存,这样对于区间查询支持就较为完美。它的缺点就是关键字分配的不合理,就是导致热点出现。如关键字是时间戳,分区对应一个时间范围,如每天一个分区,那么每天的写入必能会集中到一个分区节点中。所以对于Hbase来说,rowkey的设计非常的关键。

cassandra的分区则是采用一致hash和关键字排序组合策略来完成的。一致哈希就是通过哈希函数处理数据倾斜并使其均匀分布。Cassandra内置的哈希函数是MD5。Cassandra中的表可以声明为多个列组成的复合主键。复合主键只有第一部分用于哈希来进行分区,而其它列则用作联合索引来对Cassandra的SSTable中的数据进行排序。所以,它不支持在第一列进行排序,但可以在其它列上进行高效的区间查询了。这点我认为非常适合我们现有的业务模型,如:我们可以把(公司id,开票时间)做为一个复合主键,那么就可以很轻松的检索出某个公司在一段时间内所有的开票信息了。

采用一致hash来分区一般来说是可以减轻数据倾斜问题,但无法避免,如一些大B用户它的业务量与小B用户的业务量不可同日而语,这样就导致了数据倾斜,这时一个简单的技术就是在关键字的开头或结尾加随机数来把他们分到不同的分区上。注意这些措施用在少量的大B用户才有意义,用在普遍的小B上反而增加不了不必要的开销。此外还需额外的元数据来标记哪些关键字进行了特殊处理。

分区与二级索引

二级索引是关系数据库的必备特性,在文档数据库也非常普遍。在考虑它的复杂性,像Hbase并不支持二级索引。但可以通过依附在其上的数据库驱动如:phoenix 可以实现Hbase的简单的二级索引。还有一种方案就是引入ES做它的二级索引。 二级索引主要的难点是不能规整地映射到分区中,有两种主要的方法来支持二级索引进行分区:基于文档的分区和基于词条的分区。

cassandra和Elasticsearch等采用的是基于文档分区的二级索引,它在分个分区上各自维护自己的二级索引,每个分区只管自己文档面不关心其它分区的文档,文档分区索引也称为本地索引,并不是全局索引。在读取时一般来说需要读所有分区(除非做做特殊处理才能达到只读一个分区)的二级索引,查询到结果后进行合并最后返回正结果。这种查询分区数据为听方法称为分散/聚集,很明显这种二级索引的代价确实高昂。所以要求我们能构建合适的分区方案,尽量由单个分区就能满足二级索引查询。

分区再平衡

现有的集群会随着时间的推移变得压力越来越大,增加新节点是必不可少的操作,那么增加的节点以什么样的规则来承接现在集群的分区数据呢?这就是分区再平衡要考虑的问题。一般来说我们应用程序碰到这种问题 都会用hash取模的方式来散列到不同的分区。 数据训的分区再平衡策略里去没有取模这一项,为什么不能使用取模策略?比如先前有10个节点,hash(key)=123456,那么它应该放到第6个节点,当节点数增加到11时,它需移动到节点3(123456%11=3),取模操作将会导致几乎所有的数据都需要做类似的移动。这种迁移操作会大大增加再平衡的成本。我们需要找一种策略来减少这种操作成本,一般来说分区再平衡有下面几种策略:

一、固定数量的分区

就是在创建远超节点数的分区数,为每个节点分区多个分区。到时新增节点时,我们就可以通过一种算法把部分分区整体地移到新的节点完成再平衡。比如Elasticsearch就是采用这种方案。

二、动态分区(Hbase)

前面说过,Hbase采用的是基于关健字区间的分区策略,如果边界设置有问题,分导致数据倾斜,Hbase就可以设置一个分区大小阀值(默认为10GB),如果超过这个阀值,这个分区就会分裂为2个分区,每个分区承担一半数据量,相反,有大量数据被删除,分区缩小到某个阀值,就会产生分区的合并,把它和相邻的分区进行合并。非常像B树的分裂操作。Hbase允许在一个空的数据量上配置一级初始分区(预分裂)。

三、按节点比例分区(Cassandra)

前面两种分区,分区的数量只与数据集的大小成正比。与节点的数量是无关的。而按节点比例分区的目标是使分区数与集群的节点数成正比关系。这种策略把每个节点设置为固定数量的分区(cassandra默认为256个分区),当有新节点加入集群时,它随机选择(通过算法保证不公平的分区分裂)固定数量的现有分区进行分裂。拿走这些分区的一半数据,随机选择分区边界的前提要求采用一致哈希的分区策略。

比较

分区数与数据集成正比还是以节点数成正比,我认为差别并不大,现代的数据库都能做到自适应的分区再平衡。 重要的是保证数据不要出现倾斜,而这点还是考验架构师对业务的理解程序及对数据库原理的了解程度,综合评比再做设计。

请求路由

我们的数据集已在多个节点的多个分区上了,有一个问题,客户端如何才能正确地路由到正确的节点,找到它想要的数据?如果出现了分区再平衡,节点、分区的变化 怎么通知客户端?这其实是服务发现的问题,像我们的微服务也需要解决类似的问题。一般来说有三种请求路由策略:

1、客户请求连接任意一个节点,如果这个节点可以处理请求就处理,如果不能就转发到下一个合适节点。

2、采用一个中间层“路由层”,所有的节点会注册到这层,客户端从这层拿信息直接连接到合适节点。

3、“富客户端”模式,由客户端感知分区和节点分配关系,相关于把“路由层”做进了客户端,客户直连合适节点。

其中Hbase就是采用了第2种策略,采用zookeeper做它的中间“路由层”。而cassandra则是采用了第1种策略,它采用gossip协议同步集群状态,请求可以发送到任何节点,然后由该节点转发请求到合适的节点处理。这样就可以避免对zookeeper之类的外部调用服务的依赖。gossip协议的集群状态同步也避免了单节点故障。

Quorum与故障处理

这个概念只有在Dynamo风格的数据库才好提及。假设有这么一个场景,有三个副本数据库,其中一个副本不可用(可能在重启或是发生了网络异常),如果是主从复制,且需要进入到切换操作(从节点故障重启后进就会进行追赶式恢复,主节点故障则需要选举产生一个主节点),但对于 Dynamo风格的数据库这种无主复制,则不需要这种切换操作,三个副本只要有二个已接收并确认成功,则直接认为写入成功,完全不用理会那个失败的节点。

如果这个失败的节点修复成功后,客户端又会读到这个节点的过期的数据,为了解决这个问题,客户端不是向一个副本发请求,而是并行地发送到多个副本,通过版本号技术就可以确定哪个是正确的值了,同时会修复那个过期节点的数据, Dynamo风格的数据库通过二种方式对过期数据进行修复

1、读修复: 就是在并发请求多个副本时,如果发现有过期版本的副本,那么客户端可以对它进行修复

2、反熵过程:启动后台进程,不断的查找副本间数据差异,此反熵过程并不保证顺序复制写入。

如果没有反熵过程可能会导致,那些修复久远的数据由于不存在副本中而导致数据丢失。

我们考虑一下,三个副本有2个写入成功了,那么也就是只存在一个副本有问题,那么我们并发读的时候则必须至少发送2个副本的并发读才能保证至少读到一个正确的副本。把这个道理推广,如果副本总数为n,写入需要w个节点确认成功了就可以认为成功了,读取时的并发副本数最少为r ,我们只要满足w+r>n 就能保证读取的数最少有一个正确的,应用上面的例子就是 2(W)+2(r)>3(n) ,所以上面案例能正常工作。在 Dynamo风格的数据库中,n/w/r通常都是可配置的,一般来说n设置为奇数,w=r=(n+1)/2 ,这样就能保证最优的读写性能。

实际上,Cassandra也可以以牺牲响应时间的代价来获得和HBase一样的强一致性。比如w=n,r=1就能达到强一致性, 通过对Quorum的合适的设置,可以在响应时间和数据一致性得到一个很好的折衷值。

CAP理论

CAP代表一致性、可用性、分区容错性,系统只能支持其中两个特性。对于单机数据库(一般来说是关系型数据库,如mysql)满足CA。但对于分布式数据库,网络分区是一种故障,不管你喜欢还是不喜欢,它都可能发生,所以无法选择或避免。系统在网络正常时,系统都可以同时保证可用性与一致性(线性化)。而一旦发生网络故障时,要么选择一致性,要么选择可用性。Hbase选择了一致性(CP),可cassandra选择了可用性(AP)。关于数据一致性,实际上,Cassandra也可以以牺牲响应时间的代价来获得和HBase一样的一致性。而且,通过对Quorum的合适的设置,可以在响应时间和数据一致性得到一个很好的折衷值。

结论:

hbase和cassandra两种数据库都有各自的优势,hbase的强一致保证了数据的多次查询结果是一样的,就好像看起来只有一个副本一样,cassandra虽说是最终一致性,但不是说它不能做到强一致性,可以设置严苛的Quorum参数来达到像hbase的强一致性。

二级索引方面hbase直接不支持,需要借助于ES等做二级索引,这需要一个同步的过程且对资源也有更多的要求。

Hbase采用的是基于关健字区间的分区策略,而cassandra采用一致hash和关键字排序组合策略来完成的,这个比hbase更为灵活。

Cassandra的设计初衷就不是存储大文件,但是Amazon的S3实际上就是基于Dynamo构建的,总是会让人想入非非地让Cassandra去存储超大文件。而和Cassandra不同,HBase基于HDFS,HDFS的设计初衷就是存储超大规模文件并且提供最大吞吐量和最可靠的可访问性。因此,从这一点来说,Cassandra由于背后不是一个类似HDFS的超大文件存储的文件系统,对于存储那种巨大的(几百T甚至P)的超大文件目前是无能为力的。而且就算由Client手工去分割,这实际上是非常不明智和消耗Client CPU的工作的。但我们主要需求通过列式存储来存储业务数据,这个缺点我们可以不以关注。

运维方面,Cassandra所有的节点是对等的,管理非常简单,而Hbase还依赖于HDFS,需要强大的运维,但是阿里云已出云版本的Hbase,如果交给阿里云来做倒是会比Cassandra的运维来的来简单些。

本文作者 : andy.zhou
原文链接 : https://rjzjh.gitee.io/2019/08/11/dw-dbsel/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

知识 & 情怀 | 二者兼得

微信扫一扫, 向我投食

微信扫一扫, 向我投食

支付宝扫一扫, 向我投食

支付宝扫一扫, 向我投食

留下足迹