<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href='http://feed.feedsky.com/styles/temp01.xsl' type='text/xsl' ?><!--这是一个由Feedsy提供技术支持的Feed，为了提高读者阅读的体验，以及满足用户美化自己Feed的需要，我们设计了多种精美的Feed模板，提供给大家选择，所有最终呈现出来的样式，皆由用户自愿选择使用，未经许可，任何团体和个人，请不要擅自修改样式或者盗用，这是对于用户选择权的尊重。--><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:fs="http://www.feedsky.com/namespace/feed" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link href="http://feed.feedsky.com/iammutex" type="application/rss+xml" rel="self"></atom:link><fs:self_link href="http://feed.feedsky.com/iammutex" type="application/rss+xml"></fs:self_link><lastBuildDate>Sat, 25 Jun 2011 04:26:55 GMT</lastBuildDate><title>iammutex</title><description>我要经历你不曾经历的--磨难！</description><link>http://lgone.com</link><sy:updatePeriod>hourly</sy:updatePeriod><sy:updateFrequency>1</sy:updateFrequency><language>en</language><pubDate>Sat, 25 Jun 2011 04:28:40 GMT</pubDate><item><title>NoSQL生态系统翻译完稿</title><link>http://lgone.com/html/y2011/882.html</link><content:encoded>&lt;p&gt;好久不更新博客了，一方面因为工作比较忙，另外很多东西发到了&lt;a href=&quot;http://nosqlfan.com&quot;&gt;NoSQLFan&lt;/a&gt;上，由于精力有限，个人博客这边就荒于维护了。前段时间在网上看到大作 &lt;a href=&quot;http://www.lulu.com/content/paperback-book/the-architecture-of-open-source-applications/10559746&quot;&gt;The Architecture of Open Source Applications&lt;/a&gt; ，感觉其13章对NoSQL原理的介绍非常不错，于是萌生翻译的念头，花了半个多月业余时间，昨天终于完稿。现已发布到NoSQLFan上，关注NoSQL技术的同学可以看一下，其内容绝不输Bigtable论文和Dynamo论文。&lt;/p&gt;
&lt;p&gt;翻译过程还是比较有意思，虽然其中一些已经烂熟的东西会比较枯燥，但还是常常碰到原来理解不正确或者不清楚的地方，很庆幸在此次翻译中能够得到纠正。&lt;/p&gt;
&lt;p&gt;当然，最后还是要说，由于水平有限，错误之处难免，请各位发现错误多多指正。比如其原文中其实有一些不清楚或者不太正确的地方，我在翻译中也加了注释指出。&lt;/p&gt;
&lt;p&gt;好了，闲话扯到此，下面请各位移步了：&lt;/p&gt;
&lt;p&gt;［&lt;a href=&quot;https://docs.google.com/document/pub?id=1NO__9thOb8V1mM3iQID3IUmwGh9wtra7xkpmvCZIvoU&quot;&gt;HTML版&lt;/a&gt;］［&lt;a href=&quot;http://blog.nosqlfan.com/wp-content/uploads/2011/nosql_ecosystem.pdf&quot;&gt;PDF版&lt;/a&gt;］［&lt;a href=&quot;http://blog.nosqlfan.com/html/2171.html&quot;&gt;NoSQLFan版&lt;/a&gt;］&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;下面是目录&lt;/span&gt;：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;13.1 NoSQL其名&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.1.1 SQL及其关联型结构&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.1.2 NoSQL的启示&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.1.3 特性概述&lt;br /&gt;
13.2 NoSQL数据模型及操作模型&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.2.1 基于key值存储的NoSQL数据模型&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Key-Value 存储&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Key &amp;#8211; 结构化数据 存储&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Key &amp;#8211; 文档 存储&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BigTable 的列簇式存储&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.2.2 图结构存储&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.2.3 复杂查询&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.2.4 事务机制&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.2.5 Schema-free的存储&lt;br /&gt;
13.3 数据可靠性&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.3.1 单机可靠性&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;控制fsync的调用频率&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;使用日志型的数据结构&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;通过合并写操作提高吞吐性能&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.3.2 多机可靠性&lt;br /&gt;
13.4 横向扩展带来性能提升&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.4.1 如非必要，请勿分片&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;读写分离&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;使用缓存&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.4.2 通过协调器进行数据分片&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.4.3 一致性hash环算法&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Hash环图*&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;备份数据&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;优化的数据分配策略&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.4.4 连续范围分区&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BigTable的处理方式&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;故障处理&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;基于范围分区的NoSQL项目&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.4.5 选择哪种分区策略&lt;br /&gt;
13.5 一致性&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.5.1 关于CAP理论&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.5.2 强一致性&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;13.5.3 最终一致性&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;版本控制与冲突&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;冲突解决&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;读时修复&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Hinted Handoff&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Anti-Entropy&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Gossip&lt;br /&gt;
13.6 写在最后的话&lt;br /&gt;
13.7 致谢&lt;br /&gt;
相关信息&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369457/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2011/882.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2011/882.html/feed</wfw:commentRss><slash:comments>0</slash:comments><description>好久不更新博客了，一方面因为工作比较忙，另外很多东西发到了NoSQLFan上，由于精力有限，个人博客这边就荒于维护了。前段时间在网上看到大作 The Architecture of Open Source Applications ，感觉其13章对NoSQL原理的介绍非常不错，于是萌生翻译的念头，花了半个多月业余时间，昨天终于完稿。现已发布到NoSQLFan上，关注NoSQL技术的同学可以看一下，其内容绝不输Bigtable论文和Dynamo论文。
翻译过程还是比较有意思，虽然其中一些已经烂熟的东西会比较枯燥，但还是常常碰到原来理解不正确或者不清楚的地方，很庆幸在此次翻译中能够得到纠正。
当然，最后还是要说，由于水平有限，错误之处难免，请各位发现错误多多指正。比如其原文中其实有一些不清楚或者不太正确的地方，我在翻译中也加了注释指出。
好了，闲话扯到此，下面请各位移步了：
［HTML版］［PDF版］［NoSQLFan版］
下面是目录：
13.1 NoSQL其名&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.1.1 SQL及其关联型结构
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.1.2 NoSQL的启示
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.1.3 特性概述
13.2 NoSQL数据模型及操作模型
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.2.1 基于key值存储的NoSQL数据模型
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;Key-Value 存储
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;Key &amp;#8211; 结构化数据 存储
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;Key &amp;#8211; 文档 存储
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;BigTable 的列簇式存储
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.2.2 图结构存储
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.2.3 复杂查询
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.2.4 事务机制
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.2.5 Schema-free的存储
13.3 数据可靠性
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.3.1 单机可靠性
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;控制fsync的调用频率
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;使用日志型的数据结构
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;通过合并写操作提高吞吐性能
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.3.2 多机可靠性
13.4 横向扩展带来性能提升
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.4.1 如非必要，请勿分片
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;读写分离
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;使用缓存
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.4.2 通过协调器进行数据分片
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.4.3 一致性hash环算法
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;Hash环图*
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;备份数据
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;优化的数据分配策略
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.4.4 连续范围分区
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;BigTable的处理方式
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;故障处理
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;基于范围分区的NoSQL项目
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.4.5 选择哪种分区策略
13.5 一致性
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.5.1 关于CAP理论
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.5.2 强一致性
&amp;#160;&amp;#160;&amp;#160;&amp;#160;13.5.3 最终一致性
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;版本控制与冲突
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;冲突解决
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;读时修复
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;Hinted Handoff
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;Anti-Entropy
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;Gossip
13.6 写在最后的话
13.7 致谢
相关信息&lt;img src=&quot;http://www1.feedsky.com/t1/527369457/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2011/882.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>翻译</category><category>技术相关</category><category>NoSQL</category><pubDate>Sat, 25 Jun 2011 12:26:55 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2011/882.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=882</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2011/882.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369457/5457389</fs:itemid></item><item><title>优雅的Bitcask</title><link>http://lgone.com/html/y2010/861.html</link><content:encoded>&lt;p&gt;Bitcask是一个日志型的基于hash表结构和key-value存储模型，我了解到他也就几天时间，但是其简洁有效的设计思路，让我的某种技术癖好得到了极大满足，于是酝酿出这篇东西。&lt;/p&gt;
&lt;p&gt;Bitcask模型指导下的存储系统有Riak和豆瓣的beansdb新版本（beansdb新版本信息，参见&lt;a href=&quot;http://blog.nosqlfan.com/html/940.html&quot;&gt;这里&lt;/a&gt;），下面就简单的介绍一下Bitcask模型：&lt;/p&gt;
&lt;h2&gt;1.日志型的数据文件&lt;/h2&gt;
&lt;p&gt;何谓日志型？就是append only，所有写操作只追加而不修改老的数据，就像我们的各种服务器日志一样。在Bitcask模型中，数据文件以日志型只增不减的写入文件，而文件有一定的大小限制，当文件大小增加到相应的限制时，就会产生一个新的文件，老的文件将只读不写。在任意时间点，只有一个文件是可写的，在Bitcask模型中称其为active data file，而其他的已经达到限制大小的文件，称为older data file，如下图：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.21.38.jpg&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-862&quot; title=&quot;屏幕快照 2010-12-26 下午01.21.38&quot; src=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.21.38.jpg&quot; alt=&quot;&quot; width=&quot;469&quot; height=&quot;318&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;文件中的数据结构非常简单，是一条一条的数据写入操作，每一条数据的结构如下：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.24.25.jpg&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-863&quot; title=&quot;屏幕快照 2010-12-26 下午01.24.25&quot; src=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.24.25.jpg&quot; alt=&quot;&quot; width=&quot;587&quot; height=&quot;161&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;上面数据项分别为key，value，key的大小，value的大小，时间戳（应该是），以及对前面几项做的crc校验值。（数据删除操作也不会删除旧的条目，而是将value设定为一个特殊的值以作标示）&lt;/p&gt;
&lt;p&gt;数据文件中就是连续一条条上面格式的数据，如下图：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.30.13.jpg&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-864&quot; title=&quot;屏幕快照 2010-12-26 下午01.30.13&quot; src=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.30.13.jpg&quot; alt=&quot;&quot; width=&quot;570&quot; height=&quot;350&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;好了，上面是日志型的数据文件，如果数据文件这样持续的存下去，肯定是会无限膨胀的，为了解决个问题，和其他日志型存储系统一样Bitcask也有一个定期的merge操作。&lt;/p&gt;
&lt;p&gt;merge操作，即定期将所有older data file中的数据扫描一遍并生成新的data file（没有包括active data file 是因为它还在不停写入），这里的merge其实就是将对同一个key的多个操作以只保留最新一个的原则进行删除。每次merge后，新生成的数据文件就不再有冗余数据了。&lt;/p&gt;
&lt;h2&gt;2.基于hash表的索引数据&lt;/h2&gt;
&lt;p&gt;上面讲到的是数据文件，日志类型的数据文件会让我们的写入操作非常快（日志型的优势之一是将磁盘当作磁带，进行顺序读写的效率非常高，可以参见&lt;a href=&quot;http://lgone.com/html/y2010/801.html&quot;&gt;这里&lt;/a&gt;），而如果在这样的日志型数据上进行key值查找，那将是一件非常低效的事情。于是我们需要使用一些方法来提高查找效率。&lt;/p&gt;
&lt;p&gt;例如在Bigtable中，使用bloom-filter算法为每一个数据文件维护一个bloom-filter 的数据块，以此来判定一个值是否在某一个数据文件中。&lt;/p&gt;
&lt;p&gt;而在Bitcask模型中，我们使用了另一种方法，使用了一个基于hash表的索引数据结构。&lt;/p&gt;
&lt;p&gt;在Bitcask模型中，除了存储在磁盘上的数据文件，还有另外一块数据，那就是存储在内存中的hash表，hash表的作用是通过key值快速的定位到value的位置。hash表的结构大致如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.43.54.jpg&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-865&quot; title=&quot;屏幕快照 2010-12-26 下午01.43.54&quot; src=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.43.54.jpg&quot; alt=&quot;&quot; width=&quot;588&quot; height=&quot;405&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;hash表对应的这个结构中包括了三个用于定位数据value的信息，分别是文件id号(file_id)，value值在文件中的位置（value_pos）,value值的大小（value_sz），于是我们通过&lt;strong&gt;读取file_id对应文件的value_pos开始的value_sz个字节&lt;/strong&gt;，就得到了我们需要的value值。整个过程如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.50.03.jpg&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-866&quot; title=&quot;屏幕快照 2010-12-26 下午01.50.03&quot; src=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午01.50.03.jpg&quot; alt=&quot;&quot; width=&quot;482&quot; height=&quot;465&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;由于多了一个hash表的存在，我们的写操作就需要多更新一块内容，即这个hash表的对应关系。于是一个写操作就需要进行一次顺序的磁盘写入和一次内存操作。&lt;/p&gt;
&lt;h2&gt;3.有用的hint file&lt;/h2&gt;
&lt;p&gt;至此，Bitcask模型基本上已经讲述完成，而这一节讲到的hint file，则是一个有用的技巧，本人认为并不一定是Bitcask模型的必须特性。&lt;/p&gt;
&lt;p&gt;从上面我们可以知道，我们称其为索引的hash表，是存储在内存中的，虽然在各自的实现中可以做一些持久化的保证，但是Bitcask模型中并不对在断电或重启后的hash表数据不丢失做出保证。&lt;/p&gt;
&lt;p&gt;因此，如果我们不做额外的工作，那么我们启动时重建hash表时，就需要整个扫描一遍我们的数据文件，如果数据文件很大，这将是一个非常耗时的过程。因此Bitcask模型中包含了一个称作hint file的部分，目的在于提高重建hash表的速度。&lt;/p&gt;
&lt;p&gt;我们上面讲到在old data file进行merge操作时，会产生新的data file，而Bitcask模型实际还鼓励生成一个hint file，这个hint file中每一项的数据结构，与data file中的数据结构非常相似，不同的是他并不存储具体的value值，而是存储value的位置（像在hash表中的一样），其结构如下图：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午02.00.50.jpg&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-867&quot; title=&quot;屏幕快照 2010-12-26 下午02.00.50&quot; src=&quot;http://lgone.com/wp-content/uploads/2010/12/屏幕快照-2010-12-26-下午02.00.50.jpg&quot; alt=&quot;&quot; width=&quot;617&quot; height=&quot;491&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这样，在重建hash表时，就不需要再扫描所有data file文件，而仅仅需要将hint file中的数据一行行读取并重建即可。大大提高了利用数据文件重启数据库的速度。&lt;/p&gt;
&lt;h2&gt;结语：&lt;/h2&gt;
&lt;p&gt;以上就是Bitcask数据模型的所有内容，非常之精简易懂，但是记住，他只是一个模型，如果我们要实现一个基于Bitcask的存储系统的话，相信还有很多工作要做，还有很多细节可以优化。有兴趣的同学可以看一看Riak或豆瓣beansdb &lt;a href=&quot;http://beansdb.googlecode.com/files/beansdb-0.5.2.tar.gz&quot;&gt;0.5.2&lt;/a&gt; 版本的源码。&lt;/p&gt;
&lt;p&gt;参考文献：&lt;a href=&quot;http://downloads.basho.com/papers/bitcask-intro.pdf&quot;&gt;http://downloads.basho.com/papers/bitcask-intro.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;更多资料：&lt;a href=&quot;http://goo.gl/b7Bqg&quot;&gt;http://goo.gl/b7Bqg&lt;/a&gt;&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369458/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/861.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2010/861.html/feed</wfw:commentRss><slash:comments>14</slash:comments><description>Bitcask是一个日志型的基于hash表结构和key-value存储模型，我了解到他也就几天时间，但是其简洁有效的设计思路，让我的某种技术癖好得到了极大满足，于是酝酿出这篇东西。
Bitcask模型指导下的存储系统有Riak和豆瓣的beansdb新版本（beansdb新版本信息，参见这里），下面就简单的介绍一下Bitcask模型：
1.日志型的数据文件
何谓日志型？就是append only，所有写操作只追加而不修改老的数据，就像我们的各种服务器日志一样。在Bitcask模型中，数据文件以日志型只增不减的写入文件，而文件有一定的大小限制，当文件大小增加到相应的限制时，就会产生一个新的文件，老的文件将只读不写。在任意时间点，只有一个文件是可写的，在Bitcask模型中称其为active data file，而其他的已经达到限制大小的文件，称为older data file，如下图：

文件中的数据结构非常简单，是一条一条的数据写入操作，每一条数据的结构如下：

上面数据项分别为key，value，key的大小，value的大小，时间戳（应该是），以及对前面几项做的crc校验值。（数据删除操作也不会删除旧的条目，而是将value设定为一个特殊的值以作标示）
数据文件中就是连续一条条上面格式的数据，如下图：

好了，上面是日志型的数据文件，如果数据文件这样持续的存下去，肯定是会无限膨胀的，为了解决个问题，和其他日志型存储系统一样Bitcask也有一个定期的merge操作。
merge操作，即定期将所有older data file中的数据扫描一遍并生成新的data file（没有包括active data file 是因为它还在不停写入），这里的merge其实就是将对同一个key的多个操作以只保留最新一个的原则进行删除。每次merge后，新生成的数据文件就不再有冗余数据了。
2.基于hash表的索引数据
上面讲到的是数据文件，日志类型的数据文件会让我们的写入操作非常快（日志型的优势之一是将磁盘当作磁带，进行顺序读写的效率非常高，可以参见这里），而如果在这样的日志型数据上进行key值查找，那将是一件非常低效的事情。于是我们需要使用一些方法来提高查找效率。
例如在Bigtable中，使用bloom-filter算法为每一个数据文件维护一个bloom-filter 的数据块，以此来判定一个值是否在某一个数据文件中。
而在Bitcask模型中，我们使用了另一种方法，使用了一个基于hash表的索引数据结构。
在Bitcask模型中，除了存储在磁盘上的数据文件，还有另外一块数据，那就是存储在内存中的hash表，hash表的作用是通过key值快速的定位到value的位置。hash表的结构大致如下图所示：

hash表对应的这个结构中包括了三个用于定位数据value的信息，分别是文件id号(file_id)，value值在文件中的位置（value_pos）,value值的大小（value_sz），于是我们通过读取file_id对应文件的value_pos开始的value_sz个字节，就得到了我们需要的value值。整个过程如下图所示：

由于多了一个hash表的存在，我们的写操作就需要多更新一块内容，即这个hash表的对应关系。于是一个写操作就需要进行一次顺序的磁盘写入和一次内存操作。
3.有用的hint file
至此，Bitcask模型基本上已经讲述完成，而这一节讲到的hint file，则是一个有用的技巧，本人认为并不一定是Bitcask模型的必须特性。
从上面我们可以知道，我们称其为索引的hash表，是存储在内存中的，虽然在各自的实现中可以做一些持久化的保证，但是Bitcask模型中并不对在断电或重启后的hash表数据不丢失做出保证。
因此，如果我们不做额外的工作，那么我们启动时重建hash表时，就需要整个扫描一遍我们的数据文件，如果数据文件很大，这将是一个非常耗时的过程。因此Bitcask模型中包含了一个称作hint file的部分，目的在于提高重建hash表的速度。
我们上面讲到在old data file进行merge操作时，会产生新的data file，而Bitcask模型实际还鼓励生成一个hint file，这个hint file中每一项的数据结构，与data file中的数据结构非常相似，不同的是他并不存储具体的value值，而是存储value的位置（像在hash表中的一样），其结构如下图：

这样，在重建hash表时，就不需要再扫描所有data file文件，而仅仅需要将hint file中的数据一行行读取并重建即可。大大提高了利用数据文件重启数据库的速度。
结语：
以上就是Bitcask数据模型的所有内容，非常之精简易懂，但是记住，他只是一个模型，如果我们要实现一个基于Bitcask的存储系统的话，相信还有很多工作要做，还有很多细节可以优化。有兴趣的同学可以看一看Riak或豆瓣beansdb 0.5.2 版本的源码。
参考文献：http://downloads.basho.com/papers/bitcask-intro.pdf
更多资料：http://goo.gl/b7Bqg&lt;img src=&quot;http://www1.feedsky.com/t1/527369458/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/861.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>技术相关</category><category>riak</category><category>NoSQL</category><category>beansdb</category><category>bitcask</category><pubDate>Sun, 26 Dec 2010 14:14:03 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2010/861.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=861</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2010/861.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369458/5457389</fs:itemid></item><item><title>Scent of a Woman</title><link>http://lgone.com/html/y2010/854.html</link><content:encoded>&lt;p&gt;&lt;a href=&quot;http://lgone.com/wp-content/uploads/2010/11/s2962420.jpeg&quot;&gt;&lt;img class=&quot;size-full wp-image-855 alignleft&quot; title=&quot;s2962420&quot; src=&quot;http://lgone.com/wp-content/uploads/2010/11/s2962420.jpeg&quot; alt=&quot;&quot; width=&quot;297&quot; height=&quot;444&quot; /&gt;&lt;/a&gt;无论是中文译名《闻香识女人》还是英文名《Scent of a Woman》，都很难让人对电影的内容有正确的联想。当年若不是看到AL Pacino的名字，估计我也是不会去看的。&lt;/p&gt;
&lt;p&gt;前几天从“收藏”文件夹里调出来又和大家看了一次，想来大约是第4、5遍了吧，但心情还是在该澎湃的时候澎湃了。看完已经一点左右，趟在床上回味着情节，忽然想到好像写过一篇观后感之类的东西，于是开始翻遍留过脚印的各个角落，最后发现却只是一篇简单推荐，败兴而收，于是萌生写今天这篇东西的想法。&lt;/p&gt;
&lt;p&gt;当时同学问这是一部什么电影，我想说是关于人生的，关于选择的，但最后只说了一句，算是励志片吧。但在这里，我要说我从里面读到的人生的选择。&lt;/p&gt;
&lt;p&gt;故事一开始，查理近乎懦弱的形象与军官的强势形成对比，在两人渐渐熟悉，军官对查理渐生信任后，他打算用自己的处理哲学帮助查理摆脱当下的困境，他的人生哲学大概就是不折手段的达到目的，什么正直，什么道义，在他眼中都是狗屁。而查理的富二代同学的表现更让他的判断显得更为正确。&lt;/p&gt;
&lt;p&gt;然而查理并没有受他的处世方法指导，而是相信自己的正义哲学。军官一方面对查理的不谙世事而气愤，而同时又为查理身上这种正直的精神所打动，而这些，正是他所没有的。这些，是他认为正确，但是在人生的选择中没有勇气去选择的，从这一方面来看，他是羡慕甚至崇敬查理的。&lt;/p&gt;
&lt;p&gt;故事的转折出现在军官打算自杀的一幕，查理终于用自己的人生观影响了军官，军官认为自己已成废人，留在世上无用，但是查理用真诚打动了他，让他认识到活着，并不是如他过往的糜烂生活一样才叫有人生，相反，人生的意义不在于成就高低，而在于心底对自己的认同，人生的意义并不在于成就非凡的事业，而在于坚持自己的信念与热爱。就像军官自己说的一样“If you make mistakes, get all tangle up, just tango on”，就像他们旅途同的舞池探戈，街头飚车一样。&lt;/p&gt;
&lt;p&gt;故事的高潮当然是最后的激情讲演，讲演的内容非常精彩，而这里面，包含的不仅是为正义的辩护，还有军官的觉醒。&lt;/p&gt;
&lt;p&gt;煽情的讲演，在他过往的人生中，那是他再拿手不过的把戏，而在今天，这不再是一个把戏，而是真正的发自内心的呐喊，对自己人生的总结，失败的总结。&lt;/p&gt;
&lt;p&gt;他的人生看似辉煌，但是却只是用自己擅长的手段获取到那些浮华的果实，这并不是他想要的，他的内心是多么希望能做正确的选择，走正确的路，但是这条路实在太难了，于是他只能选择堕落，但他无时无刻不在否定自己的人生，于是他挥霍那些通过不正当手段得得来的名利，于是落得如此下场。&lt;/p&gt;
&lt;p&gt;然而查理的正直与真诚打动了他，在军官的人生中，他多么希望如果自己做出正确的选择时，能有人挺身而出能为自己辩护，维护自己，但这样的人太少了，所以他不敢听从内心的选择。而今天，他愿意做这样的一个人，也希望说服更多的人做这样的人，让正义与正直能够生存，而不是受到打压。&lt;/p&gt;
&lt;p&gt;他成功了，此时的查理，已化作年少时的军官，他已经沉浸在自己选择了正确道路并且一往无前的喜悦与成就感中。&lt;/p&gt;
&lt;p&gt;汽车沿着并不平坦的路远去，故事圆满落幕，而后面的路，依旧崎岖，是否还能有一个又有一个良心觉醒的人来维护这个社会的正义。是留给我们永远的思考。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369459/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/854.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2010/854.html/feed</wfw:commentRss><slash:comments>14</slash:comments><description>无论是中文译名《闻香识女人》还是英文名《Scent of a Woman》，都很难让人对电影的内容有正确的联想。当年若不是看到AL Pacino的名字，估计我也是不会去看的。
前几天从“收藏”文件夹里调出来又和大家看了一次，想来大约是第4、5遍了吧，但心情还是在该澎湃的时候澎湃了。看完已经一点左右，趟在床上回味着情节，忽然想到好像写过一篇观后感之类的东西，于是开始翻遍留过脚印的各个角落，最后发现却只是一篇简单推荐，败兴而收，于是萌生写今天这篇东西的想法。
当时同学问这是一部什么电影，我想说是关于人生的，关于选择的，但最后只说了一句，算是励志片吧。但在这里，我要说我从里面读到的人生的选择。
故事一开始，查理近乎懦弱的形象与军官的强势形成对比，在两人渐渐熟悉，军官对查理渐生信任后，他打算用自己的处理哲学帮助查理摆脱当下的困境，他的人生哲学大概就是不折手段的达到目的，什么正直，什么道义，在他眼中都是狗屁。而查理的富二代同学的表现更让他的判断显得更为正确。
然而查理并没有受他的处世方法指导，而是相信自己的正义哲学。军官一方面对查理的不谙世事而气愤，而同时又为查理身上这种正直的精神所打动，而这些，正是他所没有的。这些，是他认为正确，但是在人生的选择中没有勇气去选择的，从这一方面来看，他是羡慕甚至崇敬查理的。
故事的转折出现在军官打算自杀的一幕，查理终于用自己的人生观影响了军官，军官认为自己已成废人，留在世上无用，但是查理用真诚打动了他，让他认识到活着，并不是如他过往的糜烂生活一样才叫有人生，相反，人生的意义不在于成就高低，而在于心底对自己的认同，人生的意义并不在于成就非凡的事业，而在于坚持自己的信念与热爱。就像军官自己说的一样“If you make mistakes, get all tangle up, just tango on”，就像他们旅途同的舞池探戈，街头飚车一样。
故事的高潮当然是最后的激情讲演，讲演的内容非常精彩，而这里面，包含的不仅是为正义的辩护，还有军官的觉醒。
煽情的讲演，在他过往的人生中，那是他再拿手不过的把戏，而在今天，这不再是一个把戏，而是真正的发自内心的呐喊，对自己人生的总结，失败的总结。
他的人生看似辉煌，但是却只是用自己擅长的手段获取到那些浮华的果实，这并不是他想要的，他的内心是多么希望能做正确的选择，走正确的路，但是这条路实在太难了，于是他只能选择堕落，但他无时无刻不在否定自己的人生，于是他挥霍那些通过不正当手段得得来的名利，于是落得如此下场。
然而查理的正直与真诚打动了他，在军官的人生中，他多么希望如果自己做出正确的选择时，能有人挺身而出能为自己辩护，维护自己，但这样的人太少了，所以他不敢听从内心的选择。而今天，他愿意做这样的一个人，也希望说服更多的人做这样的人，让正义与正直能够生存，而不是受到打压。
他成功了，此时的查理，已化作年少时的军官，他已经沉浸在自己选择了正确道路并且一往无前的喜悦与成就感中。
汽车沿着并不平坦的路远去，故事圆满落幕，而后面的路，依旧崎岖，是否还能有一个又有一个良心觉醒的人来维护这个社会的正义。是留给我们永远的思考。&lt;img src=&quot;http://www1.feedsky.com/t1/527369459/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/854.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>艺术欣赏</category><pubDate>Wed, 24 Nov 2010 02:39:10 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2010/854.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=854</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2010/854.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369459/5457389</fs:itemid></item><item><title>城门不开</title><link>http://lgone.com/html/y2010/849.html</link><content:encoded>&lt;div&gt;
&lt;p&gt;这里有一座城。&lt;/p&gt;
&lt;p&gt;这里原本没有城，但是自从他们来到后，建立了这座城。他们是他和她。他们的名字没人知道，大家知道的是城主和夫人。说这是他们的城，没有人会有异议。外面的世界如何，城主和夫人从不关心，因为这座城有一个神秘之处，任何外界的事物，无城主应允，都无法进入，但任何城中的事物，只要出城，再也无法回来。当然，除了城主和夫人，他们来去自如。&lt;/p&gt;
&lt;p&gt;因此外面世界的纷争从来不会波及到这里。这里的一切都很简单，这里的人都很满足，也没有任何人想要出去。世人无比艳羡，当然也不乏觊觎者，他们时刻想毁灭这座完美之城。&lt;/p&gt;
&lt;p&gt;那天是城主大寿。虽然没有人知道城主有多少岁，但大家记得八月初八，这是城主的生辰，是夫人的生辰，是这座城的生辰。于是每年八月八成为城里最盛大的节日。这一天，会大开城门，向世人展示城中的富足与和谐，以期消解世间的无畏纷争。&lt;/p&gt;
&lt;p&gt;而就在八月初八，夫人被劫！&lt;/p&gt;
&lt;p&gt;这天城门大开，门外出现一只三耳玉兔。夫人见到非常喜欢，于是出门追逐，不曾想这是一个埋伏，于是夫人被觊觎者劫。觊觎者出言五日内城主自毁高墙，否则夫人将永不得还。城主最后选择了保住古城，忍痛割爱。&lt;/p&gt;
&lt;p&gt;夫人被劫后，城的神秘之处只剩下一半，里面的人依然出不去，但外面的事物，无须城主应允，只要城门打开，便可进入。&lt;/p&gt;
&lt;p&gt;于是城主下令：城门不开！&lt;/p&gt;
&lt;p&gt;城门不开，又过了多少个春秋，又是一年八月初八，城门外居然出现夫人呼唤，原来夫人已经从觊觎者那里逃脱。三天三夜不停逃奔到城门口。身后不到一里，便是追兵。看门人求城主下令开门，城主只道：城门不开！&lt;/p&gt;
&lt;p&gt;城门不开，过了多少春秋。&lt;/p&gt;
&lt;p&gt;城门不开，还有多少春秋。&lt;/p&gt;
&lt;/div&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369460/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/849.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2010/849.html/feed</wfw:commentRss><slash:comments>8</slash:comments><description>这里有一座城。
这里原本没有城，但是自从他们来到后，建立了这座城。他们是他和她。他们的名字没人知道，大家知道的是城主和夫人。说这是他们的城，没有人会有异议。外面的世界如何，城主和夫人从不关心，因为这座城有一个神秘之处，任何外界的事物，无城主应允，都无法进入，但任何城中的事物，只要出城，再也无法回来。当然，除了城主和夫人，他们来去自如。
因此外面世界的纷争从来不会波及到这里。这里的一切都很简单，这里的人都很满足，也没有任何人想要出去。世人无比艳羡，当然也不乏觊觎者，他们时刻想毁灭这座完美之城。
那天是城主大寿。虽然没有人知道城主有多少岁，但大家记得八月初八，这是城主的生辰，是夫人的生辰，是这座城的生辰。于是每年八月八成为城里最盛大的节日。这一天，会大开城门，向世人展示城中的富足与和谐，以期消解世间的无畏纷争。
而就在八月初八，夫人被劫！
这天城门大开，门外出现一只三耳玉兔。夫人见到非常喜欢，于是出门追逐，不曾想这是一个埋伏，于是夫人被觊觎者劫。觊觎者出言五日内城主自毁高墙，否则夫人将永不得还。城主最后选择了保住古城，忍痛割爱。
夫人被劫后，城的神秘之处只剩下一半，里面的人依然出不去，但外面的事物，无须城主应允，只要城门打开，便可进入。
于是城主下令：城门不开！
城门不开，又过了多少个春秋，又是一年八月初八，城门外居然出现夫人呼唤，原来夫人已经从觊觎者那里逃脱。三天三夜不停逃奔到城门口。身后不到一里，便是追兵。看门人求城主下令开门，城主只道：城门不开！
城门不开，过了多少春秋。
城门不开，还有多少春秋。&lt;img src=&quot;http://www1.feedsky.com/t1/527369460/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/849.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>舞文弄墨</category><category>城门不开</category><pubDate>Sat, 20 Nov 2010 23:24:26 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2010/849.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=849</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2010/849.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369460/5457389</fs:itemid></item><item><title>auto-sharding 无用论：auto-sharding vs. manual-sharding</title><link>http://lgone.com/html/y2010/835.html</link><content:encoded>&lt;h2&gt;一、美好的蓝图&lt;/h2&gt;
&lt;p&gt;刚接触MongoDB的时候，看到它的auto-sharding功能图，配合上replica sets简直有一种一统世界的感觉。既下图：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://lgone.com/wp-content/uploads/2010/11/sharding.png&quot;&gt;&lt;img class=&quot;size-full wp-image-836 alignnone&quot; title=&quot;auto-sharding&quot; src=&quot;http://lgone.com/wp-content/uploads/2010/11/sharding.png&quot; alt=&quot;mongodb auto-sharding&quot; width=&quot;572&quot; height=&quot;333&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;图中路由机mongos可以有多台，config机器可以多台配置成主从或者replica sets，sharding的每个结点是三台mongod组成的replica sets。高可用性，无限扩展性尽在眼前。看似宏伟壮观。&lt;/p&gt;
&lt;p&gt;然而当我真的打算要用auto-sharding功能的时候，才发现此设计根本不适用，下面谈一点我个人的看法。&lt;/p&gt;
&lt;p&gt;Sharding一词的翻译是分片，在这里的意思是将数据进行水平切分。最简单的例子就是我们在数据库设计中的分库分表，在Memcached缓存中的多结点hash存储。而MongoDB的auto-sharding功能重在一个auto（自动化分片）。官方称，使用这一功能，你只需要指定数据分片依赖的某个字段值，既可在不关心具体结点数量的情况下存储你的数据，数据会自动平均分配到后端结点，在增加结点时，数据又会自动的进行迁移，对整个系统进行负载平衡。相比我们传统的分库分表，它是auto（自动）的，而传统方法是manual（手动）的。&lt;/p&gt;
&lt;h2&gt;二、Foursquare宕机事件&lt;/h2&gt;
&lt;p&gt;看罢上面的描述，好像没有任何可以挑剔的理由，但事实并不如描绘的那样美好，从&lt;a href=&quot;http://www.dbanotes.net/arch/foursquare_outage.html&quot;&gt;Foursquare长达11小时的宕机&lt;/a&gt;事件分析中，我们可以看到如下一些问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;auto-sharding算法导致数据分配不均&lt;/strong&gt;：事实上数据并不会那么平均的在各个机器间平均分配，由此而造成的短板效应是无法忍受的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据迁移代价过大&lt;/strong&gt;：在增加新的sharding结点时，数据确实会自动的进行迁移，而这种迁移对于原来线上服务的结点，服务上是有影响的。所以我们最好在存储瓶颈到来很早之前就开始做这个迁移，而这样做，和我们手动进行分片，预先估算好数据量相比，优势不再明显。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据迁移造成碎片&lt;/strong&gt;：同样是Foursquare的失败经验，在数据迁移时，我们需要迁移的数据通常并不会在磁盘上进行连续存储（除非你的hash条件是天然的insert时间戳）。而我们又知道MongoDB采用的是磁盘空间预分配的机制，于是在数据迁移时，可能并不会减小磁盘占用空间，反而会使得磁盘碎片化。更甚的是由于MongoDB在是采用mmap提速数据访问，磁盘空洞并不会减小mmap的大小，反而导致内存也碎片化。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而以上一些问题，在我们预先规划好存储量的manual-sharding上，是不存在的。&lt;/p&gt;
&lt;h2&gt;三、冷数据不冷&lt;/h2&gt;
&lt;p&gt;我认为在存储规划上，可以简单的从两方面来评估，&lt;span style=&quot;color: #ff0000;&quot;&gt;一是磁盘占用，也就是总数据量，二是内存占用，也就是热数据量。&lt;/span&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;&lt;strong&gt;热数据在全部数据的中所占的比例，通常会越来越小。&lt;/strong&gt;&lt;/span&gt;造成这一原因的是老数据的访问量下降。我们考虑这样一种情况，如果你的应用用户量和每日新增数据量已经相当稳定，我们以最近三天的数据为热数据进行评估，那么总数据量会随着时间的增长而增长，而热数据量永远是三天的量。我们这里举的是一个极端的例子，可能你的用户量会每天增长，但是热数据量在全部数据中占的比例，通常是越来越少的，这个事实不容反驳。&lt;/p&gt;
&lt;p&gt;上面说了这么多，下面我们来看我们的问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;auto-sharding数据量变化情况&lt;/strong&gt;：和我们上面说的情况一样，auto-sharding机群会通过增加结点数来扩展集群的存储能力。结点数和数据量成线性的正比关系。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;auto-sharding的热数据比例&lt;/strong&gt;：auto-sharding一旦一个结点配置完成，那么这些机器的内存与磁盘空间比就定了。于是整个集群的热数据比例占所有数据的比例也是一定的。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而对比我们上面的说法，热数据占比例在所有数据中是会越来越小的。于是我们可以认为，auto-sharding中的老结点浪费掉了大量的内存和CPU资源。&lt;/p&gt;
&lt;p&gt;而如果我们是人为手动进行分片，我们完全可以自己控制数据的存储，自行设定自己的LRU或者TTL机制，将老数据，冷数据进行存储转移，不再占用高性能机器的各项机能。从而让我们公司花大钱买来的机器能够物尽其用。&lt;/p&gt;
&lt;p&gt;好了，就说这一些，理解不一定正确，欢迎探讨，拍砖。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369461/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/835.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2010/835.html/feed</wfw:commentRss><slash:comments>9</slash:comments><description>一、美好的蓝图
刚接触MongoDB的时候，看到它的auto-sharding功能图，配合上replica sets简直有一种一统世界的感觉。既下图：

图中路由机mongos可以有多台，config机器可以多台配置成主从或者replica sets，sharding的每个结点是三台mongod组成的replica sets。高可用性，无限扩展性尽在眼前。看似宏伟壮观。
然而当我真的打算要用auto-sharding功能的时候，才发现此设计根本不适用，下面谈一点我个人的看法。
Sharding一词的翻译是分片，在这里的意思是将数据进行水平切分。最简单的例子就是我们在数据库设计中的分库分表，在Memcached缓存中的多结点hash存储。而MongoDB的auto-sharding功能重在一个auto（自动化分片）。官方称，使用这一功能，你只需要指定数据分片依赖的某个字段值，既可在不关心具体结点数量的情况下存储你的数据，数据会自动平均分配到后端结点，在增加结点时，数据又会自动的进行迁移，对整个系统进行负载平衡。相比我们传统的分库分表，它是auto（自动）的，而传统方法是manual（手动）的。
二、Foursquare宕机事件
看罢上面的描述，好像没有任何可以挑剔的理由，但事实并不如描绘的那样美好，从Foursquare长达11小时的宕机事件分析中，我们可以看到如下一些问题：

auto-sharding算法导致数据分配不均：事实上数据并不会那么平均的在各个机器间平均分配，由此而造成的短板效应是无法忍受的。
数据迁移代价过大：在增加新的sharding结点时，数据确实会自动的进行迁移，而这种迁移对于原来线上服务的结点，服务上是有影响的。所以我们最好在存储瓶颈到来很早之前就开始做这个迁移，而这样做，和我们手动进行分片，预先估算好数据量相比，优势不再明显。
数据迁移造成碎片：同样是Foursquare的失败经验，在数据迁移时，我们需要迁移的数据通常并不会在磁盘上进行连续存储（除非你的hash条件是天然的insert时间戳）。而我们又知道MongoDB采用的是磁盘空间预分配的机制，于是在数据迁移时，可能并不会减小磁盘占用空间，反而会使得磁盘碎片化。更甚的是由于MongoDB在是采用mmap提速数据访问，磁盘空洞并不会减小mmap的大小，反而导致内存也碎片化。

而以上一些问题，在我们预先规划好存储量的manual-sharding上，是不存在的。
三、冷数据不冷
我认为在存储规划上，可以简单的从两方面来评估，一是磁盘占用，也就是总数据量，二是内存占用，也就是热数据量。热数据在全部数据的中所占的比例，通常会越来越小。造成这一原因的是老数据的访问量下降。我们考虑这样一种情况，如果你的应用用户量和每日新增数据量已经相当稳定，我们以最近三天的数据为热数据进行评估，那么总数据量会随着时间的增长而增长，而热数据量永远是三天的量。我们这里举的是一个极端的例子，可能你的用户量会每天增长，但是热数据量在全部数据中占的比例，通常是越来越少的，这个事实不容反驳。
上面说了这么多，下面我们来看我们的问题。

auto-sharding数据量变化情况：和我们上面说的情况一样，auto-sharding机群会通过增加结点数来扩展集群的存储能力。结点数和数据量成线性的正比关系。
auto-sharding的热数据比例：auto-sharding一旦一个结点配置完成，那么这些机器的内存与磁盘空间比就定了。于是整个集群的热数据比例占所有数据的比例也是一定的。

而对比我们上面的说法，热数据占比例在所有数据中是会越来越小的。于是我们可以认为，auto-sharding中的老结点浪费掉了大量的内存和CPU资源。
而如果我们是人为手动进行分片，我们完全可以自己控制数据的存储，自行设定自己的LRU或者TTL机制，将老数据，冷数据进行存储转移，不再占用高性能机器的各项机能。从而让我们公司花大钱买来的机器能够物尽其用。
好了，就说这一些，理解不一定正确，欢迎探讨，拍砖。&lt;img src=&quot;http://www1.feedsky.com/t1/527369461/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/835.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>MongoDB</category><category>NoSQL</category><category>存储</category><category>auto-sharding</category><category>manual-sharding</category><pubDate>Tue, 02 Nov 2010 21:40:47 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2010/835.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=835</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2010/835.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369461/5457389</fs:itemid></item><item><title>关于NoSQL的思考-为什么我们要优化存储的写性能？</title><link>http://lgone.com/html/y2010/823.html</link><content:encoded>&lt;p&gt;在NoSQL的许多产品中，我们通过benchmark可以看到的都是写性能极度提升，而读性能并没有太大的涨幅甚至相对传统RDBMS还有下降。比如Cassandra，MongoDB这两个NoSQL的杰出代表。究其原因，我们可能会想到是因为当前UGC模式已经发展到白热化，用户产生内容导致读写比已经接近或者说小于1：1。&lt;/p&gt;
&lt;p&gt;但是我认为这绝不是个中真实原因。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;1 缓存导致存储的raw read效率不再重要&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;真实原因是我们对读的优化已经做得足够多了，数据存储我们使用Memcached，TokyoTyrant/TokyoCabinet等缓存存储，页面及文件缓存我们使用squid，nginx proxy_cache等存储，都可以达到非常好的读缓存效果，如果数据即时性要求不高，或者说缓存设计合理（读写皆缓存），缓存命中率会足够的高，因此我们无需再过分优化底层存储的raw read效率。&lt;/p&gt;
&lt;p&gt;试想缓存层如果有高达99％以上的命中率，那么相对于raw read设备，我们的亿级的数据读取请求就轻松的变成百万级请求，上千并发轻松变成数十并发。当然，这需要我们的缓存层足够靠谱。比如nginx proxy_cache 可以多较多，这时候宕掉一台不至于使全部读请求穿透到底层存储。至于多了之后purge等操作如何全面的执行，不在本文讨论之列。&lt;/p&gt;
&lt;p&gt;综上，raw read效率不需要再提升，因为其需求已经被缓存层大量取代。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;2 无法取代的raw write功能&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;看到缓存减轻raw read的工作量，我们可以在想是否有方法可以减轻raw write的工作量。答案是不可以的。如果您认为可以。可以留言探讨。既然raw write的工作量是不可取代的，那么我们大概可以有两种方法提升写操作的性能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;3.1 sharding&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通过对数据的分区，我们可以将数据进行分布式的存储，于是每个结点只会分配到一部分的raw write请求。这样相当于公司员工效率不变，多招了人。但由于结点的增多，其中有结点出问题的效率也大大增加。于是我们不得不做一些replication操作来提供HA方案。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;3.2 提升raw write效率&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如上面的举例，我们只能选择提升raw write效率来实现总体（包括cache层）更好的读写效率。这里通常使用的方法就是将随机的写操作在内存中进行序列化，并在一定量后进行顺序的flush到磁盘操作。所谓将内存当成硬盘，将硬盘当作磁带就是这个意思。（可参见我更早的一篇文章：《&lt;a href=&quot;http://lgone.com/html/y2010/801.html&quot;&gt;NoSQL理论之-内存是新的硬盘，硬盘是新的磁带&lt;/a&gt;》）所以我们看到前面说到的很多NoSQL产品着重对写操作进行了优化，而对读性能提升并不明显，甚至不惜以更慢的读作为提升写操作性能的代价。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;4 总结&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;由于读性能可以通过设置合理的缓存策略来减少raw read操作的数量。因此不仅对读写比不大的情形需要着重进行写操作的优化，对读写比大的情况下，仍旧需要优化写性能而非读性能。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369462/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/823.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2010/823.html/feed</wfw:commentRss><slash:comments>5</slash:comments><description>在NoSQL的许多产品中，我们通过benchmark可以看到的都是写性能极度提升，而读性能并没有太大的涨幅甚至相对传统RDBMS还有下降。比如Cassandra，MongoDB这两个NoSQL的杰出代表。究其原因，我们可能会想到是因为当前UGC模式已经发展到白热化，用户产生内容导致读写比已经接近或者说小于1：1。
但是我认为这绝不是个中真实原因。
1 缓存导致存储的raw read效率不再重要
真实原因是我们对读的优化已经做得足够多了，数据存储我们使用Memcached，TokyoTyrant/TokyoCabinet等缓存存储，页面及文件缓存我们使用squid，nginx proxy_cache等存储，都可以达到非常好的读缓存效果，如果数据即时性要求不高，或者说缓存设计合理（读写皆缓存），缓存命中率会足够的高，因此我们无需再过分优化底层存储的raw read效率。
试想缓存层如果有高达99％以上的命中率，那么相对于raw read设备，我们的亿级的数据读取请求就轻松的变成百万级请求，上千并发轻松变成数十并发。当然，这需要我们的缓存层足够靠谱。比如nginx proxy_cache 可以多较多，这时候宕掉一台不至于使全部读请求穿透到底层存储。至于多了之后purge等操作如何全面的执行，不在本文讨论之列。
综上，raw read效率不需要再提升，因为其需求已经被缓存层大量取代。
2 无法取代的raw write功能
看到缓存减轻raw read的工作量，我们可以在想是否有方法可以减轻raw write的工作量。答案是不可以的。如果您认为可以。可以留言探讨。既然raw write的工作量是不可取代的，那么我们大概可以有两种方法提升写操作的性能。
3.1 sharding
通过对数据的分区，我们可以将数据进行分布式的存储，于是每个结点只会分配到一部分的raw write请求。这样相当于公司员工效率不变，多招了人。但由于结点的增多，其中有结点出问题的效率也大大增加。于是我们不得不做一些replication操作来提供HA方案。
3.2 提升raw write效率
如上面的举例，我们只能选择提升raw write效率来实现总体（包括cache层）更好的读写效率。这里通常使用的方法就是将随机的写操作在内存中进行序列化，并在一定量后进行顺序的flush到磁盘操作。所谓将内存当成硬盘，将硬盘当作磁带就是这个意思。（可参见我更早的一篇文章：《NoSQL理论之-内存是新的硬盘，硬盘是新的磁带》）所以我们看到前面说到的很多NoSQL产品着重对写操作进行了优化，而对读性能提升并不明显，甚至不惜以更慢的读作为提升写操作性能的代价。
4 总结
由于读性能可以通过设置合理的缓存策略来减少raw read操作的数量。因此不仅对读写比不大的情形需要着重进行写操作的优化，对读写比大的情况下，仍旧需要优化写性能而非读性能。&lt;img src=&quot;http://www1.feedsky.com/t1/527369462/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/823.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>读性能</category><category>写性能</category><category>架构</category><category>NoSQL</category><category>存储</category><pubDate>Wed, 13 Oct 2010 18:07:01 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2010/823.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=823</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2010/823.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369462/5457389</fs:itemid></item><item><title>MongoDB GridFS 数据读取效率 benchmark</title><link>http://lgone.com/html/y2010/815.html</link><content:encoded>&lt;p&gt;GridFS 是 MongoDB 下的一个子模块，利用这一模块，可以实现使用MongoDB存储文件，MongoDB 二进制包的bin目录下的mongofile 命令即可模拟实现该功能。&lt;/p&gt;
&lt;p&gt;根据反馈，本文给一些朋友造成了一些误解，特声明如下：此测试非本人进行，数据来源于下面链接中博客，有对测试过程中的问题有更好建议的朋友可以在留言中回复，欢迎各种讨论。&lt;/p&gt;
&lt;p&gt;数据来源：&lt;a href=&quot;http://www.coffeepowered.net/2010/02/17/serving-files-out-of-gridfs/&quot;&gt;http://www.coffeepowered.net/2010/02/17/serving-files-out-of-gridfs/&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://www.coffeepowered.net/2010/02/24/serving-files-out-of-gridfs-part-2/&quot;&gt;http://www.coffeepowered.net/2010/02/24/serving-files-out-of-gridfs-part-2/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;另外一份benchmark数据：&lt;a href=&quot;http://www.ypass.net/solaris/nginx-gridfs-benchmarks/rawresults.php&quot;&gt;http://www.ypass.net/solaris/nginx-gridfs-benchmarks/rawresults.php&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;目前使用GridFS有以下三种方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用脚本读取，这时文件数据会全部读取到脚本中，再调用脚本客户端的输出功能输出给页面。毫无疑问，这种使用方式是效率非常低下的。&lt;/li&gt;
&lt;li&gt;使用&lt;a href=&quot;http://github.com/mikejs/gridfs-fuse/&quot;&gt;gridfs-fuse&lt;/a&gt;使GridFS中存的数据可以通过标准磁盘IO方式进行访问，可以说是真正实现了一个文件系统。&lt;/li&gt;
&lt;li&gt;使用&lt;a href=&quot;http://github.com/mdirolf/nginx-gridfs&quot;&gt;nginx-gridfs&lt;/a&gt;利用Nginx直接读取gridfs中的数据进行发送，这个类似于Nginx的sendfile机制。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;文章作者就这几种方式做了测试，得出以下的benchmark数据。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;1.1 通过Apache直接读取普通文件&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;wp_codebox&quot;&gt;&lt;table&gt;&lt;tr id=&quot;p8156&quot;&gt;&lt;td class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot; id=&quot;p815code6&quot;&gt;&lt;pre class=&quot;shell&quot; style=&quot;font-family:monospace;&quot;&gt;[chris@polaris conf]# ab -n 50000 -c 10 http://advice/images/embed/alliance-60.png
&amp;nbsp;
Server Software:        Apache/2.2.13
Server Hostname:        advice
Server Port:            80
&amp;nbsp;
Document Path:          /images/embed/normal_alliance-60.png
Document Length:        31596 bytes
&amp;nbsp;
Concurrency Level:      10
Time taken for tests:   1.904 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Total transferred:      159463760 bytes
HTML transferred:       158043192 bytes
Requests per second:    2625.37 [#/sec] (mean)
Time per request:       3.809 [ms] (mean)
Time per request:       0.381 [ms] (mean, across all concurrent requests)
Transfer rate:          81767.87 [Kbytes/sec] received
&amp;nbsp;
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   0.4      1       4
Processing:     1    3   0.5      3       6
Waiting:        0    1   0.4      1       4
Total:          2    4   0.4      4       8
&amp;nbsp;
Percentage of the requests served within a certain time (ms)
  50%      4
  66%      4
  75%      4
  80%      4
  90%      4
  95%      4
  98%      5
  99%      5
 100%      8 (longest request)&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;结果：每秒&lt;span style=&quot;color: #008000;&quot;&gt;&lt;strong&gt;2625&lt;/strong&gt;&lt;/span&gt;次的请求处理，&lt;span style=&quot;color: #008000;&quot;&gt;&lt;strong&gt;81.77M&lt;/strong&gt;&lt;/span&gt;的吞吐率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;1.2 通过Nginx直接读取普通文件&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;wp_codebox&quot;&gt;&lt;table&gt;&lt;tr id=&quot;p8157&quot;&gt;&lt;td class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot; id=&quot;p815code7&quot;&gt;&lt;pre class=&quot;shell&quot; style=&quot;font-family:monospace;&quot;&gt;[chris@polaris conf]# ab -n 50000 -c 10 http://advice:81/images/embed/normal_alliance-60.png
&amp;nbsp;
Server Software:        nginx/0.8.33
Server Hostname:        advice
Server Port:            81
&amp;nbsp;
Document Path:          /images/embed/normal_alliance-60.png
Document Length:        31596 bytes
&amp;nbsp;
Concurrency Level:      10
Time taken for tests:   7.623 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Total transferred:      1590513618 bytes
HTML transferred:       1579863192 bytes
Requests per second:    6559.31 [#/sec] (mean)
Time per request:       1.525 [ms] (mean)
Time per request:       0.152 [ms] (mean, across all concurrent requests)
Transfer rate:          203763.10 [Kbytes/sec] received
&amp;nbsp;
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       9
Processing:     1    1   0.4      1      11
Waiting:        0    0   0.1      0       9
Total:          1    1   0.5      1      12
&amp;nbsp;
Percentage of the requests served within a certain time (ms)
  50%      1
  66%      1
  75%      1
  80%      2
  90%      2
  95%      2
  98%      3
  99%      3
 100%     12 (longest request)&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;结果：每秒&lt;strong&gt;&lt;span style=&quot;color: #008000;&quot;&gt;6559&lt;/span&gt;&lt;/strong&gt;次的请求处理，&lt;span style=&quot;color: #008000;&quot;&gt;&lt;strong&gt;203.763M&lt;/strong&gt;&lt;/span&gt;的吞吐率。&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;&lt;strong&gt;2.通过gridfs-fuse读取gridfs中的数据&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;

&lt;div class=&quot;wp_codebox&quot;&gt;&lt;table&gt;&lt;tr id=&quot;p8158&quot;&gt;&lt;td class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot; id=&quot;p815code8&quot;&gt;&lt;pre class=&quot;shell&quot; style=&quot;font-family:monospace;&quot;&gt;[chris@polaris gridfs-fuse]# ab -n 5000 -c 25 http://advice:81/images/gfs/uploads/user/avatar/4b8347a698db740b30000057/thumb_adrine-big.png
&amp;nbsp;
Server Software:        nginx/0.8.33
Server Hostname:        advice
Server Port:            81
&amp;nbsp;
Document Path:          /images/gfs/uploads/user/avatar/4b8347a698db740b30000057/thumb_adrine-big.png
Document Length:        14332 bytes
&amp;nbsp;
Concurrency Level:      25
Time taken for tests:   5.029 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Total transferred:      72725000 bytes
HTML transferred:       71660000 bytes
Requests per second:    994.22 [#/sec] (mean)
Time per request:       25.145 [ms] (mean)
Time per request:       1.006 [ms] (mean, across all concurrent requests)
Transfer rate:          14121.93 [Kbytes/sec] received
&amp;nbsp;
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:    16   25   1.4     25      52
Waiting:        2   24   1.4     24      52
Total:         17   25   1.4     25      53
&amp;nbsp;
Percentage of the requests served within a certain time (ms)
  50%     25
  66%     25
  75%     25
  80%     25
  90%     25
  95%     26
  98%     27
  99%     32
 100%     53 (longest request)&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;结果：每秒&lt;strong&gt;&lt;span style=&quot;color: #339966;&quot;&gt;994&lt;/span&gt;&lt;/strong&gt;次的请求处理，&lt;span style=&quot;color: #339966;&quot;&gt;&lt;strong&gt;14.121M&lt;/strong&gt;&lt;/span&gt;的吞吐率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;3 通过nginx-gridfs模块读取gridfs中的数据&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;wp_codebox&quot;&gt;&lt;table&gt;&lt;tr id=&quot;p8159&quot;&gt;&lt;td class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot; id=&quot;p815code9&quot;&gt;&lt;pre class=&quot;shell&quot; style=&quot;font-family:monospace;&quot;&gt;[chris@polaris conf]# ab -n 5000 -c 10 http://advice:81/images/gfs/uploads/user/avatar/4b7b2c0e98db7475fc000003/normal_alliance-60.png
&amp;nbsp;
Server Software:        nginx/0.8.33
Server Hostname:        advice
Server Port:            81
&amp;nbsp;
Document Path:          /images/gfs/uploads/user/avatar/4b7b2c0e98db7475fc000003/normal_alliance-60.png
Document Length:        31596 bytes
&amp;nbsp;
Concurrency Level:      10
Time taken for tests:   4.613 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Total transferred:      158580000 bytes
HTML transferred:       157980000 bytes
Requests per second:    1083.88 [#/sec] (mean)
Time per request:       9.226 [ms] (mean)
Time per request:       0.923 [ms] (mean, across all concurrent requests)
Transfer rate:          33570.65 [Kbytes/sec] received
&amp;nbsp;
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     1    9   4.7      9     103
Waiting:        1    9   4.7      9     102
Total:          2    9   4.7      9     103
&amp;nbsp;
Percentage of the requests served within a certain time (ms)
  50%      9
  66%      9
  75%      9
  80%      9
  90%      9
  95%      9
  98%      9
  99%     11
 100%    103 (longest request)&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;结果：每秒&lt;strong&gt;&lt;span style=&quot;color: #008000;&quot;&gt;1083&lt;/span&gt;&lt;/strong&gt;次的请求处理，&lt;strong&gt;&lt;span style=&quot;color: #008000;&quot;&gt;33.57M&lt;/span&gt;&lt;/strong&gt;吞吐率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;4 通过Rails客户端读取数据再发送&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;wp_codebox&quot;&gt;&lt;table&gt;&lt;tr id=&quot;p81510&quot;&gt;&lt;td class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot; id=&quot;p815code10&quot;&gt;&lt;pre class=&quot;shell&quot; style=&quot;font-family:monospace;&quot;&gt;[chris@polaris nginx-gridfs]$ ab -n 250 -c 4  http://advice/images/gfs/uploads/user/avatar/4b7b2c0e98db7475fc000003/normal_alliance-60.png
&amp;nbsp;
Server Software:        Apache/2.2.13
Server Hostname:        advice
Server Port:            80
&amp;nbsp;
Document Path:          /images/gfs/uploads/user/avatar/4b7b2c0e98db7475fc000003/normal_alliance-60.png
Document Length:        31596 bytes
&amp;nbsp;
Concurrency Level:      4
Time taken for tests:   4.646 seconds
Complete requests:      250
Failed requests:        0
Write errors:           0
Total transferred:      7960000 bytes
HTML transferred:       7899000 bytes
Requests per second:    53.81 [#/sec] (mean)
Time per request:       74.338 [ms] (mean)
Time per request:       18.585 [ms] (mean, across all concurrent requests)
Transfer rate:          1673.10 [Kbytes/sec] received
&amp;nbsp;
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:    15   74  75.6     34     287
Waiting:        0   72  75.8     30     276
Total:         15   74  75.6     34     288
&amp;nbsp;
Percentage of the requests served within a certain time (ms)
  50%     34
  66%     39
  75%    139
  80%    192
  90%    201
  95%    210
  98%    239
  99%    245
 100%    288 (longest request)&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;结果：每秒&lt;strong&gt;&lt;span style=&quot;color: #008000;&quot;&gt;53&lt;/span&gt;&lt;/strong&gt;的请求处理，&lt;span style=&quot;color: #008000;&quot;&gt;&lt;strong&gt;1.673M&lt;/strong&gt;&lt;/span&gt;的吞吐率（效率确实不可能高）&lt;/p&gt;
&lt;p&gt;结论：&lt;/p&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Requests/second&lt;/th&gt;
&lt;th&gt;% Apache FS&lt;/th&gt;
&lt;th&gt;% Nginx FS&lt;/th&gt;
&lt;th&gt;%gridfs-fuse&lt;/th&gt;
&lt;th&gt;% Nginx GridFS&lt;/th&gt;
&lt;th&gt;% Apache Ruby&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filesystem via Apache&lt;/td&gt;
&lt;td&gt;2625.37&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;40.03%&lt;/td&gt;
&lt;td&gt;264.06%&lt;/td&gt;
&lt;td&gt;242.22%&lt;/td&gt;
&lt;td&gt;4,878.96%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filesystem via Nginx&lt;/td&gt;
&lt;td&gt;6559.31&lt;/td&gt;
&lt;td&gt;249.84%&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;657.74%&lt;/td&gt;
&lt;td&gt;605.17%&lt;/td&gt;
&lt;td&gt;12,189.76%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nginx via gridfs-fuse&lt;/td&gt;
&lt;td&gt;994.22&lt;/td&gt;
&lt;td&gt;37.86%&lt;/td&gt;
&lt;td&gt;15.15%&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;91.74%&lt;/td&gt;
&lt;td&gt;1847.65%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GridFS via nginx module&lt;/td&gt;
&lt;td&gt;1083.88&lt;/td&gt;
&lt;td&gt;41.28%&lt;/td&gt;
&lt;td&gt;16.52%&lt;/td&gt;
&lt;td&gt;109.02%&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;2014.27%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rails metal handler via Passenger&lt;/td&gt;
&lt;td&gt;53.81&lt;/td&gt;
&lt;td&gt;2.05%&lt;/td&gt;
&lt;td&gt;0.82%&lt;/td&gt;
&lt;td&gt;5.41%&lt;/td&gt;
&lt;td&gt;4.96%&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369463/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/815.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2010/815.html/feed</wfw:commentRss><slash:comments>6</slash:comments><description>GridFS 是 MongoDB 下的一个子模块，利用这一模块，可以实现使用MongoDB存储文件，MongoDB 二进制包的bin目录下的mongofile 命令即可模拟实现该功能。
根据反馈，本文给一些朋友造成了一些误解，特声明如下：此测试非本人进行，数据来源于下面链接中博客，有对测试过程中的问题有更好建议的朋友可以在留言中回复，欢迎各种讨论。
数据来源：http://www.coffeepowered.net/2010/02/17/serving-files-out-of-gridfs/
http://www.coffeepowered.net/2010/02/24/serving-files-out-of-gridfs-part-2/
另外一份benchmark数据：http://www.ypass.net/solaris/nginx-gridfs-benchmarks/rawresults.php
目前使用GridFS有以下三种方式：

使用脚本读取，这时文件数据会全部读取到脚本中，再调用脚本客户端的输出功能输出给页面。毫无疑问，这种使用方式是效率非常低下的。
使用gridfs-fuse使GridFS中存的数据可以通过标准磁盘IO方式进行访问，可以说是真正实现了一个文件系统。
使用nginx-gridfs利用Nginx直接读取gridfs中的数据进行发送，这个类似于Nginx的sendfile机制。

文章作者就这几种方式做了测试，得出以下的benchmark数据。
1.1 通过Apache直接读取普通文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[chris@polaris conf]# ab -n 50000 -c 10 http://advice/images/embed/alliance-60.png
&amp;#160;
Server Software:        Apache/2.2.13
Server Hostname:        advice
Server Port:            80
&amp;#160;
Document Path:          [...]&lt;img src=&quot;http://www1.feedsky.com/t1/527369463/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/815.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>gridfs-fuse</category><category>技术相关</category><category>GridFS</category><category>NoSQL</category><category>效率</category><category>测试</category><category>benchmark</category><category>nginx-gridfs</category><pubDate>Wed, 13 Oct 2010 13:50:48 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2010/815.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=815</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2010/815.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369463/5457389</fs:itemid></item><item><title>【译】Cassandra 和 HBase 中使用的 BigTable 模型</title><link>http://lgone.com/html/y2010/812.html</link><content:encoded>&lt;div&gt;
&lt;p&gt;看到一篇好文章，部分翻译在此供大家参考。（PS：翻译部分全部是意译，挑了一些重点进行解释，详尽介绍和未翻译部分请参照&lt;a href=&quot;http://horicky.blogspot.com/2010/10/bigtable-model-with-cassandra-and-hbase.html&quot;&gt;原文&lt;/a&gt;。具体的一些实现原因，也可以参考我的上一篇文章《&lt;a title=&quot;Permanent Link: NoSQL理论之-内存是新的硬盘，硬盘是新的磁带&quot; rel=&quot;bookmark&quot; href=&quot;http://lgone.com/html/y2010/801.html&quot;&gt;NoSQL理论之-内存是新的硬盘，硬盘是新的磁带&lt;/a&gt;》）&lt;/p&gt;
&lt;p&gt;众所周知，BigTable是NoSQL数据库的王者，其论文更是NoSQL理论的基石，但遗憾的是BigTable不开源，于是有了开源的BigTable版本这一说法，其中的佼佼者包括今天提到的两位：Cassandra和HBase。&lt;/p&gt;
&lt;p&gt;本文主要对Cassandra和HBase特性和实现中对BigTable理论的应用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;1.Fundamentally Distributed（分布式存储）&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;项目从最初规划上，就是为海量数据服务的，当然分布式存储的思想也是扎根于其血脉中。分布式系统主要需要考虑两个方面：partitioning（分区存储，也可以理解为通常说的Sharding）、replication（数据复制，主要是将数据复制成多份以提高可用性）。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://blog.nosqlfan.com/wp-content/uploads/2010/10/P3.png&quot;&gt;&lt;img title=&quot;P3&quot; src=&quot;http://blog.nosqlfan.com/wp-content/uploads/2010/10/P3.png&quot; alt=&quot;&quot; width=&quot;400&quot; height=&quot;186&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;2.Column Oriented（列式存储）&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;和普通的RDBMS不一样，普通的RDBMS通常是行式存储的，一行数据是连续存在一段磁盘空间上的。而列式存储是将各个列分别进行连续的存储。也正是因此，它对于处理字段中的NULL字段，能够不占用过多的空间。同时能够支持灵活松散的列定义。也就是我们通常所说的schema-less。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://blog.nosqlfan.com/wp-content/uploads/2010/10/P4.png&quot;&gt;&lt;img title=&quot;P4&quot; src=&quot;http://blog.nosqlfan.com/wp-content/uploads/2010/10/P4.png&quot; alt=&quot;&quot; width=&quot;400&quot; height=&quot;215&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;&lt;strong&gt;3.Sequential write（顺序写磁盘）&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;BigTable型系统的一个特点是其对写性能进行的优化。它的写都是通过先记一条操作日志，然后直接写在内存中的数据集合，然后其集合按条件或定时将数据flush到磁盘。这里涉及到的记操作日志或者数据flush到磁盘都会顺序的磁盘操作。故而避免了磁盘随机操作造成的无谓的磁盘寻道时间。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;4.Merged read（读操作数据合并）&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;上面说到写操作是通过定时将数据直接flush到磁盘进行的，每次flush都会生成一个数据块，那可能造成一个数据在多个数据块中的情况，而在读的时候就需要将这多个版本中的值进行合并。其中在判断一个数据块是否包含指定值时使用了bloom-filter算法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;5.Periodic Data Compaction（定期数据合并）&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;同样是上面说到的，一个数据可能存在于多个数据块，如果我们不做处理，随着时间的推移，数据块会越来越多。所以BigTable型系统会进行定时的数据合并。在上面讲到的将内存中的数据直接flush到磁盘的过程中，flush之前进行了一次数据的排序操作，既是说存在磁盘中的块中的数据，都是顺序的，那么对一堆顺序的数据进行排重合并，其实和我们熟知的多路归并排序很相似。故而其定时数据合并的效率也是非常高的。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://blog.nosqlfan.com/wp-content/uploads/2010/10/P2.png&quot;&gt;&lt;img title=&quot;P2&quot; src=&quot;http://blog.nosqlfan.com/wp-content/uploads/2010/10/P2.png&quot; alt=&quot;&quot; width=&quot;400&quot; height=&quot;252&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;接下来的部分是关于标题中的两个产品Cassandra和HBase在这些理论上的具体实践和修改。暂时就不翻译了。有兴趣的同学可以查看英文原文。&lt;/p&gt;
&lt;/div&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369464/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/812.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2010/812.html/feed</wfw:commentRss><slash:comments>2195</slash:comments><description>看到一篇好文章，部分翻译在此供大家参考。（PS：翻译部分全部是意译，挑了一些重点进行解释，详尽介绍和未翻译部分请参照原文。具体的一些实现原因，也可以参考我的上一篇文章《NoSQL理论之-内存是新的硬盘，硬盘是新的磁带》）
众所周知，BigTable是NoSQL数据库的王者，其论文更是NoSQL理论的基石，但遗憾的是BigTable不开源，于是有了开源的BigTable版本这一说法，其中的佼佼者包括今天提到的两位：Cassandra和HBase。
本文主要对Cassandra和HBase特性和实现中对BigTable理论的应用。
1.Fundamentally Distributed（分布式存储）
项目从最初规划上，就是为海量数据服务的，当然分布式存储的思想也是扎根于其血脉中。分布式系统主要需要考虑两个方面：partitioning（分区存储，也可以理解为通常说的Sharding）、replication（数据复制，主要是将数据复制成多份以提高可用性）。

2.Column Oriented（列式存储）
和普通的RDBMS不一样，普通的RDBMS通常是行式存储的，一行数据是连续存在一段磁盘空间上的。而列式存储是将各个列分别进行连续的存储。也正是因此，它对于处理字段中的NULL字段，能够不占用过多的空间。同时能够支持灵活松散的列定义。也就是我们通常所说的schema-less。

3.Sequential write（顺序写磁盘）
BigTable型系统的一个特点是其对写性能进行的优化。它的写都是通过先记一条操作日志，然后直接写在内存中的数据集合，然后其集合按条件或定时将数据flush到磁盘。这里涉及到的记操作日志或者数据flush到磁盘都会顺序的磁盘操作。故而避免了磁盘随机操作造成的无谓的磁盘寻道时间。
4.Merged read（读操作数据合并）
上面说到写操作是通过定时将数据直接flush到磁盘进行的，每次flush都会生成一个数据块，那可能造成一个数据在多个数据块中的情况，而在读的时候就需要将这多个版本中的值进行合并。其中在判断一个数据块是否包含指定值时使用了bloom-filter算法。
5.Periodic Data Compaction（定期数据合并）
同样是上面说到的，一个数据可能存在于多个数据块，如果我们不做处理，随着时间的推移，数据块会越来越多。所以BigTable型系统会进行定时的数据合并。在上面讲到的将内存中的数据直接flush到磁盘的过程中，flush之前进行了一次数据的排序操作，既是说存在磁盘中的块中的数据，都是顺序的，那么对一堆顺序的数据进行排重合并，其实和我们熟知的多路归并排序很相似。故而其定时数据合并的效率也是非常高的。

接下来的部分是关于标题中的两个产品Cassandra和HBase在这些理论上的具体实践和修改。暂时就不翻译了。有兴趣的同学可以查看英文原文。&lt;img src=&quot;http://www1.feedsky.com/t1/527369464/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/812.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>BigTable</category><category>HBase</category><category>NoSQL</category><category>Cassandra</category><category>列式存储</category><category>分布式</category><pubDate>Sat, 09 Oct 2010 22:06:41 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2010/812.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=812</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2010/812.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369464/5457389</fs:itemid></item><item><title>NoSQL理论之-内存是新的硬盘，硬盘是新的磁带</title><link>http://lgone.com/html/y2010/801.html</link><content:encoded>&lt;div&gt;“内存是新的硬盘，硬盘是新的磁带”此话出自图灵奖得主&lt;a href=&quot;http://research.microsoft.com/en-us/um/people/gray/JimGrayHomePageSummary.htm&quot;&gt;Jim Gray&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;一、前言&lt;/h2&gt;
&lt;p&gt;我理解这句话的意思是，我们应该把随机IO都放到内存中去，而把像磁带一样的顺序IO留给硬盘（这里不包括SSD）。&lt;/p&gt;
&lt;p&gt;如果应用没有达到一定的级别，可能我们看上面两句话都会觉得太geek，然而在应用数据量日益庞大，动态内容比例日益增大的今天，再忽视这个基本准则将会是一个灾难。&lt;/p&gt;
&lt;p&gt;今天我们谈一下这一理论在NoSQL产品中的展现。&lt;/p&gt;
&lt;h2&gt;二、实现&lt;/h2&gt;
&lt;h3&gt;问题一：宕机数据丢失&lt;/h3&gt;
&lt;p&gt;我们先看一下几个杰出的NoSQL代表，Cassandra，MongoDB，Redis。他们几乎都使用了同一种存储模式，就是将写操作在内存中进行，定时或按某一条件将内存中的数据直接写到磁盘上。这样做的好处是我们可以充分利用内存在随机IO上的优势，而避免了直接写磁盘带来的随机IO瓶颈：磁盘寻道时间。当然，坏处就是如果遭遇宕机等问题时，可能会丢失一些数据。&lt;/p&gt;
&lt;p&gt;解决宕机丢数据的问题有两个方法：&lt;/p&gt;
&lt;h4&gt;1.实时记录操作日志&lt;/h4&gt;
&lt;p&gt;这时通常的做法是当一个写操作到达，系统首先会往日志文件里追加一条写记录，成功后再操作内存进行写数据操作。而由于日志文件是不断追加的，因此也就保证了不会有大量的随机IO产生。&lt;/p&gt;
&lt;h4&gt;2.Quorum NRW&lt;/h4&gt;
&lt;p&gt;这一理论是基于集群式存储的，其原理是如果集群有N个结点，那么如果我们每次写操作需要至少同步到W个结点才算成功，而每次读操作只要从R个结点读数据就一定能保证其得到正确结果（如果某一结点有此数据，既成功，如果所有R个结点都无数据，则说明无此数据）。而NRW之间的关系必须满足N &amp;lt; R + W 。其实这一理论并不难理解，我们可以将这个不等式做一下移项：R &amp;gt; N &amp;#8211; W ，我们有N个结点，写的时候最少写W个才算成功，也就是W个结点有这份数据，那么N-W就是说可能没有某一份数据的最大结点数。最多可能有N-W个结点没有某一数据，那如果我们进行数据读取操作时，读到大于N-W个结点，那么必然有一个以上的结点是有这份数据的。所以要求R &amp;gt; N &amp;#8211; W。&lt;/p&gt;
&lt;p&gt;所以可能你已经想明白了，为了防止数据丢失，我们采用的实际是简单的冗余备份的方法。数据写到多台机器会比写单台机器的磁盘快吗？对。相对于直接的磁盘操作，跨网络进行内存操作可以更快。其最简单的例子就是改进的一致性hash，（关于一致性hash请看&lt;a href=&quot;http://en.wikipedia.org/wiki/Consistent_hashing&quot;&gt;这里&lt;/a&gt;）：&lt;img src=&quot;https://lh3.googleusercontent.com/XMMPt9FtrYkyFeRXiMmMEfK0RVRb-6nGc1lTEP3gJDLhr2aLPaM-M0UaV16tPCBTpa9G2SX8wEeOlvV3wcaCaSPNTD4w6rSBIEN0GucyDJ-dnlKyVw&quot; alt=&quot;&quot; width=&quot;334px;&quot; height=&quot;265px;&quot; /&gt;&lt;br /&gt;
上图摘自&lt;a href=&quot;http://s3.amazonaws.com/AllThingsDistributed/sosp/amazon-dynamo-sosp2007.pdf&quot;&gt;Amazon的Dynamo文档&lt;/a&gt;，key的hash值位于A，B结点间的数据，并不是只存在B结点上，而是顺着环的方向分别在C和D结点进行备份。当然这样做的好处并不完全在于上面说的冗余备份。&lt;/p&gt;
&lt;p&gt;当然，很多时候是上面两种解决方法同时使用以保证数据的高可用性。&lt;/p&gt;
&lt;h3&gt;问题二：内存容量的限制&lt;/h3&gt;
&lt;p&gt;当我们将内存当作硬盘来用的时候，我们必然会面临容量问题。这也是我们上面说到的数据会定时flush到磁盘的原因，当内存中的数据已经超出可用内存的大小，那么我们就需要将其进行落地操作，对swap的过度使用是不符合我们初衷的，也是达不到高效随机IO的效果的。这里也有两种解决方案：&lt;/p&gt;
&lt;h4&gt;1.应用层swap&lt;/h4&gt;
&lt;p&gt;采用这种方法的有 TokyoCabinet 和 Redis 两个产品。TokyoCabinet主要是通过mmap提高IO效率，而其mmap到的只有数据文件头部的一部分内容。一旦数据文件大于其设置的最大mmap长度（由参数xmsize控制），那剩下的部分就是纯粹的低效磁盘操作了。于是它提供了一种类似于Memcached的缓存机制，通过参数rcnum配置，将一些通过LRU机制筛选出来的热数据进行key-value式的缓存，这一部分内存是和mmap占用的内存完全独立的。同样的，Redis在2.0版本之后增加了对磁盘存储的支持，其机制与 TokyoCabinet 类似，也是通过数据操作来判断数据的热度，并将热数据尽量放到内存中。&lt;/p&gt;
&lt;h4&gt;2.多版本的数据合并&lt;/h4&gt;
&lt;p&gt;什么叫多版本的数据合并呢？我们上面讲 Bigtable，或其开源版本 Cassandra，都是通过定时将内存中的数据块flush到磁盘中，那么我们会想，如果这次是一个update操作，比如 keyA 的值从 ValueA 变成了 ValueB，那么我们在flush到磁盘的时候就得执行对老数据 ValueA 的清除工作了。而这样，是否就达不到我们希望进行顺序的磁盘IO的目的呢？没错，这样是达不到的，所以 Bigtable 类型的系统确实也并不是这样做的，在flush磁盘的时候，并不会执行合并操作，而是直接将内存数据写入磁盘。这样写是方便很多，那读的时候可能会存在一个值有多个版本的情况，这时就需要我们来进行多版本合并了。所以第二种方法就是将一段时间的写操作写成一个块（可能并非一个文件），保证内存的使用不会无限膨胀。在读取时通过读多个文件块进行数据版本合并来完成。&lt;/p&gt;
&lt;p&gt;那如果存储在磁盘的数据量是内存容量的很多倍，我们可能会产生许多个数据块，那么我们在获取数据版本时，是否需要全部遍历所有数据块呢？当然不用，如果你看过&lt;a href=&quot;http://labs.google.com/papers/bigtable-osdi06.pdf&quot;&gt;BigTable论文&lt;/a&gt;，相信你还记得它其中用到了 &lt;a href=&quot;http://en.wikipedia.org/wiki/Bloom_filter&quot;&gt;bloom-filter&lt;/a&gt; 算法。bloom-filter 算法最广泛的应用是在搜索引擎爬虫中，它用于判断一个URL是否存在于已抓取集合中，这一算法并不百分之百精准（可能将不在集合中的数据误判为在集合中，但不会出现相反的误差），但其在时间复杂度上仅是几次hash计算，而空间复杂度也非常低。Bigtable 实现中也用到了 bloom-filter 算法，用它来判断一个值是否在某一个集合中。而由于 bloom-filter 算法的特点，我们只会多读（几率很小），不会少读数据块。于是我们就实现对远远大于物理内存容量的数据的存储。&lt;/p&gt;
&lt;h2&gt;三、结尾&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;好了，就写到这里，关于NoSQL中对此原理的应用还有更多理解和认识的同学，欢迎交流。&lt;/div&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369465/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/801.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2010/801.html/feed</wfw:commentRss><slash:comments>22</slash:comments><description>“内存是新的硬盘，硬盘是新的磁带”此话出自图灵奖得主Jim Gray。
一、前言
我理解这句话的意思是，我们应该把随机IO都放到内存中去，而把像磁带一样的顺序IO留给硬盘（这里不包括SSD）。
如果应用没有达到一定的级别，可能我们看上面两句话都会觉得太geek，然而在应用数据量日益庞大，动态内容比例日益增大的今天，再忽视这个基本准则将会是一个灾难。
今天我们谈一下这一理论在NoSQL产品中的展现。
二、实现
问题一：宕机数据丢失
我们先看一下几个杰出的NoSQL代表，Cassandra，MongoDB，Redis。他们几乎都使用了同一种存储模式，就是将写操作在内存中进行，定时或按某一条件将内存中的数据直接写到磁盘上。这样做的好处是我们可以充分利用内存在随机IO上的优势，而避免了直接写磁盘带来的随机IO瓶颈：磁盘寻道时间。当然，坏处就是如果遭遇宕机等问题时，可能会丢失一些数据。
解决宕机丢数据的问题有两个方法：
1.实时记录操作日志
这时通常的做法是当一个写操作到达，系统首先会往日志文件里追加一条写记录，成功后再操作内存进行写数据操作。而由于日志文件是不断追加的，因此也就保证了不会有大量的随机IO产生。
2.Quorum NRW
这一理论是基于集群式存储的，其原理是如果集群有N个结点，那么如果我们每次写操作需要至少同步到W个结点才算成功，而每次读操作只要从R个结点读数据就一定能保证其得到正确结果（如果某一结点有此数据，既成功，如果所有R个结点都无数据，则说明无此数据）。而NRW之间的关系必须满足N &amp;#60; R + W 。其实这一理论并不难理解，我们可以将这个不等式做一下移项：R &amp;#62; N &amp;#8211; W ，我们有N个结点，写的时候最少写W个才算成功，也就是W个结点有这份数据，那么N-W就是说可能没有某一份数据的最大结点数。最多可能有N-W个结点没有某一数据，那如果我们进行数据读取操作时，读到大于N-W个结点，那么必然有一个以上的结点是有这份数据的。所以要求R &amp;#62; N &amp;#8211; W。
所以可能你已经想明白了，为了防止数据丢失，我们采用的实际是简单的冗余备份的方法。数据写到多台机器会比写单台机器的磁盘快吗？对。相对于直接的磁盘操作，跨网络进行内存操作可以更快。其最简单的例子就是改进的一致性hash，（关于一致性hash请看这里）：
上图摘自Amazon的Dynamo文档，key的hash值位于A，B结点间的数据，并不是只存在B结点上，而是顺着环的方向分别在C和D结点进行备份。当然这样做的好处并不完全在于上面说的冗余备份。
当然，很多时候是上面两种解决方法同时使用以保证数据的高可用性。
问题二：内存容量的限制
当我们将内存当作硬盘来用的时候，我们必然会面临容量问题。这也是我们上面说到的数据会定时flush到磁盘的原因，当内存中的数据已经超出可用内存的大小，那么我们就需要将其进行落地操作，对swap的过度使用是不符合我们初衷的，也是达不到高效随机IO的效果的。这里也有两种解决方案：
1.应用层swap
采用这种方法的有 TokyoCabinet 和 Redis 两个产品。TokyoCabinet主要是通过mmap提高IO效率，而其mmap到的只有数据文件头部的一部分内容。一旦数据文件大于其设置的最大mmap长度（由参数xmsize控制），那剩下的部分就是纯粹的低效磁盘操作了。于是它提供了一种类似于Memcached的缓存机制，通过参数rcnum配置，将一些通过LRU机制筛选出来的热数据进行key-value式的缓存，这一部分内存是和mmap占用的内存完全独立的。同样的，Redis在2.0版本之后增加了对磁盘存储的支持，其机制与 TokyoCabinet 类似，也是通过数据操作来判断数据的热度，并将热数据尽量放到内存中。
2.多版本的数据合并
什么叫多版本的数据合并呢？我们上面讲 Bigtable，或其开源版本 Cassandra，都是通过定时将内存中的数据块flush到磁盘中，那么我们会想，如果这次是一个update操作，比如 keyA 的值从 ValueA 变成了 ValueB，那么我们在flush到磁盘的时候就得执行对老数据 ValueA 的清除工作了。而这样，是否就达不到我们希望进行顺序的磁盘IO的目的呢？没错，这样是达不到的，所以 Bigtable 类型的系统确实也并不是这样做的，在flush磁盘的时候，并不会执行合并操作，而是直接将内存数据写入磁盘。这样写是方便很多，那读的时候可能会存在一个值有多个版本的情况，这时就需要我们来进行多版本合并了。所以第二种方法就是将一段时间的写操作写成一个块（可能并非一个文件），保证内存的使用不会无限膨胀。在读取时通过读多个文件块进行数据版本合并来完成。
那如果存储在磁盘的数据量是内存容量的很多倍，我们可能会产生许多个数据块，那么我们在获取数据版本时，是否需要全部遍历所有数据块呢？当然不用，如果你看过BigTable论文，相信你还记得它其中用到了 bloom-filter 算法。bloom-filter 算法最广泛的应用是在搜索引擎爬虫中，它用于判断一个URL是否存在于已抓取集合中，这一算法并不百分之百精准（可能将不在集合中的数据误判为在集合中，但不会出现相反的误差），但其在时间复杂度上仅是几次hash计算，而空间复杂度也非常低。Bigtable 实现中也用到了 bloom-filter 算法，用它来判断一个值是否在某一个集合中。而由于 bloom-filter 算法的特点，我们只会多读（几率很小），不会少读数据块。于是我们就实现对远远大于物理内存容量的数据的存储。
三、结尾

好了，就写到这里，关于NoSQL中对此原理的应用还有更多理解和认识的同学，欢迎交流。&lt;img src=&quot;http://www1.feedsky.com/t1/527369465/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/801.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>技术相关</category><category>NoSQL</category><category>内存是新的硬盘</category><category>硬盘是新的磁带</category><category>Quorum NRW</category><pubDate>Mon, 04 Oct 2010 19:12:24 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2010/801.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=801</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2010/801.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369465/5457389</fs:itemid></item><item><title>MongoDB MapReduce</title><link>http://lgone.com/html/y2010/790.html</link><content:encoded>&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;MapReduce&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;
MapReduce是一种计算模型，简单的说就是将大批量的工作（数据）分解（MAP）执行，然后再将结果合并成最终结果（REDUCE）。这样做的好处是可以在任务被分解后，可以通过大量机器进行并行计算，减少整个操作的时间。&lt;/p&gt;
&lt;p&gt;对科班出生的程序员来说，最好的例子莫过于归并排序的例子，没错，归并排序流程就可以看作是一个MapReduce，只是我们在学校写过的归并排序程序可能还没有涉及到并行计算罢了。&lt;/p&gt;
&lt;p&gt;上面是MapReduce的理论部分，下面说实际的应用，下面以MongoDB MapReduce为例说明。&lt;/p&gt;
&lt;p&gt;下面是MongoDB官方的一个例子：&lt;/p&gt;

&lt;div class=&quot;wp_codebox&quot;&gt;&lt;table&gt;&lt;tr id=&quot;p79014&quot;&gt;&lt;td class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot; id=&quot;p790code14&quot;&gt;&lt;pre class=&quot;shell&quot; style=&quot;font-family:monospace;&quot;&gt;$ ./mongo
&amp;gt; db.things.insert( { _id : 1, tags : ['dog', 'cat'] } );
&amp;gt; db.things.insert( { _id : 2, tags : ['cat'] } );
&amp;gt; db.things.insert( { _id : 3, tags : ['mouse', 'cat', 'dog'] } );
&amp;gt; db.things.insert( { _id : 4, tags : []  } );
&amp;nbsp;
&amp;gt; // map function
&amp;gt; m = function(){
...    this.tags.forEach(
...        function(z){
...            emit( z , { count : 1 } );
...        }
...    );
...};
&amp;nbsp;
&amp;gt; // reduce function
&amp;gt; r = function( key , values ){
...    var total = 0;
...    for ( var i=0; i&amp;lt;values.length; i++ )
...        total += values[i].count;
...    return { count : total };
...};
&amp;nbsp;
&amp;gt; res = db.things.mapReduce(m,r);
&amp;gt; res
{&amp;quot;timeMillis.emit&amp;quot; : 9 , &amp;quot;result&amp;quot; : &amp;quot;mr.things.1254430454.3&amp;quot; ,
 &amp;quot;numObjects&amp;quot; : 4 , &amp;quot;timeMillis&amp;quot; : 9 , &amp;quot;errmsg&amp;quot; : &amp;quot;&amp;quot; , &amp;quot;ok&amp;quot; : 0}
&amp;nbsp;
&amp;gt; db[res.result].find()
{&amp;quot;_id&amp;quot; : &amp;quot;cat&amp;quot; , &amp;quot;value&amp;quot; : {&amp;quot;count&amp;quot; : 3}}
{&amp;quot;_id&amp;quot; : &amp;quot;dog&amp;quot; , &amp;quot;value&amp;quot; : {&amp;quot;count&amp;quot; : 2}}
{&amp;quot;_id&amp;quot; : &amp;quot;mouse&amp;quot; , &amp;quot;value&amp;quot; : {&amp;quot;count&amp;quot; : 1}}
&amp;nbsp;
&amp;gt; db[res.result].drop()&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;例子很简单，计算一个标签系统中每个标签出现的次数。&lt;/p&gt;
&lt;p&gt;这里面，除了emit函数之外，所有都是标准的js语法，当然你也可以使用你所知道的所有标准js函数。而这个emit函数是非常重要的，他的作用是将一条数据放入数据分组集合，这个分组是以emit的第一个参数为key的。你可以这样理解，当你在所有需要计算的行执行完了map函数，你就得到了一组key-values对。基本key是emit中的key，values是每次emit函数的第二个参数组成的集合。&lt;/p&gt;
&lt;p&gt;现在我们的任务就是将这一个key-values变在key-value，也就是把这一个集合变成一个单一的值。这个操作就是Reduce。&lt;/p&gt;
&lt;p&gt;好像这里和我们前面的理论是完全一样的，其实不然。当我们的key-values中的values集合过大，会被再切分成很多个小的key-values块，然后分别执行Reduce函数，再将多个块的结果组合成一个新的集合，作为Reduce函数的第二个参数，继续Reducer操作。可以预见，如果我们初始的values非常大，可能还会对第一次分块计算后组成的集合再次Reduce。这就类似于多阶的归并排序了。具体会有多少重，就看数据量了。&lt;/p&gt;
&lt;p&gt;上面这一内部机制，我们不必非常了解，但我们必须了解这一机制会要求我们遵守的原则，那就是当我们书写Map函数时，emit的第二个参数形式是我们的Reduce函数的第二个参数，而Reduce函数的返回值，可能会作为新的输入参数再次执行Reduce操作，所以Reduce函数的返回值也需要和Reduce函数的第二个参数结构一致。&lt;/p&gt;
&lt;p&gt;作为结束，下面照本宣科说一下MongoDB MapReduce调用参数和返回结果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;参数表如下&lt;/span&gt;&lt;/strong&gt;：&lt;/p&gt;

&lt;div class=&quot;wp_codebox&quot;&gt;&lt;table&gt;&lt;tr id=&quot;p79015&quot;&gt;&lt;td class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot; id=&quot;p790code15&quot;&gt;&lt;pre class=&quot;shell&quot; style=&quot;font-family:monospace;&quot;&gt;db.runCommand(
 { mapreduce : &amp;lt;collection&amp;gt;,
   map : &amp;lt;mapfunction&amp;gt;,
   reduce : &amp;lt;reducefunction&amp;gt;
   [, query : &amp;lt;query filter object&amp;gt;]
   [, sort : &amp;lt;sort the query.  useful for optimization&amp;gt;]
   [, limit : &amp;lt;number of objects to return from collection&amp;gt;]
   [, out : &amp;lt;output-collection name&amp;gt;]
   [, keeptemp: &amp;lt;true|false&amp;gt;]
   [, finalize : &amp;lt;finalizefunction&amp;gt;]
   [, scope : &amp;lt;object where fields go into javascript global scope &amp;gt;]
   [, verbose : true]
 }
);&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;mapreduce：指定要进行mapreduce处理的collection&lt;/li&gt;
&lt;li&gt;map：map函数&lt;/li&gt;
&lt;li&gt;reduce：reduce函数&lt;/li&gt;
&lt;li&gt;query：一个筛选条件，只有满足条件的行才会加入mapreduce集合，而这个筛选过程是先于整个mapreduce流程而执行的&lt;/li&gt;
&lt;li&gt;sort：和query结合的sort排序参数，这是唯一可以优化分组机制的地方&lt;/li&gt;
&lt;li&gt;limit：同上&lt;/li&gt;
&lt;li&gt;out：结果输出的collection的名字，不指定会默认创建一个随机名字的collection&lt;/li&gt;
&lt;li&gt;keytemp：true或false，表明结果输出到的collection是否是临时的，如果为true，则会在客户端连接中断后自动删除，如果你用的是MongoDB的mongo客户端连接，那必须exit后才会删除。如果是脚本执行，脚本退出或调用close会自动删除结果collection&lt;/li&gt;
&lt;li&gt;finalize：和map，reduce一样是一个函数，它可以在reduce得出一个结果后再对key和value进行一次计算并返回一个最终结果&lt;/li&gt;
&lt;li&gt;scope：设置参数值，在这里设置的值在map，reduce，finalize函数中可见&lt;/li&gt;
&lt;li&gt;verbose：在执行过程中打印调试信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;返回结果结构如下&lt;/span&gt;&lt;/strong&gt;：&lt;/p&gt;

&lt;div class=&quot;wp_codebox&quot;&gt;&lt;table&gt;&lt;tr id=&quot;p79016&quot;&gt;&lt;td class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot; id=&quot;p790code16&quot;&gt;&lt;pre class=&quot;shell&quot; style=&quot;font-family:monospace;&quot;&gt;{ result : &amp;lt;collection_name&amp;gt;,
  counts : {
       input :  &amp;lt;number of objects scanned&amp;gt;,
       emit  : &amp;lt;number of times emit was called&amp;gt;,
       output : &amp;lt;number of items in output collection&amp;gt;
  } ,
  timeMillis : &amp;lt;job_time&amp;gt;,
  ok : &amp;lt;1_if_ok&amp;gt;,
  [, err : &amp;lt;errmsg_if_error&amp;gt;]
}&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;result：储存结果的collection的名字&lt;/li&gt;
&lt;li&gt;input：满足条件的数据行数&lt;/li&gt;
&lt;li&gt;emit：emit调用次数，也就是所有集合中的数据总量&lt;/li&gt;
&lt;li&gt;ouput：返回结果条数&lt;/li&gt;
&lt;li&gt;timeMillis：执行时间，毫秒为单位&lt;/li&gt;
&lt;li&gt;ok：是否成功，成功为1&lt;/li&gt;
&lt;li&gt;err：如果失败，这里可以有失败原因，不过从经验上来看，原因比较模糊，作用不大&lt;/li&gt;
&lt;/ul&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/527369466/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/790.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</content:encoded><wfw:commentRss>http://lgone.com/html/y2010/790.html/feed</wfw:commentRss><slash:comments>6</slash:comments><description>MapReduce
MapReduce是一种计算模型，简单的说就是将大批量的工作（数据）分解（MAP）执行，然后再将结果合并成最终结果（REDUCE）。这样做的好处是可以在任务被分解后，可以通过大量机器进行并行计算，减少整个操作的时间。
对科班出生的程序员来说，最好的例子莫过于归并排序的例子，没错，归并排序流程就可以看作是一个MapReduce，只是我们在学校写过的归并排序程序可能还没有涉及到并行计算罢了。
上面是MapReduce的理论部分，下面说实际的应用，下面以MongoDB MapReduce为例说明。
下面是MongoDB官方的一个例子：

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ ./mongo
&amp;#62; db.things.insert( { _id : 1, tags : ['dog', 'cat'] } );
&amp;#62; db.things.insert( { _id : 2, tags : ['cat'] } );
&amp;#62; db.things.insert( { _id : 3, tags : ['mouse', 'cat', 'dog'] } );
&amp;#62; db.things.insert( { _id : 4, tags : []  } );
&amp;#160;
&amp;#62; // map function
&amp;#62; m = function(){
...   [...]&lt;img src=&quot;http://www1.feedsky.com/t1/527369466/iammutex/feedsky/s.gif?r=http://lgone.com/html/y2010/790.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><category>MapReduce</category><category>MongoDB</category><category>NoSQL</category><pubDate>Wed, 25 Aug 2010 23:12:25 +0800</pubDate><author>iammutex</author><comments>http://lgone.com/html/y2010/790.html#comments</comments><guid isPermaLink="false">http://lgone.com/?p=790</guid><dc:creator>iammutex</dc:creator><fs:srclink>http://lgone.com/html/y2010/790.html</fs:srclink><fs:srcfeed>http://lgone.com/blog/feed</fs:srcfeed><fs:itemid>feedsky/iammutex/~7347961/527369466/5457389</fs:itemid></item></channel></rss>
