Apache三剑客:HBase, Cassandra, CouchDB。HBase的前景最为看好,因为它的开发者众多并且都是顶尖高手。Cassandra目前有很多否定的声音。CouchDB的小而精悍,赞誉很多,将要正式发布的CouchBase融合了MemBase和CouchDB,很令人期待。
HBase和Cassandra都是效仿Google的BigTable的基于列的数据库,它们都是用Java写的。另外一类似的数据库是HyperTable,百度用在一些后台分析,因为它是C++写的,速度比较快。不过HyperTable有点边缘,不太流行。这些基于列的开源数据库目前都比Goolge的BigTable差之少一个数量级
CouchDB是一个文档数据库。其最大的竞争者是MongoDB。MongoDB和HBase都采用主从服务器设计。CouchDB的服务器分布设计和Cassandra类似,Peer to Peer类型的。主从服务器设计一般能更好的strong consistent,属于CAP理论中的CP类型。 CouchDB和Cassandra一般认为都是eventual consistent,属于CAP理论中的AP类型。但其实MongoDB和Cassandra都可以设置成strong consistent或者eventual consistent。
以上所提到的数据库都支持MapReduce。好像出了HyperTable都支持非主键索引。HBase和strong consistent配置的MongoDB都支持最基本的锁定(HBase单行锁定,MongoDB单文档锁定),因此可以实现transaction,但是实现有点复杂和低效。单就transaction这一点,目前开源NoSQL数据库没有做的比较好的。
MongoDB的最大卖点是不需构建非主键索引也能执行很多查询。但是MongoDB的服务器分布设计实在不能让人恭维,可以说是NoSQL数据库中最Ugly的实现。
K-V数据库比较多,而且上面提到的基于列的数据库和文档数据库其实也都是K-V数据库。比较流行的纯种K-V数据库有:
Memcached: 非常流行,不支持持久化
VMWare's Redis: 很流行,新浪和知乎都在用,CP类型。
MemBase: 由很多Memcached的开发者开发,使用sqlite作底层存储。在社交游戏中用的比较多, zynga在用,CP类型。
Riak, 分布式实现和CouchDB/Cassandra比较像,AP类型。支持MapReduce。
Linkin's Voldemort, 在K-V中少见的eventual consistent ,AP类型。
TT, TC
纯基于二维座标索引的是Neo4j。但是现在MongoDB和CouchDB都集成这一特性。
目前CouchDB的开发者成立的公司CouchOne收购了MemBase,将其底层sqlite换成CouchDB推出了CouchBase,从而引入MapReduce以支持非主键索引。CouchBase暂时还没有正式发布官方正式版,不过快了。虽然CouchDB是eventual consistent的,但是CouchBase的开发者宣称CouchBase保持了MemBase的strong consistent特性,具体实现有待以后研究。
如果从成熟的角度来看,比较成熟并且十分流行的的有CouchDB,Memcached,Redis。
前言:
MySQL与MongoDB都是开源的常用数据库,但是MySQL是传统的关系型数据库,MongoDB则是非关系型数据库,也叫文档型数据库,是一种NoSQL的数据库。它们各有各的优点,关键是看用在什么地方。所以我们所熟知的那些SQL语句就不适用于MongoDB了,因为SQL语句是关系型数据库的标准语言。
一、关系型数据库-MySQL
1、在不同的引擎上有不同的存储方式。
2、查询语句是使用传统的sql语句,拥有较为成熟的体系,成熟度很高。
3、开源数据库的份额在不断增加,mysql的份额页在持续增长。
4、缺点就是在海量数据处理的时候效率会显著变慢。
二、非关系型数据库-MongoDB
非关系型数据库(nosql ),属于文档型数据库。先解释一下文档的数据库,即可以存放xml、json、bson类型系那个的数据。这些数据具备自述性,呈现分层的树状数据结构。数据结构由键值(key=>value)对组成。
1、存储方式:虚拟内存+持久化。
2、查询语句:是独特的MongoDB的查询方式。
3、适合场景:事件的记录,内容管理或者博客平台等等。
4、架构特点:可以通过副本集,以及分片来实现高可用。
5、数据处理:数据是存储在硬盘上的,只不过需要经常读取的数据会被加载到内存中,将数据存储在物理内存中,从而达到高速读写。
6、成熟度与广泛度:新兴数据库,成熟度较低,Nosql数据库中最为接近关系型数据库,比较完善的DB之一,适用人群不断在增长。
三、MongoDB优势与劣势
优势:
1、在适量级的内存的MongoDB的性能是非常迅速的,它将热数据存储在物理内存中,使得热数据的读写变得十分快。
2、MongoDB的高可用和集群架构拥有十分高的扩展性。
3、在副本集中,当主库遇到问题,无法继续提供服务的时候,副本集将选举一个新的主库继续提供服务。
4、MongoDB的Bson和JSon格式的数据十分适合文档格式的存储与查询。
劣势:
1、 不支持事务操作。MongoDB本身没有自带事务机制,若需要在MongoDB中实现事务机制,需通过一个额外的表,从逻辑上自行实现事务。
2、 应用经验少,由于NoSQL兴起时间短,应用经验相比关系型数据库较少。
3、MongoDB占用空间过大。
mongodb众所周知不支持事务,所以需要强事务的业务根本不能考虑mongodb。
mongodb的优势就是文档存储:
1 业务经常变动,需要不时的添加字段,那么mongodb比较适合,关系型数据库添加字段的复杂度也还好
2 嵌套文档,业务数据比较复杂,适合嵌套文档式存储,那么mongodb非常合适,这个关系型数据库比较难搞,虽然MySQL和pg也有文档存储,但MySQL的不成熟,pg毕竟现在生产中使用还是偏少,个人也不了解,这里不谈。但这不仅仅这一点优势,具体下面会细说。
3 upsert支持,查询速度也不慢
4 高可用的副本集支持
5 查询语法非常丰富,嵌套文档查询功能非常强大,不是重度用户可能不能理解
下面说说一个具体的使用事例:
项目的一条数据在10kb左右,如果使用关系型数据库那么需要将这条数据拆分成大概几百条左右,建造多个表,设计较复杂,这种数据大概在一百万条左右,想想拆分后在十几亿的数据量就可怕。打平后的数据什么DB也都可以拿下,只是一百万变十几亿比较恐怖而已。
如果采用MySQL存储,每次查询需要使用外键查询多个表,从这些表中拉取数据,性能肯定要下降很多,比不上只在一个表查询,而且只拉取少两个数量级的数据。查询也还好,业务允许可以对结果做缓存,放到redis里去。
但是重点来了,需求要增量更新部分数据,这时候需要更新多个表,根本没法做到原子性(注意事务不是原子操作),当然也可以使用cas等技术补偿,达到最终一致性。但使用mongodb存储只需要update一条数据,对相应的嵌套文档中内容更新,可以做到原子性,是不是很方便?
推荐学习《python教程》
具体说说该项目的难点,查询无法使用缓存,可能会很吃惊,但是业务决定了确实做不了,而且增量更新的量达到上万的QPS,如果不能保证原子性想想多么可怕!
所以mongodb在这里帮了大忙,关系型数据库解决不了这个难题。
有人可能要问,mongodb没有事务,上游数据写入也会有问题,你不可能所有数据都存一个表吧?
当然不是的,我们mongodb里的数据是从MySQL中清洗出来存到mongodb中的,mongodb只做单点的业务需求,综合的数据还是在MySQL中。
此项目我们用了上百个副本集,保证系统的高可用,这些副本集配置只要一条shell就搞定,如果用MySQL的主从不知道怎么配(我自己不懂),估计DBA得忙死,而该项目完全不需要也没用到DBA。
说了这么多mongo的优点,也说说他的缺点:
1 查询优化器和MySQL没法比
2 不支持reload,只能冷重启,初始化配置的时候比较麻烦
3 没有事务,不敢存储第一手数据,多用来做备份数据的存储
mongodb可以做很多事情,取决于你脑洞,性能不差,存一些相对不重要的数据,mongodb嵌套文档功能强大,多看看官方文档挖掘挖掘有用信息,每次都能发现惊喜。
高可用是MongoDB最核心的功能之一,相信很多同学也是因为这一特性才想深入了解它的。
那么本节就来说下MongoDB通过哪些方式来实现它的高可用,然后给予这些特性我们可以实现什么程度的高可用。
相信一旦提到高可用,浮现在大家脑海里会有如下几个问题:
那么,带着这些问题,我们继续看下去,看完大家应该会对这些问题有所了解了。
MongoDB高可用的基础是复制集群,复制集群本质来说就是一份数据存多份,保证一台机器挂掉了数据不会丢失。
一个副本集至少有3个节点组成:
从上面的节点类型可以看出,一个三节点的复制集群可能是PSS或者PSA结构。
PSA结构优点是节约成本,但是缺点是Primary挂掉之后,一些依赖 majority(多数)特性的写功能出问题,因此一般不建议使用。
复制集群确保数据一致性的核心设计是:
从上面4点我们可以得出 MongoDB 高可用的如下结论:
MongoDB宕机重启之后可以通过checkpoint快速恢复上一个60s之前的数据。
MongoDB最后一个checkpoint到宕机期间的数据可以通过Journal日志回放恢复。
Journal日志因为是100ms刷盘一次,因此至多会丢失100ms的数据
(这个可以通过WriteConcern的参数控制不丢失,只是性能会受影响,适合可靠性要求非常严格的场景)
如果在写数据开启了多数写,那么就算Primary宕机了也是至多丢失100ms数据(可避免,同上)。
分布式系统必须要面对的一个问题就是数据的一致性和高可用,针对这个问题有一个非常著名的理论就是CAP理论。
CAP理论的核心结论是:一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。
关于CAP理论在网上有非常多的论述,这里也不赘述。
CAP理论提出了分布式系统必须面临的问题,但是我们也不可能因为这个问题就不用分布式系统。
因此,BASE(Basically Available基本可用、Soft state软状态、Eventually consistent最终一致性)理论被提出来了。
BASE理论是在一致性和可用性上的平衡,现在大部分分布式系统都是基于 BASE理论设计的,当然MongoDB也是遵循此理论的。
MongoDB为了保证可用性和分区容错性,采用的是副本集的方式,这种模式就必须要解决的一个问题就是怎样快速在系统启动和Primary发生异常时选取一个合适的主节点。
这里潜在着多个问题:
MongoDB的选举算法是基于Raft协议的改进,Raft协议将分布式集群里面的节点有3种状态:
leader:就是Primary节点,负责整个集群的写操作。
candidate:候选者,在Primary节点挂掉之后,参与竞选的节点。只有选举期间才会存在,是个临时状态。
follower:就是Secondary节点,被动的从Primary节点拉取更新数据。
节点的状态变化是:正常情况下只有一个leader和多个flower,当leader挂掉了,那么flower里面就会有部分节点成为candidate参与竞选。
当某个candidate竞选成功之后就成为新的leader,而其他candidate回到flower状态。
具体状态机如下:
Raft协议中有两个核心RPC协议分别应用在选举阶段和正常阶段:
请求投票:选举阶段,candidate向其他节点发起请求,请求对方给自己投票。
追加条目:正常阶段,leader节点向follower节点发起请求,告诉对方有数据更新,同时作为心跳机制来向所有follower宣示自己的地位。
如果follower在一定时间内没有收到该请求就会启动新一轮的选举投票。
Raft协议规定了在选举阶段的投票规则:
一个节点,在一个选举周期(Term)内只能给一个candidate节点投赞成票,且先到先得。
只有在candidate节点的oplog领先或和自己相同时才投赞成票。
一轮完整的选举过程包含如下内容:
以上就是目前掌握的MongoDB的选举机制,其中有个问题暂时还未得到解答,就是最后一个,怎样确保选出的Primary是最合适的那一个
因为,从前面的协议来看,存在一个逻辑bug:由于follower转换成candidate是随机并行的,再加上先到先得的投票机制会导致选出一个次优的节点成为Primary。
针对Raft协议的这个问题,下来查询了一些资料,结论是:
Raft协议确实不保证选举出来的Primary节点是最优的。
MongoDB通过在选举成功,到新Primary即位之前,新增了一个 catchup(追赶)操作来解决。
即在节点获取投票胜利之后,会先检查其它节点是否有比自己更新的oplog,如果没有就直接即位,如果有就先把数据同步过来再即位。
MongoDB的主从同步机制是确保数据一致性和可靠性的重要机制。其同步的基础是oplog,类似MySQL的binlog,但是也有一些差异,oplog虽然叫log但并不是一个文件,而是一个集合(Collection)。
同时由于 oplog 的并行写入,存在尾部乱序和空洞现象,具体来说就是oplog里面的数据顺序可能是和实际数据顺序不一致,并且存在时间的不连续问题。
为了解决这个问题,MongoDB采用的是混合逻辑时钟(HLC)来解决的,HLC不止解决乱序和空洞问题,同时也是用来解决分布式系统上事务一致性的方案。
主从同步的本质实际上就是,Primary节点接收客户端请求,将更新操作写到oplog,然后Secondary从同步源拉取oplog并本地回放,实现数据的同步。
同步源是指节点拉取oplog的源节点,这个节点不一定是Primary,链式复制模式下就可能是任何节点。
节点的同步源选取是一个非常复杂的过程,大致上来说是:
在同步源选取时有些特殊情况:
用户可以为节点指定同步源。
如果关闭链式复制,所有Secondary节点的同步源都是Primary节点。
如果从同步源拉取出错了,会被短期加入黑名单。
整个拉取和回放的逻辑非常复杂,这里根据自己的理解简化说明,如果想了解更多知识可以参考《MongoDB复制技术内幕》
节点有一个专门拉取oplog的线程,通过Exhausted cursor从同步源拉取 oplog。拉取下来之后,并不会执行回放执行,而是会将其丢到一个本地
的阻塞队列中。
然后有多个具体的执行线程,从阻塞队列中取出oplog并执行。
在取出过程中,同一个Collection的oplog一定会被同一个线程取出执行,线程会尽可能的合并连续的插入命令。
整个回放的执行过程,大致为先加锁,然后写本店oplog,然后将oplog刷盘(WAL机制),最后更新自己的最新opTime。
MongoDB全方位知识图谱
>
1 片键介绍
数据划分(partitioning)关键问题是怎么样将一个集合中的数据均衡的分布在集群中的节点上。 MongoDB 数据划分的是在集合的层面上进行的,它根据片键来划分集合中的数据。
(1)使用片键的取值范围指定数据块
设置分片的时候,需要从集合里选出一个字段,用该字段的值作为数据拆分的依据,这个字段称为片键(shard key),文档中的数据按照这个字段排序切分成块,分布到各个片上。比如说有个表示人员的集合,如果选择名字(“name”)字段作为片键,第一片可能会存放名字以A F开头的文档,第二个存放的是以G P开头的文档,第三个存的Q~Z的名字。随着添加(删除)片,MonogDB会重新平衡数据,使每片的流量都比较均衡,数据量也在合理范围内。
按照片键取值范围来作为数据块划分的区间依据,优点是按范围查询的时候它的效率很高,当给定一个查询范围,根据mongos中的映射表可以很快的定位到分片上的数据块。除此之外当两个分片的键取值比较靠近的时候,会被放到相近的块中,由于数据的局部性原理,这样的话可以加快查询效率,同时也可以减少内存换页次数。
缺点是可能会导致数据分布不均衡,如果选择的片键具有线性的性质,例如时间,将其作为片键的话 ,在某个时间段的写请求(读请求)都会被映射到同一个分片的同一个数据块上, 这样的话不仅会降低系统的读写性能,而且也会因写操作过于集中导致片间的不平衡。
(2)按照片键哈希值来作为数据块的划分区间依据
优点是可以确保一个比较均衡的数据分布,因为即使当两个文档的片键取值很接近的时候,例如上面例子中一个x=25,一个x=26,它们的哈希结果也会有很大的差别,这样的话数据会随机的分布到集群中,有利于数据的均衡的分布,减少数据块的移动次数,同时由于数据分散会减少单个数据块的写操作的压力,提高写入速度。
缺点是随机划分导致数据过于分散,当要查询某个范围内的数据时比如年龄大于20小于25的所有男生信息,如果直接使用范围划分的话,由于其具有良好的数据局部性特点,可能只要访问几个相邻的数据块就行了, 但是如果要使用哈希划分的方法很可能要访问所有的数据块。
(3)取值有限的片键
这是一种粗力度的片键,比如上边说的用户ID。如果按照用户ID分片,你可以预料到插入会分布在各个分片上,因为无法预知哪个用户何时会插入数据。这样一来,粗粒度分片键也能拥有随机性,还能发挥分片集群的优势。而且粗粒度的片键还能使用局部性带来的效率提升。当某个用户上传100个文件,基于用户ID字段的分片建能确保这些插入都落到同一个分片上,并几乎能写入索引的同一部分,这样效率很高。粗粒度分片键在分布性和局部性上都表现很好,但是它也有一个很难解决的问题:块有可能无限制的增长。想想基于用户ID的片键,假如有几个特殊用户,他们上传了上百万个文件,那么一个块里就可能只有一个用户ID,这个块能拆分么?不能,因为用户ID是最小的粒度,拆分了查询就没法路由到数据。这就造成分片之间数据量不均衡。更典型的就是type,status这类的字段,因为它们的选择性实在是太低,导致无法拆分。片键基比较小时,所有的键值相同导致MongoDB不能分裂Chunk,迁移这些不可分裂的Chunk将更加耗时,即使迁移后也难以保证数据在各个分片上的平衡。Chunk数量被基约束住后,我们就不能利用MongoD分片集群特性将集合部署到更多的机器。
2 片键的选取原则
在Sharding结构中,分片策略,片键选择是影响性能的关键因素,片键不仅影响数据分布,而且影响业务逻辑,所以片键的选择不单单是均匀的将数据分布到各个片上,而且要考虑查询的性能。坏的片键有时候会导致数据分布很差,有时候会导致无法使用局部性原理,还有一些会影响数据块的拆分。
上边我们讨论了低效片键的问题和原因,理想的片键应该结合粗粒度分片键与细粒度片键两者的优势。
一个好的片键必须包含的特性:
1、保证CRUD能利用局限性 ==》升序片键的优点
2、将插入数据均匀分布到各个分片上 ==》随机片键的优点
3、有足够的粒度进行块拆分 ==》粗粒度片键的优点
满足这些要求的的片键通常由两个字段组成,第一个是粗粒度,第二个是粒度较细。那么我们需要使用复合片键。例如对上面的例子,选取{userid:1,_id:1}作为片键,当用户同时插入数据时,我们可以预见大多数情况下,这些数据会被均匀的分布到所有的片上,而且分片里的唯一字段_id能保证对任意一个文档的查询和更新始终都能指向单个分片。如果对用户ID执行更复杂的查询,那么路由也只会将查询路由包含此用户ID存在的片上,而不会发到所有分片。由于_id(升序)的存在,保证了块始终是能继续拆分的,哪怕用户创建了大量文档,情况也是如此。
所以在选择片键时尽量能保持良好的数据局部性而又不会导致过度热点的出现,很多时候,组合片键是一种比较常用的做法。
除此之外,也可以选择我们经常查询的字段作为片键,这类分片键可以使得查询时mongos仅仅将查询发送给特定的mongod实例,不需要等待多个实例返回数据后再进行合并。
以上就是关于现在最成熟的开源nosql是什么分别有什么优缺点全部的内容,包括:现在最成熟的开源nosql是什么分别有什么优缺点、mysql mongodb区别、mongodb数据库适合做什么等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!