nosql学习笔记

来源:互联网 发布:数据作战室好用吗 编辑:程序博客网 时间:2024/05/14 16:04

关系型数据库缺点:

1.复杂导致不确定性。使用SQL的一个问题就是计算某个查询的代价或者产生的负载几乎是不可能的。使用简单的查询语言可能会导致应用层的逻辑更复杂,但是这样可以将存储系统的工作简单化,让它只需要响应一些简单的请求

2.对一个问题建模有很多种方式。其中关联型的数据模型是非常严格的一种:表结构的定义规定了表中每一行数据的存储内容。如果你的数据结构化并没有那么强,或者对每一行数据的要求比较灵活,那可能关联型的数据模型就太过严格了。类似的,应用层的开发人员可能对关联型的数据结构并不满意。比如很多应用程序是用面向对象的语言写的,数据在这些语言中通常是以列表、队列或集合的形式组织的,程序员们当然希望他们的数据存储层也能和应用层的数据模型一致。

3.当数据量增长到一台机器已经不能容纳,我们需要将不同的数据表分布到不同的机器。而为了避免在不同机器上的数据表在进行联合查询时需要跨网络进行。我们必须进行反范式的数据库设计,这种设计方式要求我们把需要一次性查询到的数据存储在一起

选择nosql注意事项

数据模型:模型是文档,对象还是行存储呢,是否适合你的业务需求。

可靠性:更新时是否立刻保存到持久化文件中,是否同步到多个节点中。

扩展性:单机是否可以支持你的业务需求。

分区策略:考虑扩展性时,是否需要将数据保存到多个节点.

一致性:数据怎样保证一致性。

事务:你的业务是否支持ACID

单机性能:考虑单机是否可以承受你的性能(读写要求)

负载:考虑读多写少的情况,需要考虑负载功能。

操作模型:考虑操作模型是sql,key value,map reduce中的哪一种,是否符合你的需求。

数据存储方式:

key-value

典型代表:memcached

value可以是任何数据,不提供真对属性的操作,只提供set,get,delete

key-结构化数据

典型代表:redis

value可以是字符串,列表,集合,有序集合,提供针对特定数据结构的操作。

key-文档

典型代表:mongo

value保存类似json格式的结构化文档,虽然结构化文档可以保存任何灵活的格式,查是应用层查询比较复杂

列式存储

典型代表:bigtable,hbase,Cassandra

这种数据模型的特点是列式存储,每一行数据的各项被存储在不同的列中(这些列的集合称作列簇)。而每一列中每一个数据都包含一个时间戳属性,这样列中的同一个数据项的多个版本都能保存下来。列式存储可以理解成这样,将行ID、列簇号,列号以及时间戳一起,组成一个Key,然后将Value按Key的顺序进行存储

复杂查询

在NoSQL存储系统中,有很多比键值查找更复杂的操作。比如MongoDB可以在任意数据行上建立索引,可以使用Javascript语法设定复杂的查询条件。BigTable型的系统通常支持对单独某一行的数据进行遍历,允许对单列的数据进行按特定条件地筛选。CouchDB允许你创建同一份数据的多个视图,通过运行MapReduce任务来实现一些更为复杂的查询或者更新操作。很多NoSQL系统都支持与Hadoop或者其它一些MapReduce框架结合来进行一些大规模数据分析工作。

数据可靠性

单机可靠性

fsync

由于操作系统的buffer存在,数据还是不会立刻被写到物理磁盘上,只有调用fsync时才会写到磁盘,尽量减少随机写,增加顺序写,增加性能。

日志型数据结构

由于fsync会产生随机写,所以很多nosql都会把写操作顺序的写入到一个日志文件中上面说到的日志文件可以频繁的进行fsync操作。这个日志文件记录了操作行为,可以用于在出现故障后恢复丢失那段时间的数据,这样就把随机写变成顺序写了

多机可靠性

Redis采用了传统的主从数据同步的方式。所有在master上执行的操作,都会通过类似于操作日志的结构顺序地传递给slave上再执行一遍。如果master发生宕机等事故,slave可以继续执行完master传来的操作日志并且成为新的master。可能这中间会导致一些数据丢失,因为master同步操作到slave是非阻塞的,master并不知道操作是否已经同步线slave了。CouchDB 实现了一个类似的指向性的同步功能,它使得一个写操作可以同步到其它节点上。

MongoDB提供了一个叫Replica Sets的架构方制,这个架构策略使得每一个文档都会保存在组成Replica Sets的所有机器上。MongoDB提供了一些选项,让开发者可以确定一个写操作是否已经同步到了所有节点上,也可以在节点数据并不是最新的情况下执行一些操作。很多其它的分布式NoSQL存储都提供了类似的多机可靠性支持。由于HBase的底层存储是HDFS,它也就自然的获得了HDFS提供的多机可靠性保证。HDFS的多机可靠性保证是通过把每个写操作都同步到两个以上的节点来实现的。

Riak、Cassandra和Voldemort提供了一些更灵活的可配置策略。这三个系统提供一个可配置的参数N,代表每一个数据会被备份的份数,然后还可以配置一个参数W,代表每个写操作需要同步到多少能机器上才返回成功。当然W是小于N的。

横向扩展

横向扩展的目标是达到线性的效果,即如果你增加一倍的机器,那么负载能力应该也能相应的增加一倍。其主要需要解决的问题是如何让数据在多台机器间分布。数据分片技术实际上就是将对数据和读写请求在多个机器节点上进行分配的技术

分片会导致系统复杂程序大增,所以,如果没有必要,请不要使用分片。下面我们先讲两种不用分片就能让系统具有扩展性的方法。

读写分离

大多数应用场景都是读多写少的场景。所以在这种情况下,可以用一个简单的方法来分担负载,就是把数据同步到多台机器上。这时候写请求还是由master机器处理,而读请求则可以分担给那些同步到数据的机器了。而同步数据的操作,通常是不会对master带来多大的压力的。

如果你已经使用了主从配置,将数据同步到多台机器以提供高可靠性了,那么你的slave机器应该能够为master分担不少压力了。对有些实时性要求不是非常高的查询请求,比如一些统计操作,你完全可以放到slave上来执行。通常来说,你的应用对实时性要求越低,你的slave机器就能承担越多的任务。

使用缓存

将一些经常访问的数据放到缓存层中,通常会带来很好的效果。Memcached 主要的作用就是将数据层的数据进行分布式的缓存。Memcached 通过客户端的算法(译者: 常见的一致性hash算法)来实现横向扩展,这样当你想增大你缓存池的大小时,只需要添加一台新的缓存机器即可。

由于Memcached仅仅是一个缓存存储,它并不具备一些持久存储的复杂特性。当你在考虑使用复杂的扩展方案时,希望你先考虑一下使用缓存来解决你的负载问题。注意,缓存并不是临时的处理方案:Facebook 就部署了总容量达到几十TB的Memcahced内存池。

通过读写分离和构建有效的缓存层,通常可以大大分担系统的读负载,但是当你的写请求越来越频繁的时候,你的master机器还是会承受越来越大的压力。对于这种情况,我们可能就要用到下面说到的数据分片技术了。

13.4.3 一致性hash环算法

一致性hash算法的工作原理如下:首先我们有一个hash函数H,可以通过数据的key值计算出一个数字型的hash值。然后我们将整个hash环的范围定义为[1,L]这个区间,我们将刚才算出的hash值对L进行取余,就能算出一个key值在这个环上的位置。而每一台真实服务器结点就会负责[1-L]之间的某个区间的数据

备份数据

一致性hash下的数据备份通常采用下面的方法:将数据冗余的存在其归属的节点的顺序往下的节点,例如你的冗余系数为3(即数据会在不同节点中保存三份),那么如果通过hash计算你的数据在A区间[7,233],你的数据会被同时保存在A,B,C三个节点上。这样如果A节点出现故障,那么B,C节点就能处理这部分数据的请求了。而某些设计会使E节点将自己的范围扩大到A233,以接受对出故障的A节点的请求。

优化的数据分配策略

虽然hash算法能够产生相对均匀的hash值。而且通常是节点数量越多,hash算法会越平均的分配key值。然而通常在项目初期不会有太多的数据,当然也不需要那么多的机器节点,这时候就会造成数据分配不平均的问题。比如上面的5个节点,其中A节点需要负责的hash区间范围大小为227,而E节点负责的区间范围为132。同时在这种情况下,出故障后数据请求转移到相邻节点的策略也可能不好实施了。

为了解决由于节点比较少导致数据分配不均的问题,很多DHT系统都实现了一种叫做虚拟节点的技术。例如4个虚拟节点的系统中,A节点可能被虚拟化成A_1,A_2,A_3,A_4这四个虚拟节点,然后对这四个虚拟节点再进行hash运算,A节点负责的key值区间就比较分散了。Voldemort 使用了与上面类似的策略,它允许对虚拟节点数进行配置,通常这个节点数会大于真实节点数,这样每个真实节点实际上是负责了N个虚拟节点上的数据。

连续范围分区

使用连续范围分区的方法进行数据分片,需要我们保存一份映射关系表,标明哪一段key值对应存在哪台机器上。和一致性hash类似,连续范围分区会把key值按连续的范围分段,每段数据会被指定保存在某个节点上,然后会被冗余备份到其它的节点。和一致性hash不同的是,连续范围分区使得key值上相邻的两个数据在存储上也基本上是在同一个数据段。这样数据路由表只需记录某段数据的开始和结束点[start,end]就可以了。

通过动态调整数据段到机器结点的映射关系,可以更精确的平衡各节点机器负载。如果某个区段的数据负载比较大,那么负载控制器就可以通过缩短其所在节点负责的数据段,或者直接减少其负责的数据分片数目。通过添加这样一个监控和路由模块,使我们能够更好的对数据节点进行负载均衡。

选择哪种分区策略

上面我们说到了Hash分区和范围分区两种策略,哪种更好呢?这要看情况了,如果你需要经常做范围查询,需要按顺序对key值进行操作,那么你选择范围分区会比较好。因为如果选择hash分区的话,要查询一个范围的数据可能就需要跨好几个节点来进行了。

那如果我不会进行范围查询或者顺序查询呢?这时候hash分区相对来说可能更方便一点,而且hash分区时可能通过虚拟结点的设置来解决hash不均的问题。在hash分区中,基本上只要在客户端执行相应的hash函数就能知道对应的数据存在哪个节点上了。而如果考虑到节点故障后的数据转移情况,可能获取到数据存放节点就会麻烦一些了。

范围分区要求在查询数据前对配置节点还要进行一次查询,如果没有特别好的高可用容灾方案,配置节点将会是一个危险的故障单点。当然,你可以把配置节点再进行一层负载均衡来减轻负载。而范围分区时如果某个节点故障了,它上面的数据可以被分配到多个节点上,而不像在一致性hash时,只能迁移到其顺序的后一个节点,造成下一个节点的负载飙升。

一致性

在NoSQL中,通常有两个层次的一致性:第一种是强一致性,既集群中的所有机器状态同步保持一致。第二种是最终一致性,既可以允许短暂的数据不一致,但数据最终会保持一致