NoSQL架构实践

常常有朋友遇到困惑,看到NoSQL的介绍,以为很好,可是殊不知道如何正式用到本身的项目中。很大的缘由就是思惟固定在MySQL中了,他们问得最多的问题就是用了NoSQL,我如何作关系查询。那么接下来,咱们看下怎么样在咱们的系统中使用NoSQL。php

怎么样把NoSQL引入到咱们的系统架构设计中,须要根据咱们系统的业务场景来分析,什么样类型的数据适合存储在NoSQL数据库中,什么样类型的数据必须使用关系数据库存储。明确引入的NoSQL数据库带给系统的做用,它能解决什么问题,以及可能带来的新的问题。下面咱们分析几种常见的NoSQL架构。html

(一)NoSQL做为镜像

不改变原有的以MySQL做为存储的架构,使用NoSQL做为辅助镜像存储,用NoSQL的优点辅助提高性能。前端

图 1 -NoSQL为镜像(代码完成模式 )mysql

//写入数据的示例伪代码 

//data为咱们要存储的数据对象 
data.title=”title”; 
data.name=”name”; 
data.time=”2009-12-01 10:10:01”; 
data.from=”1”; 
id=DB.Insert(data);//写入MySQL数据库 
NoSQL.Add(id,data);//以写入MySQL产生的自增id为主键写入NoSQL数据库

若是有数据一致性要求,能够像以下的方式使用web

//写入数据的示例伪代码 
//data为咱们要存储的数据对象 
bool status=false; 
DB.startTransaction();//开始事务 
id=DB.Insert(data);//写入MySQL数据库 
if(id>0){ 
    status=NoSQL.Add(id,data);//以写入MySQL产生的自增id为主键写入NoSQL数据库 
} 
if(id>0 && status==true){ 
    DB.commit();//提交事务 
}else{ 
    DB.rollback();//不成功,进行回滚 
}

上面的代码看起来可能以为有点麻烦,可是只须要在DB类或者ORM层作一个统一的封装,就能实现重用了,其余代码都不用作任何的修改。sql

这种架构在原有基于MySQL数据库的架构上增长了一层辅助的NoSQL存储,代码量不大,技术难度小,却在可扩展性和性能上起到了很是大的做用。只须要程序在写入MySQL数据库后,同时写入到NoSQL数据库,让MySQL和NoSQL拥有相同的镜像数据,在某些能够根据主键查询的地方,使用高效的NoSQL数据库查询,这样就节省了MySQL的查询,用NoSQL的高性能来抵挡这些查询。mongodb

图 2 -NoSQL为镜像(同步模式)数据库

这种不经过程序代码,而是经过MySQL把数据同步到NoSQL中,这种模式是上面一种的变体,是一种对写入透明可是具备更高技术难度一种模式。这种模式适用于现有的比较复杂的老系统,经过修改代码不易实现,可能引发新的问题。同时也适用于须要把数据同步到多种类型的存储中。json

MySQL到NoSQL同步的实现可使用MySQL UDF函数,MySQL binlog的解析来实现。能够利用现有的开源项目来实现,好比:后端

有了这两个MySQL UDF函数库,咱们就能经过MySQL透明的处理Memcached或者Http协议,这样只要有兼容Memcached或者Http协议的NoSQL数据库,那么咱们就能经过MySQL去操做以进行同步数据。再结合lib_mysqludf_json,经过UDF和MySQL触发器功能的结合,就能够实现数据的自动同步。

(二)MySQL和NoSQL组合

MySQL中只存储须要查询的小字段,NoSQL存储全部数据。

图 3 -MySQL和NoSQL组合

//写入数据的示例伪代码 

//data为咱们要存储的数据对象 
data.title=”title”; 
data.name=”name”; 
data.time=”2009-12-01 10:10:01”;
data.from=”1”;
bool status=false; 
DB.startTransaction();//开始事务 
id=DB.Insert(“INSERT INTO table (from) VALUES(data.from)”);//写入MySQL数据库,只写from须要where查询的字段 
if(id>0){ 
    status=NoSQL.Add(id,data);//以写入MySQL产生的自增id为主键写入NoSQL数据库 
} 
if(id>0 && status==true){ 
    DB.commit();//提交事务 
}else{ 
    DB.rollback();//不成功,进行回滚 
}

把须要查询的字段,通常都是数字,时间等类型的小字段存储于MySQL中,根据查询创建相应的索引,其余不须要的字段,包括大文本字段都存储在NoSQL中。在查询的时候,咱们先从MySQL中查询出数据的主键,而后从NoSQL中直接取出对应的数据便可。

这种架构模式把MySQL和NoSQL的做用进行了融合,各司其职,让MySQL专门负责处理擅长的关系存储,NoSQL做为数据的存储。它有如下优势:

  • 节省MySQL的IO开销。因为MySQL只存储须要查询的小字段,再也不负责存储大文本字段,这样就能够节省MySQL存储的空间开销,从而节省MySQL的磁盘IO。咱们曾经经过这种优化,把MySQL一个40G的表缩减到几百M。
  • 提升MySQl Query Cache缓存命中率。咱们知道query cache缓存失效是表级的,在MySQL表一旦被更新就会失效,通过这种字段的分离,更新的字段若是不是存储在MySQL中,那么对query cache就没有任何影响。而NoSQL的Cache每每都是行级别的,只对更新的记录的缓存失效。
  • 提高MySQL主从同步效率。因为MySQL存储空间的减少,同步的数据记录也减少了,而部分数据的更新落在NoSQL而不是MySQL,这样也减小了MySQL数据须要同步的次数。
  • 提升MySQL数据备份和恢复的速度。因为MySQL数据库存储的数据的减少,很容易看到数据备份和恢复的速度也将极大的提升。
  • 比之前更容易扩展。NoSQL天生就容易扩展。通过这种优化,MySQL性能也获得提升。

好比手机凤凰网就是这种架构 http://www.cnblogs.com/sunli/archive/2010/12/20/imcp.html

 



(三)纯NoSQL架构

只使用NoSQL做为数据存储。

图 4-纯NoSQL架构

在一些数据结构、查询关系很是简单的系统中,咱们能够只使用NoSQL便可以解决存储问题。这样不但能够提升性能,还很是易于扩展。手机凤凰网的前端展现系统就使用了这种方案。

在一些数据库结构常常变化,数据结构不定的系统中,就很是适合使用NoSQL来存储。好比监控系统中的监控信息的存储,可能每种类型的监控信息都不太同样。这样能够避免常常对MySQL进行表结构调整,增长字段带来的性能问题。

这种架构的缺点就是数据直接存储在NoSQL中,不能作关系数据库的复杂查询,若是因为需求变动,须要进行某些查询,可能没法知足,因此采用这种架构的时候须要确认将来是否会进行复杂关系查询以及如何应对。

很是幸运的是,有些NoSQL数据库已经具备部分关系数据库的关系查询特性,他们的功能介于key-value和关系数据库之间,却具备key-value数据库的性能,基本能知足绝大部分web 2.0网站的查询需求。好比:

MongoDB就带有关系查询的功能,能解决经常使用的关系查询,因此也是一种很是不错的选择。下面是一些MongoDB的资料:

虽然Foursquare使用MongoDB的宕机事件的出现令人对MongoDB的自动Shard提出了质疑,可是毫无疑问,MongoDB在NoSQL中,是一个优秀的数据库,其单机性能和功能确实是很是吸引人的。因为上面的例子有详细的介绍,本文就不作MongoDB的使用介绍。

Tokyo Tyrant数据库带有一个名为table的存储类型,能够对存储的数据进行关系查询和检索。一个table库相似于MySQL中的一个表。下面咱们看一个小演示:

咱们要存储一批用户信息,用户信息包含用户名(name),年龄(age),email,最后访问时间(lastvisit),地区(area)。下面为写入的演示代码:

<? php
$tt = new TokyoTyrantTable ( " 127.0.0.1 " , 1978 );
$tt -> vanish (); // 清空
$id = $tt -> genUid (); // 获取一个自增id
//put方法提供数据写入。 put ( string $key , array $columns );

$tt -> put ( $id , array ( " id " => $id , " name " => " zhangsan " , " age " => 27 , " email " => " zhangsan@gmail.com " , " lastvisit " => strtotime ( " 2011-3-5 12:30:00 " ) , " area " => " 北京 " ) );
$id = $tt -> genUid ();
$tt -> put ( $id , array ( " id " => $id , " name " => " lisi " , " age " => 25 , " email " => " lisi@126.com " , " lastvisit " => strtotime ( " 2011-3-3 14:40:44 " ) , " area " => " 北京 " ) );
$id = $tt -> genUid ();
$tt -> put ( $id , array ( " id " => $id , " name " => " laowang " , " age " => 37 , " email " => " laowang@yahoo.com " , " lastvisit " => strtotime ( " 2011-3-5 08:30:12 " ) , " area " => " 成都 " ) );
$id = $tt -> genUid ();
$tt -> put ( $id , array ( " id " => $id , " name " => " tom " , " age " => 21 , " email " => " tom@hotmail.com " , " lastvisit " => strtotime ( " 2010-12-10 13:12:13 " ) , " area " => " 天津 " ) );
$id = $tt -> genUid ();
$tt -> put ( $id , array ( " id " => $id , " name " => " jack " , " age " => 21 , " email " => " jack@gmail.com " , " lastvisit " => strtotime ( " 2011-02-24 20:12:55 " ) , " area " => " 天津 " ) );
// 循环打印数据库的全部数据库
$it = $tt -> getIterator ();
foreach ( $it as $k => $v ) {
print_r ( $v );
}
?>

好比咱们须要查询年龄为21岁的全部用户:

<? php
$tt = new TokyoTyrantTable ( " 127.0.0.1 " , 1978 );
$query = $tt -> getQuery ();
// 查询年龄为21岁的用户
$query -> addCond ( “age” , TokyoTyrant :: RDBQC_NUMEQ , 21 ” );
print_r ( $query -> search () );
?>

查询全部在2011年3月5日以后登录的用户:

<? php
$tt = new TokyoTyrantTable ( " 127.0.0.1 " , 1978 );
$query = $tt -> getQuery ();
$query -> addCond ( “lastvisit” , TokyoTyrant :: RDBQC_NUMGE , strtotime ( " 2011-3-5 00:00:00 " ) );
print_r ( $query -> search () );
?>

从上面的示例代码能够看出,使用起来是很是简单的,甚至比SQL语句还要简单。Tokyo Tyrant的表类型存储还提供了给字段创建普通索引和倒排全文索引,大大加强了其检索功能和检索的性能。

因此,彻底用NoSQL来构建部分系统,是彻底可能的。配合部分带有关系查询功能的NoSQL,在开发上比MySQL数据库更加快速和高效。

(四)以NoSQL为数据源的架构

数据直接写入NoSQL,再经过NoSQL同步协议复制到其余存储。根据应用的逻辑来决定去相应的存储获取数据。

图 5 -以NoSQL为数据源

纯NoSQL的架构虽然结构简单,易于开发,可是在应付需求的变动、稳定性和可靠性上,老是给开发人员一种风险难于控制的感受。为了下降风险,系统的功能不局限在NoSQL的简单功能上,咱们可使用以NoSQL为数据源的架构。

在这种架构中,应用程序只负责把数据直接写入到NoSQL数据库就OK,而后经过NoSQL的复制协议,把NoSQL数据的每次写入,更新,删除操做都复制到MySQL数据库中。同 时,也能够经过复制协议把数据同步复制到全文检索实现强大的检索功能。在海量数据下面,咱们也能够根据不一样的规则,把数据同步复制到设计好的分表分库的 MySQL中。这种架构:

  • 很是灵活。能够很是方便的在线上系统运行过程当中进行数据的调整,好比调整分库分表的规则、要添加一种新的存储类型等等。
  • 操做简单。只须要写入NoSQL数据库源,应用程序就不用管了。须要增长存储类型或者调整存储规则的时候,只须要增长同步的数据存储,调整同步规则便可,无需更改应用程序的代码。
  • 性能高。数据的写入和更新直接操做NoSQL,实现了写的高性能。而经过同步协议,把数据复制到各类适合查询类型的存储中(按照业务逻辑区分不一样的存储),能实现查询的高性能,不像之前MySQL一种数据库就全包了。或者就一个表负责跟这个表相关的全部的查询,如今能够把一个表的数据复制到各类存储,让各类存储用本身的长处来对外服务。
  • 易扩展。开发人员只须要关心写入NoSQL数据库。数据的扩展能够方便的在后端由复制协议根据规则来完成。

这种架构须要考虑数据复制的延迟问题,这跟使用MySQL的master-salve模式的延迟问题是同样的,解决方法也同样。

在这种以NoSQL为数据源的架构中,最核心的就是NoSQL数据库的复制功能的实现。而当前的几乎全部的NoSQL都没有提供比较易于使用的复制接口来完成这种架构,对NoSQL进行复制协议的二次开发,须要更高的技术水平,因此这种架构看起来很好,可是却不是很是容易实现的。个人开源项目PHPBuffer中有个实现TokyoTyrant复制的例子,虽然是PHP版本的,可是很容易就能够翻译成其余语言。经过这个例子的代码,能够实现从Tokyo Tyrant实时的复制数据到其余系统中。

总结

以NoSQL为主的架构应该算是对NoSQL的一种深度应用,整个系统的架构以及代码都不是很复杂,可是却须要必定的NoSQL使用经验才行。