NoSQL架构实践
经常有朋友遇到困惑,看到NoSQL的介绍,觉得很好,但是却不知道如何正式用到自己的项目中。很大的原因就是思维固定在MySQL中了,他们问得最多的问题就是用了NoSQL,我如何做关系查询。那么接下来,我们看下怎么样在我们的系统中使用NoSQL。
怎么样把NoSQL引入到我们的系统架构设计中,需要根据我们系统的业务场景来分析,什么样类型的数据适合存储在NoSQL数据库中,什么样类型的数据必须使用关系数据库存储。明确引入的NoSQL数据库带给系统的作用,它能解决什么问题,以及可能带来的新的问题。下面我们分析几种常见的NoSQL架构。
(一)NoSQL作为镜像
不改变原有的以MySQL作为存储的架构,使用NoSQL作为辅助镜像存储,用NoSQL的优势辅助提升性能。
图 1 -NoSQL为镜像(代码完成模式 )
//写入数据的示例伪代码 //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数据库
如果有数据一致性要求,可以像如下的方式使用
//写入数据的示例伪代码
//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层做一个统一的封装,就能实现重用了,其他代码都不用做任何的修改。
这种架构在原有基于MySQL数据库的架构上增加了一层辅助的NoSQL存储,代码量不大,技术难度小,却在可扩展性和性能上起到了非常大的作用。只需要程序在写入MySQL数据库后,同时写入到NoSQL数据库,让MySQL和NoSQL拥有相同的镜像数据,在某些可以根据主键查询的地方,使用高效的NoSQL数据库查询,这样就节省了MySQL的查询,用NoSQL的高性能来抵挡这些查询。
图 2 -NoSQL为镜像(同步模式)
这种不通过程序代码,而是通过MySQL把数据同步到NoSQL中,这种模式是上面一种的变体,是一种对写入透明但是具有更高技术难度一种模式。这种模式适用于现有的比较复杂的老系统,通过修改代码不易实现,可能引起新的问题。同时也适用于需要把数据同步到多种类型的存储中。
MySQL到NoSQL同步的实现可以使用MySQL UDF函数,MySQL binlog的解析来实现。可以利用现有的开源项目来实现,比如:
- MySQL memcached UDFs:从通过UDF操作Memcached协议。
- 国内张宴开源的mysql-udf-http:通过UDF操作http协议。
有了这两个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的资料:
- 《视觉中国的NoSQL之路:从MySQL到MongoDB》
- 《Choosing a non-relational database; why we migrated from MySQL to MongoDB》
- 最近的一次Mongo Beijing 开发者聚会也有一部分资料。
虽然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使用经验才行。
NoSQL架构实践的更多相关文章
- NoSQL架构实践(一)——以NoSQL为辅
前面<为什么要使用NoSQL>和<关系数据库还是NoSQL数据库>两篇从大体上介绍了为什么要用NoSQL,何时该用NoSQL.经常有朋友遇到困惑,看到NoSQL的介绍,觉得很好 ...
- 基于 Docker 的微服务架构实践
本文来自作者 未闻 在 GitChat 分享的{基于 Docker 的微服务架构实践} 前言 基于 Docker 的容器技术是在2015年的时候开始接触的,两年多的时间,作为一名 Docker 的 D ...
- 朱晔的互联网架构实践心得S2E6:浅谈高并发架构设计的16招
朱晔的互联网架构实践心得S2E6:浅谈高并发架构设计的16招 概览 标题中的高并发架构设计是指设计一套比较合适的架构来应对请求.并发量很大的系统,使系统的稳定性.响应时间符合预期并且能在极端的情况下自 ...
- Windows平台分布式架构实践 - 负载均衡(下)
概述 我们在上一篇Windows平台分布式架构实践 - 负载均衡中讨论了Windows平台下通过NLB(Network Load Balancer) 来实现网站的负载均衡,并且通过压力测试演示了它的效 ...
- 大型网站系统架构实践(五)深入探讨web应用高可用方案
从上篇文章到这篇文章,中间用了一段时间准备,主要是想把东西讲透,同时希望大家给与一些批评和建议,这样我才能有所进步,也希望喜欢我文章的朋友,给个赞,这样我才能更有激情,呵呵. 由于本篇要写的内容有点多 ...
- 大型网站系统架构实践(六)深入探讨web应用集群Session保持
原理 在第三,四篇文章中讲到了会话保持的问题,而且还遗留了一个问题,就是会话保持存在单点故障, 当时的方案是cookie插入后缀,即haproxy指负责分发请求,应用服务自行保持用户会话,如果应 用服 ...
- Windows平台分布式架构实践负载均衡
Windows平台分布式架构实践 - 负载均衡 概述 最近.NET的世界开始闹腾了,微软官方终于加入到了对.NET跨平台的支持,并且在不久的将来,我们在VS里面写的代码可能就可以通过Mono直接在Li ...
- 单KEY业务,数据库水平切分架构实践
本文将以"用户中心"为例,介绍"单KEY"类业务,随着数据量的逐步增大,数据库性能显著降低,数据库水平切分相关的架构实践: 如何来实施水平切分 水平切分后常见的 ...
- 《App架构实践指南》
推荐书籍 <App 架构实践指南>
随机推荐
- Qt分析:Qt中的两种定时器(可是QObject为什么要提高定时器呢,没必要啊。。。)
Qt有两种定时器,一种是QObject类的定时器,另一种是QTimer类的定时器. (1)QObject类的定时器 QObject类提供了一个基本的定时器,通过函数startTimer()来启 ...
- C++ ofstream和ifstream详细用法
转载地址:http://soft.chinabyte.com/database/460/11433960.shtml ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就 ...
- 将 Java Spring Framework 应用程序迁移到 Windows Azure
我们刚刚发布了一个新教程和示例代码,以阐述如何在Windows Azure中使用 Java 相关技术.在该指南中,我们提供了分步教程,说明如何将 Java Spring Framework 应用程序( ...
- 为joomla加入�下拉菜单的方法
用 Joomla! 建站的大多数站长都须要在站点前台使用下拉菜单(dropdown menu),或者叫弹出菜单(slide menu),由于这样能够在有限的页面空间上公布很多其它的导航菜单,而且能够进 ...
- BitNami一键安装Redmine
1. 简单介绍 对于一个新手,假设严格依照官方文档来安装redmine,我想会"疯"掉的.有没有一种简便的方法.有滴,那就是BitNami. BitNami提供redmine的一键 ...
- 记录一次SQL查询语句
以前发现比较经典的句子,都是记录在电脑上,我今天想搬到博客上,在我看来,写博客真的是一件非常头疼的事,它是内心的一道坎,我必须得跨过它. CREATE TABLE t_jeff ( id int NO ...
- [Swust OJ 763]--校门外的树 Plus(暴力枚举)
题目链接:http://acm.swust.edu.cn/problem/0763/ Time limit(ms): 1000 Memory limit(kb): 65535 西南某科技大学的校门外有 ...
- (Problem 21)Amicable numbers
Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into ...
- CPU保护模式深入探秘
原文链接为:http://www.chinaunix.net/old_jh/23/483510.html 保护方式的体系结构 主要问题: 保护方式的寄存器模型 保护 ...
- validform 一行代码完成所有验证
validform一行代码完成所有验证 在使用的时候想更改提示效果为右侧提示却一直不能成功 tiptype可以为1.2 和 自定义函数.2 表示右侧提示. 注意:tiptype为 2 时,各表单元素对 ...