加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

NoSQL架构实践(一)——以NoSQL为辅

发布时间:2020-12-13 13:40:05 所属栏目:百科 来源:网络整理
导读:原文地址:http://www.infoq.com/cn/news/2011/02/nosql-architecture-practice/ - 前面《为什么要使用NoSQL》和《关系数据库还是NoSQL数据库》两篇从大体上介绍了为什么要用NoSQL,何时该用NoSQL。经常有朋友遇到困惑,看到NoSQL的介绍,觉得很好,但是却不

原文地址:http://www.infoq.com/cn/news/2011/02/nosql-architecture-practice/
- 前面《为什么要使用NoSQL》和《关系数据库还是NoSQL数据库》两篇从大体上介绍了为什么要用NoSQL,何时该用NoSQL。经常有朋友遇到困惑,看到NoSQL的介绍,觉得很好,但是却不知道如何正式用到自己的项目中。很大的原因就是思维固定在MySQL中了,他们问得最多的问题就是用了NoSQL,我如何做关系查询。那么接下来,我们看下怎么样在我们的系统中使用NoSQL。

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

(一)NoSQL作为镜像

  • 不改变原有的以MySQL作为存储的架构,使用NoSQL作为辅助镜像存储,用NoSQL的优势辅助提升性能。

    图 1 -NoSQL为镜像(代码完成模式 )
<code class="hljs haskell has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">//写入数据的示例伪代码 

//<span class="hljs-typedef" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">data</span>为我们要存储的数据对象 </span>
<span class="hljs-typedef" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">data</span>.title=”title”; </span>
<span class="hljs-typedef" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">data</span>.name=”name”; </span>
<span class="hljs-typedef" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">data</span>.time=”2009-12-01 10:10:01”; </span>
<span class="hljs-typedef" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">data</span>.from=”1”; </span>
<span class="hljs-title" style="box-sizing: border-box;">id</span>=<span class="hljs-type" style="box-sizing: border-box; color: rgb(102,102);">DB</span>.<span class="hljs-type" style="box-sizing: border-box; color: rgb(102,102);">Insert</span>(<span class="hljs-typedef" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">data</span>);//写入<span class="hljs-type" style="box-sizing: border-box; color: rgb(102,102);">MySQL</span>数据库 </span>
<span class="hljs-type" style="box-sizing: border-box; color: rgb(102,102);">NoSQL</span>.<span class="hljs-type" style="box-sizing: border-box; color: rgb(102,102);">Add</span>(id,<span class="hljs-typedef" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">data</span>);//以写入<span class="hljs-type" style="box-sizing: border-box; color: rgb(102,102);">MySQL</span>产生的自增id为主键写入<span class="hljs-type" style="box-sizing: border-box; color: rgb(102,102);">NoSQL</span>数据库</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221,221,221); list-style: none; text-align: right; background-color: rgb(238,238,238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>
  • 如果有数据一致性要求,可以像如下的方式使用
<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//写入数据的示例伪代码 </span>
<span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//data为我们要存储的数据对象 </span>
<span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">bool</span> status=<span class="hljs-literal" style="color: rgb(0,102,102); box-sizing: border-box;">false</span>; 
DB<span class="hljs-variable" style="color: rgb(102,102); box-sizing: border-box;">.startTransaction</span>();<span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//开始事务 </span>
<span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">id</span>=DB<span class="hljs-variable" style="color: rgb(102,102); box-sizing: border-box;">.Insert</span>(data);<span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//写入MySQL数据库 </span>
<span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">if</span>(<span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">id</span>><span class="hljs-number" style="color: rgb(0,102); box-sizing: border-box;">0</span>){ 
    status=NoSQL<span class="hljs-variable" style="color: rgb(102,102); box-sizing: border-box;">.Add</span>(<span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">id</span>,data);<span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//以写入MySQL产生的自增id为主键写入NoSQL数据库 </span>
} 
<span class="hljs-keyword" style="color: rgb(0,102); box-sizing: border-box;">0</span> && status==<span class="hljs-literal" style="color: rgb(0,102); box-sizing: border-box;">true</span>){ 
    DB<span class="hljs-variable" style="color: rgb(102,102); box-sizing: border-box;">.commit</span>();<span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//提交事务 </span>
}<span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">else</span>{ 
    DB<span class="hljs-variable" style="color: rgb(102,102); box-sizing: border-box;">.rollback</span>();<span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//不成功,进行回滚 </span>
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221,238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
  • 上面的代码看起来可能觉得有点麻烦,但是只需要在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组合
<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro',0); box-sizing: border-box;">//写入数据的示例伪代码 </span>

<span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//data为我们要存储的数据对象 </span>
<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">data</span><span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>title<span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">=</span>”title”; 
<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>name<span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">=</span>”name”; 
<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>time<span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">=</span>”<span class="hljs-number" style="color: rgb(0,102); box-sizing: border-box;">2009</span><span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">-</span><span class="hljs-number" style="color: rgb(0,102); box-sizing: border-box;">12</span><span class="hljs-subst" style="color: rgb(0,102); box-sizing: border-box;">01</span> <span class="hljs-number" style="color: rgb(0,102); box-sizing: border-box;">10</span>:<span class="hljs-number" style="color: rgb(0,102); box-sizing: border-box;">01</span>”;
<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>from<span class="hljs-subst" style="color: rgb(0,102); box-sizing: border-box;">1</span>”;
bool status<span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">=</span><span class="hljs-literal" style="color: rgb(0,102); box-sizing: border-box;">false</span>; 
DB<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>startTransaction();<span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//开始事务 </span>
id<span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">=</span>DB<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>Insert(“INSERT <span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">INTO</span> table (from) VALUES(<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>from)”);<span class="hljs-comment" style="color: rgb(136,0); box-sizing: border-box;">//写入MySQL数据库,只写from需要where查询的字段 </span>
<span class="hljs-keyword" style="color: rgb(0,136); box-sizing: border-box;">if</span>(id<span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">></span><span class="hljs-number" style="color: rgb(0,102); box-sizing: border-box;">0</span>){ 
    status<span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">=</span>NoSQL<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>Add(id,<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">data</span>);<span class="hljs-comment" style="color: rgb(136,102); box-sizing: border-box;">0</span> <span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">&&</span> status<span class="hljs-subst" style="color: rgb(0,0); box-sizing: border-box;">==</span><span class="hljs-literal" style="color: rgb(0,102); box-sizing: border-box;">true</span>){ 
    DB<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>commit();<span class="hljs-comment" style="color: rgb(136,136); box-sizing: border-box;">else</span>{ 
    DB<span class="hljs-built_in" style="color: rgb(102,102); box-sizing: border-box;">.</span>rollback();<span class="hljs-comment" style="color: rgb(136,238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li></ul>
  • 把需要查询的字段,一般都是数字,时间等类型的小字段存储于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为辅的架构还是以MySQL架构的思想为中心,只是在以前的架构上辅助增加了NoSQL来提高其性能和可扩展性。这种架构实现起来比较容易,却能取得不错的效果。如果正想在项目中引入NoSQL,或者你的以MySQL架构的系统目前正出现相关的瓶颈,希望本文可以为你带来帮助。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读