<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>我想网 &#187; Database</title>
	<atom:link href="http://www.iwanna.cn/topics/develope/db/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.iwanna.cn</link>
	<description></description>
	<lastBuildDate>Mon, 26 Dec 2011 05:46:16 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
		<item>
		<title>5款优秀免费在线数据备份/存储工具</title>
		<link>http://www.iwanna.cn/archives/2011/03/17/6368/</link>
		<comments>http://www.iwanna.cn/archives/2011/03/17/6368/#comments</comments>
		<pubDate>Thu, 17 Mar 2011 14:45:53 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[Tool]]></category>
		<category><![CDATA[Free]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=6368</guid>
		<description><![CDATA[数据备份非常重要，无论是为了避免系统发生故障造成损失，还是平日对重要数据进行管理。当然你可以选择使用第二个硬盘对数据进行备份管理，问题是如 果可以不花钱就能做到这一点，何乐而不为？此外，你也很难保证你的第二块硬盘不不会发生故障吧。因此，选择一些有保证的在线存储备份服务是非常不错的主 意。 在线存储工具非常之多，在去年年底我为大家总结了“2010年最佳国外免费在线存储服务”。现在再为大家介绍5款优秀的免费在线数据备份存储工具： Windows Live SkyDrive Windows Live Skydrive是非常流行的基于云计算的服务，提供25GB的免费存储空间。SkyDrive的使用非常简单，首先使用你的Live ID登录为Windows Live Skydrive账户。然后就像简单的拖放文件那样就行了，你可以根据需要在不同文件夹间拖放文件。每一个文件夹都有一个固定链接，这样你就可以轻易地与 你的朋友、家人或者网友进行分享。此外，你可以对个人私密文件夹进行加密保护。 Binfire Binfire是另一家在线备份服务商，提供10GB的免费存储空间。它类似于Skydrive可创建私密文件夹或者共享文件夹。此 外，Binfire还是一个项目管理工具。它支持它支持项目仪表盘，任务计划和里程碑功能，项目群聊和使用tweets进行情况报告。 即使你对项目管理功能不感兴趣，你也可以使用它提供的免费在线存储空间对一些文件进行备份管理。 Idrive Idrive是另一款在线备份工具，提供5GB的免费存储空间。如果你需要更大的Idrive存储空间，你得付出一定的价钱。例如，Idrive提 供150GB的付费存储存储空间，每个月需要支付4.95美金（用户群明显不是针对中国大陆的网名）。还有一个家庭套餐，提供500GB的存储空间，运行 5个家庭成员共同使用，每个月则高达14.95美金。为了保证您的数据安全，iDrive提供了128 位SSL加密传输，256位AES加密存储。 GMail Drive Gmail Drive又是一款不错的免费在线备份存储空间。你只需要一个Gmail账户即可建立一个虚拟文件系统，并运行你使用Gmail作为存储介质，因此 Gmail用户使用起来非常方便。 什么是Gmail驱动器，使用Gmail帐户在本地创建一个虚拟文件系统，你可以像Windows资源管理那样直接存储和检索数据。你可以在这个驱 动器上创建文件夹和复制内容，所使用的存储空间是Gmail的存储空间（超过6GB）。 当然，还有许许多多在线备份存储空间，这里只列举期中比较常见和出名的免费在线备份存储空间。此前介绍的12个在线存储空间也是不错的选择，你可以有多个选择，毕竟备份重要数据，最好能够做到多管齐下，有备无患。﻿ © 我想网 Akon 所有 , 2011. &#124; 永久链接 &#124; 1条评论 &#124; 提交到 Google Reader 鲜果 抓虾 Feed enhanced by Better Feed from Ozh]]></description>
			<content:encoded><![CDATA[<p>数据备份非常重要，无论是为了避免系统发生故障造成损失，还是平日对重要数据进行管理。当然你可以选择使用第二个硬盘对数据进行备份管理，问题是如 果可以不花钱就能做到这一点，何乐而不为？此外，你也很难保证你的第二块硬盘不不会发生故障吧。因此，选择一些有保证的在线存储备份服务是非常不错的主 意。</p>
<p><a href="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/1233220948875328.jpg"><img title="online-storage" src="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/1233220948875328.jpg" alt="online storage " width="480" height="196" /></a></p>
<p>在线存储工具非常之多，在去年年底我为大家总结了“<a title="2010年最佳国外免费在线存储服务" href="http://www.iwanna.cn/archives/2011/03/17/6367/" target="_blank">2010年最佳国外免费在线存储服务</a>”。现在再为大家介绍5款优秀的免费在线数据备份存储工具：</p>
<h3><a title="Windows Live SkyDrive - Free Microsoft Online Data  Storage" href="http://explore.live.com/windows-live-skydrive" target="_blank">Windows Live SkyDrive</a></h3>
<p><a href="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/12332311012217036.jpg"><img title="Windows-Live-Sky-Drive" src="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/12332311012217036.jpg" alt="Windows Live Sky Drive " width="480" height="194" /></a><br />
Windows Live  Skydrive是非常流行的基于云计算的服务，提供25GB的免费存储空间。SkyDrive的使用非常简单，首先使用你的Live  ID登录为Windows Live  Skydrive账户。然后就像简单的拖放文件那样就行了，你可以根据需要在不同文件夹间拖放文件。每一个文件夹都有一个固定链接，这样你就可以轻易地与 你的朋友、家人或者网友进行分享。此外，你可以对个人私密文件夹进行加密保护。<br />
<span id="more-6368"></span></p>
<h3><a title="Binfire - Free Online Storage and data backup tool" href="http://www.techmixer.com/free-online-storage-backup-tools/www.binfire.com" target="_blank">Binfire</a></h3>
<p><a href="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/12332321948249658.jpg"><img title="Binfire" src="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/12332321948249658.jpg" alt="Binfire " width="480" height="247" /></a></p>
<p>Binfire是另一家在线备份服务商，提供10GB的免费存储空间。它类似于Skydrive可创建私密文件夹或者共享文件夹。此 外，Binfire还是一个项目管理工具。它支持它支持项目仪表盘，任务计划和里程碑功能，项目群聊和使用tweets进行情况报告。</p>
<p>即使你对项目管理功能不感兴趣，你也可以使用它提供的免费在线存储空间对一些文件进行备份管理。</p>
<h3><a title="Idrive - Free Online Backup Storage" href="http://www.idrive.com/" target="_blank">Idrive</a></h3>
<p><a href="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/1233233249609215.jpg"><img title="IDrive" src="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/1233233249609215.jpg" alt="IDrive " width="480" height="389" /></a></p>
<p>Idrive是另一款在线备份工具，提供5GB的免费存储空间。如果你需要更大的Idrive存储空间，你得付出一定的价钱。例如，Idrive提 供150GB的付费存储存储空间，每个月需要支付4.95美金（用户群明显不是针对中国大陆的网名）。还有一个家庭套餐，提供500GB的存储空间，运行 5个家庭成员共同使用，每个月则高达14.95美金。为了保证您的数据安全，iDrive提供了128 位SSL加密传输，256位AES加密存储。</p>
<h3><a title="Gmail Drive - Store data on Gmail" href="http://www.viksoe.dk/code/gmail.htm" target="_blank">GMail Drive</a></h3>
<p><a href="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/1233234493518936.jpg"><img title="GmailDrive" src="http://images.uheed.com/iwanna/2011/03/17/five-database-tools/1233234493518936.jpg" alt="GmailDrive " width="480" height="291" /></a></p>
<p>Gmail  Drive又是一款不错的免费在线备份存储空间。你只需要一个Gmail账户即可建立一个虚拟文件系统，并运行你使用Gmail作为存储介质，因此 Gmail用户使用起来非常方便。</p>
<p>什么是Gmail驱动器，使用Gmail帐户在本地创建一个虚拟文件系统，你可以像Windows资源管理那样直接存储和检索数据。你可以在这个驱 动器上创建文件夹和复制内容，所使用的存储空间是Gmail的存储空间（超过6GB）。</p>
<p>当然，还有许许多多在线备份存储空间，这里只列举期中比较常见和出名的免费在线备份存储空间。此前介绍的<a href="http://www.x-berry.com/best-free-sharing-services-of-2010" target="_blank">12个在线存储空间</a>也是不错的选择，你可以有多个选择，毕竟备份重要数据，最好能够做到多管齐下，有备无患。﻿</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2011. |
<a href="http://www.iwanna.cn/archives/2011/03/17/6368/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2011/03/17/6368/#comments">1条评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2011/03/17/6368/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2011/03/17/6368/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2011/03/17/6368/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2011/03/17/6368/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MySQL Query Cache 小结</title>
		<link>http://www.iwanna.cn/archives/2011/02/27/6350/</link>
		<comments>http://www.iwanna.cn/archives/2011/02/27/6350/#comments</comments>
		<pubDate>Sat, 26 Feb 2011 17:35:59 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[Mysql]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=6350</guid>
		<description><![CDATA[顾名思义，MySQL Query Cache 就是用来缓存和 Query 相关的数据的。具体来说，Query Cache 缓存了我们客户端提交给 MySQL 的 SELECT 语句以及该语句的结果集。大概来讲，就是将 SELECT 语句和语句的结果做了一个 HASH 映射关系然后保存在一定的内存区域中。 在大部分的 MySQL 分发版本中，Query Cache 功能默认都是打开的，我们可以通过调整 MySQL Server 的参数选项打开该功能。主要由以下5个参数构成： query_cache_limit：允许 Cache 的单条 Query 结果集的最大容量，默认是1MB，超过此参数设置的 Query 结果集将不会被 Cache query_cache_min_res_unit：设置 Query Cache 中每次分配内存的最小空间大小，也就是每个 Query 的 Cache 最小占用的内存空间大小 query_cache_size：设置 Query Cache 所使用的内存大小，默认值为0，大小必须是1024的整数倍，如果不是整数倍，MySQL 会自动调整降低最小量以达到1024的倍数 query_cache_type：控制 Query Cache 功能的开关，可以设置为0(OFF),1(ON)和2(DEMAND)三种，意义分别如下： 0(OFF)：关闭 Query Cache 功能，任何情况下都不会使用 Query Cache [...]]]></description>
			<content:encoded><![CDATA[<p>顾名思义，<a href="http://www.iwanna.cn/tags/mysql/" class="st_tag internal_tag" rel="tag" title="标签 MySQL 下的日志">MySQL</a> Query Cache 就是用来缓存和 Query 相关的数据的。具体来说，Query Cache  缓存了我们客户端提交给 <a href="http://www.iwanna.cn/tags/mysql/" class="st_tag internal_tag" rel="tag" title="标签 MySQL 下的日志">MySQL</a> 的 SELECT 语句以及该语句的结果集。大概来讲，就是将 SELECT 语句和语句的结果做了一个 HASH  映射关系然后保存在一定的内存区域中。</p>
<p>在大部分的 MySQL 分发版本中，Query Cache 功能默认都是打开的，我们可以通过调整 MySQL Server  的参数选项打开该功能。主要由以下5个参数构成：</p>
<ul>
<li>query_cache_limit：允许 Cache 的单条 Query 结果集的最大容量，默认是1MB，超过此参数设置的 Query  结果集将不会被 Cache</li>
<li>query_cache_min_res_unit：设置 Query Cache 中每次分配内存的最小空间大小，也就是每个  Query 的 Cache 最小占用的内存空间大小</li>
<li>query_cache_size：设置 Query Cache  所使用的内存大小，默认值为0，大小必须是1024的整数倍，如果不是整数倍，MySQL 会自动调整降低最小量以达到1024的倍数</li>
<li>query_cache_type：控制 Query Cache  功能的开关，可以设置为0(OFF),1(ON)和2(DEMAND)三种，意义分别如下：
<ul>
<li>0(OFF)：关闭 Query Cache 功能，任何情况下都不会使用 Query Cache</li>
<li>1(ON)：开启 Query Cache 功能，但是当 SELECT 语句中使用的 SQL_NO_CACHE  提示后，将不使用Query Cache</li>
<li>2(DEMAND)：开启 Query Cache 功能，但是只有当 SELECT 语句中使用了 SQL_CACHE  提示后，才使用 Query Cache</li>
</ul>
</li>
<li>query_cache_wlock_invalidate：控制当有写锁定发生在表上的时刻是否先失效该表相关的 Query  Cache，如果设置为 1(TRUE)，则在写锁定的同时将失效该表相关的所有 Query  Cache，如果设置为0(FALSE)则在锁定时刻仍然允许读取该表相关的 Query Cache。</li>
</ul>
<p><span id="more-6350"></span><br />
<strong>Query Cache 如何处理子查询的？</strong><br />
这是我遇到的最为常见的一个问题。其实 Query  Cache 是以客户端请求提交的 Query 为对象来处理的，只要客户端请求的是一个 Query，无论这个 Query  是一个简单的单表查询还是多表 Join，亦或者是带有子查询的复杂 SQL，都被当作成一个 Query，不会被分拆成多个 Query 来进行  Cache。所以，存在子查询的复杂 Query 也只会产生一个Cache对象，子查询不会产生单独的Cache内容。UNION[ALL]  类型的语句也同样如此。</p>
<p><strong>Query Cache 是以 block 的方式存储的数据块吗？</strong><br />
不是，Query Cache  中缓存的内容仅仅只包含该 Query 所需要的结果数据，是结果集。当然，并不仅仅只是结果数据，还包含与该结果相关的其他信息，比如产生该  Cache 的客户端连接的字符集，数据的字符集，客户端连接的 Default Database等。</p>
<p><strong>Query Cache 为什么效率会非常高，即使所有数据都可以 Cache 进内存的情况下，有些时候也不如使用 Query  Cache 的效率高？</strong><br />
Query Cache 的查找，是在 MySQL 接受到客户端请求后在对 Query  进行权限验证之后，SQL 解析之前。也就是说，当 MySQL 接受到客户端的SQL后，仅仅只需要对其进行相应的权限验证后就会通过 Query  Cache 来查找结果，甚至都不需要经过 Optimizer 模块进行执行计划的分析优化，更不许要发生任何存储引擎的交互，减少了大量的磁盘 IO  和 CPU 运算，所以效率非常高。</p>
<p><strong>客户端提交的 SQL 语句大小写对 Query Cache 有影响吗？</strong><br />
有，由于 Query  Cache 在内存中是以 HASH 结构来进行映射，HASH 算法基础就是组成 SQL 语句的字符，所以必须要整个 SQL  语句在字符级别完全一致，才能在 Query Cache 中命中，即使多一个空格也不行。</p>
<p><strong>一个 SQL 语句在 Query Cache 中的内容，在什么情况下会失效？</strong><br />
为了保证 Query  Cache 中的内容与是实际数据绝对一致，当表中的数据有任何变化，包括新增，修改，删除等，都会使所有引用到该表的 SQL 的 Query  Cache 失效。</p>
<p><strong>为什么我的系统在开启了 Query Cache 之后整体性能反而下降了？</strong><br />
当开启了 Query  Cache 之后，尤其是当我们的 query_cache_type 参数设置为 1 以后，MySQL 会对每个 SELECT 语句都进行  Query Cache 查找，查找操作虽然比较简单，但仍然也是要消耗一些 CPU 运算资源的。而由于 Query Cache  的失效机制的特性，可能由于表上的数据变化比较频繁，大量的 Query Cache 频繁的被失效，所以 Query Cache  的命中率就可能比较低下。所以有些场景下，Query Cache 不仅不能提高效率，反而可能造成负面影响。</p>
<p><strong>如何确认一个系统的 Query Cache 的运行是否健康，命中率如何，设置量是否足够？</strong><br />
MySQL  提供了一系列的 Global Status 来记录 Query Cache 的当前状态，具体如下：</p>
<ul>
<li>Qcache_<a href="http://www.iwanna.cn/tags/free/" class="st_tag internal_tag" rel="tag" title="标签 Free 下的日志">free</a>_blocks：目前还处于空闲状态的 Query Cache 中内存 Block 数目</li>
<li>Qcache_free_memory：目前还处于空闲状态的 Query Cache 内存总量</li>
<li>Qcache_hits：Query Cache 命中次数</li>
<li>Qcache_inserts：向 Query Cache 中插入新的 Query Cache 的次数，也就是没有命中的次数</li>
<li>Qcache_lowmem_prunes：当 Query Cache 内存容量不够，需要从中删除老的 Query Cache  以给新的 Cache 对象使用的次数</li>
<li>Qcache_not_cached：没有被 Cache 的 SQL 数，包括无法被 Cache 的 SQL 以及由于  query_cache_type 设置的不会被 Cache 的 SQL</li>
<li>Qcache_queries_in_cache：目前在 Query Cache 中的 SQL 数量</li>
<li>Qcache_total_blocks：Query Cache 中总的 Block 数量</li>
</ul>
<p>可以根据这几个状态计算出 Cache 命中率，计算出 Query Cache 大小设置是否足够，总的来说，我个人不建议将 Query  Cache 的大小设置超过256MB，这也是业界比较常用的做法。<br />
<strong><br />
MySQL Cluster 是否可以使用  Query Cache？</strong><br />
其实在我们的生产环境中也没有使用 MySQL Cluster，所以我也没有在 MySQL  Cluster 环境中使用 Query Cache 的实际经验，只是 MySQL 文档中说明确实可以在 MySQL Cluster 中使用  Query Cache。从 MySQL Cluster 的原理来分析，也觉得应该可以使用，毕竟 SQL 节点和数据节点比较独立，各司其职，只是  Cache 的失效机制会要稍微复杂一点。</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2011. |
<a href="http://www.iwanna.cn/archives/2011/02/27/6350/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2011/02/27/6350/#comments">1条评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2011/02/27/6350/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2011/02/27/6350/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2011/02/27/6350/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2011/02/27/6350/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>NoSQL开篇——为什么要使用NoSQL</title>
		<link>http://www.iwanna.cn/archives/2011/01/18/6225/</link>
		<comments>http://www.iwanna.cn/archives/2011/01/18/6225/#comments</comments>
		<pubDate>Tue, 18 Jan 2011 14:48:03 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=6225</guid>
		<description><![CDATA[【编者按】NoSQL在2010年风生水起，大大小小的Web站点在追求高性能高可靠性方面，不由自主都选择了NoSQL技术作为优先考虑的方面。今年伊始，InfoQ中文站有幸邀请到凤凰网的孙立先生，为大家分享他之于NoSQL方面的经验和体会。 非常荣幸能受邀在InfoQ开辟这样一个关于NoSQL的专栏，InfoQ是我非常尊重的一家技术媒体，同时我也希望借助InfoQ，在国内推动NoSQL的发展，希望跟我一样有兴趣的朋友加入进来。这次的NoSQL专栏系列将先整体介绍NoSQL，然后介绍如何把NoSQL运用到自己的项目中合适的场景中，还会适当地分析一些成功案例，希望有成功使用NoSQL经验的朋友给我提供一些线索和信息。 NoSQL概念 随着web2.0的快速发展，非关系型、分布式数据存储得到了快速的发展，它们不保证关系数据的ACID特性。NoSQL概念在2009年被提了出来。NoSQL最常见的解释是“non-relational”，“Not Only SQL”也被很多人接受。（“NoSQL”一词最早于1998年被用于一个轻量级的关系数据库的名字。） NoSQL被我们用得最多的当数key-value存储，当然还有其他的文档型的、列存储、图型数据库、xml数据库等。在NoSQL概念提出之前，这些数据库就被用于各种系统当中，但是却很少用于web互联网应用。比如cdb、qdbm、bdb数据库。 传统关系数据库的瓶颈 传统的关系数据库具有不错的性能，高稳定型，久经历史考验，而且使用简单，功能强大，同时也积累了大量的成功案例。在互联网领域，MySQL成为了绝对靠前的王者，毫不夸张的说，MySQL为互联网的发展做出了卓越的贡献。 在90年代，一个网站的访问量一般都不大，用单个数据库完全可以轻松应付。在那个时候，更多的都是静态网页，动态交互类型的网站不多。 到了最近10年，网站开始快速发展。火爆的论坛、博客、sns、微博逐渐引领web领域的潮流。在初期，论坛的流量其实也不大，如果你接触网络比较早，你可能还记得那个时候还有文本型存储的论坛程序，可以想象一般的论坛的流量有多大。 Memcached+MySQL 后来，随着访问量的上升，几乎大部分使用MySQL架构的网站在数据库上都开始出现了性能问题，web程序不再仅仅专注在功能上，同时也在追求性能。程序员们开始大量的使用缓存技术来缓解数据库的压力，优化数据库的结构和索引。开始比较流行的是通过文件缓存来缓解数据库压力，但是当访问量继续增大的时候，多台web机器通过文件缓存不能共享，大量的小文件缓存也带了了比较高的IO压力。在这个时候，Memcached就自然的成为一个非常时尚的技术产品。 Memcached作为一个独立的分布式的缓存服务器，为多个web服务器提供了一个共享的高性能缓存服务，在Memcached服务器上，又发展了根据hash算法来进行多台Memcached缓存服务的扩展，然后又出现了一致性hash来解决增加或减少缓存服务器导致重新hash带来的大量缓存失效的弊端。当时，如果你去面试，你说你有Memcached经验，肯定会加分的。 Mysql主从读写分离 由于数据库的写入压力增加，Memcached只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负，大部分网站开始使用主从复制技术来达到读写分离，以提高读写性能和读库的可扩展性。Mysql的master-slave模式成为这个时候的网站标配了。 分表分库 随着web2.0的继续高速发展，在Memcached的高速缓存，MySQL的主从复制，读写分离的基础之上，这时MySQL主库的写压力开始出现瓶颈，而数据量的持续猛增，由于MyISAM使用表锁，在高并发下会出现严重的锁问题，大量的高并发MySQL应用开始使用InnoDB引擎代替MyISAM。同时，开始流行使用分表分库来缓解写压力和数据增长的扩展问题。这个时候，分表分库成了一个热门技术，是面试的热门问题也是业界讨论的热门技术问题。也就在这个时候，MySQL推出了还不太稳定的表分区，这也给技术实力一般的公司带来了希望。虽然MySQL推出了MySQL Cluster集群，但是由于在互联网几乎没有成功案例，性能也不能满足互联网的要求，只是在高可靠性上提供了非常大的保证。 MySQL的扩展性瓶颈 在互联网，大部分的MySQL都应该是IO密集型的，事实上，如果你的MySQL是个CPU密集型的话，那么很可能你的MySQL设计得有性能问题，需要优化了。大数据量高并发环境下的MySQL应用开发越来越复杂，也越来越具有技术挑战性。分表分库的规则把握都是需要经验的。虽然有像淘宝这样技术实力强大的公司开发了透明的中间件层来屏蔽开发者的复杂性，但是避免不了整个架构的复杂性。分库分表的子库到一定阶段又面临扩展问题。还有就是需求的变更，可能又需要一种新的分库方式。 MySQL数据库也经常存储一些大文本字段，导致数据库表非常的大，在做数据库恢复的时候就导致非常的慢，不容易快速恢复数据库。比如1000万4KB大小的文本就接近40GB的大小，如果能把这些数据从MySQL省去，MySQL将变得非常的小。 关系数据库很强大，但是它并不能很好的应付所有的应用场景。MySQL的扩展性差（需要复杂的技术来实现），大数据下IO压力大，表结构更改困难，正是当前使用MySQL的开发人员面临的问题。 NOSQL的优势 易扩展 NoSQL数据库种类繁多，但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系，这样就非常容易扩展。也无形之间，在架构的层面上带来了可扩展的能力。 大数据量，高性能 NoSQL数据库都具有非常高的读写性能，尤其在大数据量下，同样表现优秀。这得益于它的无关系性，数据库的结构简单。一般MySQL使用Query Cache，每次表的更新Cache就失效，是一种大粒度的Cache，在针对web2.0的交互频繁的应用，Cache性能不高。而NoSQL的Cache是记录级的，是一种细粒度的Cache，所以NoSQL在这个层面上来说就要性能高很多了。 灵活的数据模型 NoSQL无需事先为要存储的数据建立字段，随时可以存储自定义的数据格式。而在关系数据库里，增删字段是一件非常麻烦的事情。如果是非常大数据量的表，增加字段简直就是一个噩梦。这点在大数据量的web2.0时代尤其明显。 高可用 NoSQL在不太影响性能的情况，就可以方便的实现高可用的架构。比如Cassandra，HBase模型，通过复制模型也能实现高可用。 总结 NoSQL数据库的出现，弥补了关系数据（比如MySQL）在某些方面的不足，在某些方面能极大的节省开发成本和维护成本。 MySQL和NoSQL都有各自的特点和使用的应用场景，两者的紧密结合将会给web2.0的数据库发展带来新的思路。让关系数据库关注在关系上，NoSQL关注在存储上。 参考阅读 NoSQL：http://nosql-database.org/ NoSQL在wiki上的介绍：http://en.wikipedia.org/wiki/NoSQL NoSQL相关博客：http://nosql.mypopescu.com/ NoSQL相关博客：http://blog.nosqlfan.com/ 新浪微博NoSQL微群：http://q.t.sina.com.cn/127870 关于作者 孙立，目前在凤凰网负责底层组的研发工作。曾就职于搜狐和ku6。多年互联网从业经验和程序开发，对分布式搜索引擎的开发，高并发，大数据量网站系统架构优化，高可用性，可伸缩性，分布式系统缓存,数据库分表分库（sharding）等有丰富的经验，并且对运维监控和自动化运维控制有经验。开源项目phplock，phpbuffer的作者。近期开发了一个NOSQL数据库存储INetDB,是NoSQL数据库爱好者。他的新浪微博是：http://t.sina.com.cn/sunli1223 © 我想网 Akon 所有 , 2011. &#124; 永久链接 &#124; 没有评论 &#124; [...]]]></description>
			<content:encoded><![CDATA[<p>【编者按】NoSQL在2010年风生水起，大大小小的Web站点在追求高性能高可靠性方面，不由自主都选择了NoSQL技术作为优先考虑的方面。今年伊始，InfoQ中文站有幸邀请到凤凰网的孙立先生，为大家分享他之于NoSQL方面的经验和体会。</p>
<hr />非常荣幸能受邀在InfoQ开辟这样一个关于NoSQL的专栏，InfoQ是我非常尊重的一家技术媒体，同时我也希望借助InfoQ，在国内推动NoSQL的发展，希望跟我一样有兴趣的朋友加入进来。这次的NoSQL专栏系列将先整体介绍NoSQL，然后介绍如何把NoSQL运用到自己的项目中合适的场景中，还会适当地分析一些成功案例，希望有成功使用NoSQL经验的朋友给我提供一些线索和信息。</p>
<h2>NoSQL概念</h2>
<p>随着web2.0的快速发展，非关系型、分布式数据存储得到了快速的发展，它们不保证关系数据的ACID特性。<a href="http://nosql-database.org/">NoSQL</a>概念在2009年被提了出来。NoSQL最常见的解释是“non-relational”，“Not  Only SQL”也被很多人接受。（“<a href="http://www.iwanna.cn/tags/nosql/" class="st_tag internal_tag" rel="tag" title="标签 NoSQL 下的日志">NoSQL</a>”一词最早于1998年被用于一个轻量级的关系数据库的名字。）</p>
<p>NoSQL被我们用得最多的当数key-value存储，当然还有其他的文档型的、列存储、图型数据库、xml数据库等。在NoSQL概念提出之前，这些数据库就被用于各种系统当中，但是却很少用于web互联网应用。比如cdb、qdbm、bdb数据库。<br />
<span id="more-6225"></span></p>
<h2>传统关系数据库的瓶颈</h2>
<p>传统的关系数据库具有不错的性能，高稳定型，久经历史考验，而且使用简单，功能强大，同时也积累了大量的成功案例。在互联网领域，MySQL成为了绝对靠前的王者，毫不夸张的说，MySQL为互联网的发展做出了卓越的贡献。</p>
<p>在90年代，一个网站的访问量一般都不大，用单个数据库完全可以轻松应付。在那个时候，更多的都是静态网页，动态交互类型的网站不多。</p>
<p>到了最近10年，网站开始快速发展。火爆的论坛、博客、sns、微博逐渐引领web领域的潮流。在初期，论坛的流量其实也不大，如果你接触网络比较早，你可能还记得那个时候还有文本型存储的论坛程序，可以想象一般的论坛的流量有多大。</p>
<h2>Memcached+<a href="http://www.iwanna.cn/tags/mysql/" class="st_tag internal_tag" rel="tag" title="标签 MySQL 下的日志">MySQL</a></h2>
<p>后来，随着访问量的上升，几乎大部分使用MySQL架构的网站在数据库上都开始出现了性能问题，web程序不再仅仅专注在功能上，同时也在追求性能。程序员们开始大量的使用缓存技术来缓解数据库的压力，优化数据库的结构和索引。开始比较流行的是通过文件缓存来缓解数据库压力，但是当访问量继续增大的时候，多台web机器通过文件缓存不能共享，大量的小文件缓存也带了了比较高的IO压力。在这个时候，Memcached就自然的成为一个非常时尚的技术产品。</p>
<p>Memcached作为一个独立的分布式的缓存服务器，为多个web服务器提供了一个共享的高性能缓存服务，在Memcached服务器上，又发展了根据hash算法来进行多台Memcached缓存服务的扩展，然后又出现了一致性hash来解决增加或减少缓存服务器导致重新hash带来的大量缓存失效的弊端。当时，如果你去面试，你说你有Memcached经验，肯定会加分的。</p>
<h2>Mysql主从读写分离</h2>
<p>由于数据库的写入压力增加，Memcached只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负，大部分网站开始使用主从复制技术来达到读写分离，以提高读写性能和读库的可扩展性。Mysql的master-slave模式成为这个时候的网站标配了。</p>
<h2>分表分库</h2>
<p>随着web2.0的继续高速发展，在Memcached的高速缓存，MySQL的主从复制，读写分离的基础之上，这时MySQL主库的写压力开始出现瓶颈，而数据量的持续猛增，由于MyISAM使用表锁，在高并发下会出现严重的锁问题，大量的高并发MySQL应用开始使用InnoDB引擎代替MyISAM。同时，开始流行使用分表分库来缓解写压力和数据增长的扩展问题。这个时候，分表分库成了一个热门技术，是面试的热门问题也是业界讨论的热门技术问题。也就在这个时候，MySQL推出了还不太稳定的表分区，这也给技术实力一般的公司带来了希望。虽然MySQL推出了MySQL  Cluster集群，但是由于在互联网几乎没有成功案例，性能也不能满足互联网的要求，只是在高可靠性上提供了非常大的保证。</p>
<h2>MySQL的扩展性瓶颈</h2>
<p>在互联网，大部分的MySQL都应该是IO密集型的，事实上，如果你的MySQL是个CPU密集型的话，那么很可能你的MySQL设计得有性能问题，需要优化了。大数据量高并发环境下的MySQL应用开发越来越复杂，也越来越具有技术挑战性。分表分库的规则把握都是需要经验的。虽然有像淘宝这样技术实力强大的公司开发了透明的中间件层来屏蔽开发者的复杂性，但是避免不了整个架构的复杂性。分库分表的子库到一定阶段又面临扩展问题。还有就是需求的变更，可能又需要一种新的分库方式。</p>
<p>MySQL数据库也经常存储一些大文本字段，导致数据库表非常的大，在做数据库恢复的时候就导致非常的慢，不容易快速恢复数据库。比如1000万4KB大小的文本就接近40GB的大小，如果能把这些数据从MySQL省去，MySQL将变得非常的小。</p>
<p>关系数据库很强大，但是它并不能很好的应付所有的应用场景。MySQL的扩展性差（需要复杂的技术来实现），大数据下IO压力大，表结构更改困难，正是当前使用MySQL的开发人员面临的问题。</p>
<h2>NOSQL的优势</h2>
<p><strong>易扩展</strong></p>
<p>NoSQL数据库种类繁多，但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系，这样就非常容易扩展。也无形之间，在架构的层面上带来了可扩展的能力。</p>
<p><strong>大数据量，高性能</strong></p>
<p>NoSQL数据库都具有非常高的读写性能，尤其在大数据量下，同样表现优秀。这得益于它的无关系性，数据库的结构简单。一般MySQL使用Query  Cache，每次表的更新Cache就失效，是一种大粒度的Cache，在针对web2.0的交互频繁的应用，Cache性能不高。而NoSQL的Cache是记录级的，是一种细粒度的Cache，所以NoSQL在这个层面上来说就要性能高很多了。</p>
<p><strong>灵活的数据模型</strong></p>
<p>NoSQL无需事先为要存储的数据建立字段，随时可以存储自定义的数据格式。而在关系数据库里，增删字段是一件非常麻烦的事情。如果是非常大数据量的表，增加字段简直就是一个噩梦。这点在大数据量的web2.0时代尤其明显。</p>
<p><strong>高可用</strong></p>
<p>NoSQL在不太影响性能的情况，就可以方便的实现高可用的架构。比如Cassandra，HBase模型，通过复制模型也能实现高可用。</p>
<h2>总结</h2>
<p>NoSQL数据库的出现，弥补了关系数据（比如MySQL）在某些方面的不足，在某些方面能极大的节省开发成本和维护成本。</p>
<p>MySQL和NoSQL都有各自的特点和使用的应用场景，两者的紧密结合将会给web2.0的数据库发展带来新的思路。让关系数据库关注在关系上，NoSQL关注在存储上。</p>
<p><strong>参考阅读</strong></p>
<ol>
<li>NoSQL：<a href="http://nosql-database.org/">http://nosql-database.org/</a></li>
<li>NoSQL在wiki上的介绍：<a href="http://en.wikipedia.org/wiki/NoSQL">http://en.wikipedia.org/wiki/NoSQL</a></li>
<li>NoSQL相关博客：<a href="http://nosql.mypopescu.com/">http://nosql.mypopescu.com/</a></li>
<li>NoSQL相关博客：<a href="http://blog.nosqlfan.com/">http://blog.nosqlfan.com/</a></li>
<li>新浪微博NoSQL微群：<a href="http://q.t.sina.com.cn/127870">http://q.t.sina.com.cn/127870</a></li>
</ol>
<p><strong>关于作者</strong></p>
<p>孙立，目前在凤凰网负责底层组的研发工作。曾就职于搜狐和ku6。多年互联网从业经验和程序开发，对分布式搜索引擎的开发，高并发，大数据量网站系统架构优化，高可用性，可伸缩性，分布式系统缓存,数据库分表分库（sharding）等有丰富的经验，并且对运维监控和自动化运维控制有经验。开源项目phplock，phpbuffer的作者。近期开发了一个NOSQL数据库存储INetDB,是NoSQL数据库爱好者。他的新浪微博是：<a href="http://t.sina.com.cn/sunli1223">http://t.sina.com.cn/sunli1223</a></p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2011. |
<a href="http://www.iwanna.cn/archives/2011/01/18/6225/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2011/01/18/6225/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2011/01/18/6225/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2011/01/18/6225/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2011/01/18/6225/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2011/01/18/6225/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB 的内部构造 From 《MongoDB The Definitive Guide》</title>
		<link>http://www.iwanna.cn/archives/2010/10/06/5499/</link>
		<comments>http://www.iwanna.cn/archives/2010/10/06/5499/#comments</comments>
		<pubDate>Wed, 06 Oct 2010 15:22:27 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5499</guid>
		<description><![CDATA[今天下载了《MongoDB The Definitive Guide》电子版，浏览了里面的内容，还是挺丰富的。是官网文档实际应用方面的一个补充。和官方文档类似，介绍MongoDB的内容是少之又少，只有在 附录的一个章节中介绍了相关内容。 对于大多数的MongoDB的用户来说，MongoDB就像是一个大黑盒，但是如果你能够了解到 MongoDB内部一些构造的话，将有利于你更好地理解和使用MongoDB。 BSON 在MongoDB中，文 档是对数据的抽象，它被使用在Client端和Server端的交互中。所有的Client端（各种语言的Driver）都会使用这种抽象，它的表现形式 就是我们常说的BSON（Binary JSON ）。 BSON是一个轻量级的二进制数据格式。MongoDB能够使用BSON，并将 BSON作为数据的存储存放在磁盘中。 当Client端要将写入文档，使用查询等等操作时，需要将文档编码为BSON格式，然后再发送 给Server端。同样，Server端的返回结果也是编码为BSON格式再放回给Client端的。 使用BSON格式出于以下3种目 的： 效率 BSON是为效率而设计的，它只需要使用很少的空间。即使在最坏的情况下，BSON格式也比JSON格 式再最好的情况下存储效率高。 传输性 在某些情况下，BSON会牺牲额外的空间让数据的传输更加方便。比如，字符 串的传输的前缀会标识字符串的长度，而不是在字符串的末尾打上结束的标记。这样的传输形式有利于MongoDB修改传输的数据。 性能 最后，BSON格式的编码和解码都是非常快速的。它使用了C风格的数据表现形式，这样在各种语言中都可以高效地使用。 更多关于BSON的介绍，可以参考：http://www.bsonspec.org。 写入 协议 Client端访问Server端使用了轻量级的TCP/IP写入协议。这种协议在MongoDB Wiki中有详细介绍，它其实是在BSON数据上面做了一层简单的包装。比如说，写入数据的命令中包含了1个20字节的消息头（由消息的长度和写 入命令标识组成），需要写入的Collection名称和需要写入的数据。 数据文件 在MongoDB的数据文件 夹中（默认路径是/data/db）由构成数据库的所有文件。每一个数据库都包含一个.ns文件和一些数据文件，其中数据文件会随着数据量的增加而变多。 所以如果有一个数据库名字叫做foo，那么构成foo这个数据库的文件就会由foo.ns，foo.0，foo.1，foo.2等等组成。 数 据文件每新增一次，大小都会是上一个数据文件的2倍，每个数据文件最大2G。这样的设计有利于防止数据量较小的数据库浪费过多的空间，同时又能保证数据量 较大的数据库有相应的空间使用。 MongoDB会使用预分配方式来保证写入性能的稳定（这种方式可以使用&#8211;noprealloc关 闭）。预分配在后台进行，并且每个预分配的文件都用0进行填充。这会让MongoDB始终保持额外的空间和空余的数据文件，从而避免了数据增长过快而带来 的分配磁盘空间引起的阻塞。 名字空间和盘区 每一个数据库都由多个名字空间组成，每一个名字空间存储了相应类型的 数据。数据库中的每一个Collection都有各自对应的名字空间，索引文件同样也有名字空间。所有名字空间的元数据都存储在.ns文件中。 名字空间中的数据在磁盘中分为多个区间，这个叫做盘区。在下图中，foo这个数据库包含3个数据文件，第三个数据文件属于空的预分配文件。头两个 数据文件被分为了相应的盘区对应不同的名字空间。 上图显示了名字空间和盘区的相关特点。每一个 名字空间可以包含多个不同的盘区，这些盘区并不是连续的。与数据文件的增长相同，每一个名字空间对应的盘区大小的也是随着分配的次数不断增长的。这样做的 目的是为了平衡名字空间浪费的空间与保持某一个名字空间中数据的连续性。上图中还有一个需要注意的名字空间：$freelist，这个名字空间用于记录不 再使用的盘区（被删除的Collection或索引）。每当名字空间需要分配新的盘区的时候，都会先查看$freelist是否有大小合适的盘区可以使 用。 内存映射存储引擎 MongoDB目前支持的存储引擎为内存映射引擎。当MongoDB启动的时候，会将所有 [...]]]></description>
			<content:encoded><![CDATA[<p>今天下载了《<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> The Definitive  Guide》电子版，浏览了里面的内容，还是挺丰富的。是官网文档实际应用方面的一个补充。和官方文档类似，介绍MongoDB的内容是少之又少，只有在 附录的一个章节中介绍了相关内容。</p>
<p>对于大多数的MongoDB的用户来说，MongoDB就像是一个大黑盒，但是如果你能够了解到 MongoDB内部一些构造的话，将有利于你更好地理解和使用MongoDB。</p>
<h2>BSON</h2>
<p>在MongoDB中，文 档是对数据的抽象，它被使用在Client端和Server端的交互中。所有的Client端（各种语言的Driver）都会使用这种抽象，它的表现形式 就是我们常说的BSON（Binary JSON ）。</p>
<p>BSON是一个轻量级的二进制数据格式。MongoDB能够使用BSON，并将 BSON作为数据的存储存放在磁盘中。</p>
<p>当Client端要将写入文档，使用查询等等操作时，需要将文档编码为BSON格式，然后再发送 给Server端。同样，Server端的返回结果也是编码为BSON格式再放回给Client端的。<br />
<span id="more-5499"></span><br />
使用BSON格式出于以下3种目 的：</p>
<h3>效率</h3>
<p>BSON是为效率而设计的，它只需要使用很少的空间。即使在最坏的情况下，BSON格式也比JSON格 式再最好的情况下存储效率高。</p>
<h3>传输性</h3>
<p>在某些情况下，BSON会牺牲额外的空间让数据的传输更加方便。比如，字符 串的传输的前缀会标识字符串的长度，而不是在字符串的末尾打上结束的标记。这样的传输形式有利于MongoDB修改传输的数据。</p>
<h3>性能</h3>
<p>最后，BSON格式的编码和解码都是非常快速的。它使用了C风格的数据表现形式，这样在各种语言中都可以高效地使用。</p>
<p><span style="color: #000000;">更多关于BSON的介绍，可以参考：</span><a href="http://www.bsonspec.org/" target="_blank">http://www.bsonspec.org</a>。</p>
<h2>写入 协议</h2>
<p>Client端访问Server端使用了轻量级的TCP/IP写入协议。这种协议在<a href="http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol" target="_blank">MongoDB  Wiki</a>中有详细介绍，它其实是在BSON数据上面做了一层简单的包装。比如说，写入数据的命令中包含了1个20字节的消息头（由消息的长度和写 入命令标识组成），需要写入的Collection名称和需要写入的数据。</p>
<h2>数据文件</h2>
<p>在MongoDB的数据文件 夹中（默认路径是/data/db）由构成数据库的所有文件。每一个数据库都包含一个.ns文件和一些数据文件，其中数据文件会随着数据量的增加而变多。 所以如果有一个数据库名字叫做foo，那么构成foo这个数据库的文件就会由foo.ns，foo.0，foo.1，foo.2等等组成。</p>
<p>数 据文件每新增一次，大小都会是上一个数据文件的2倍，每个数据文件最大2G。这样的设计有利于防止数据量较小的数据库浪费过多的空间，同时又能保证数据量 较大的数据库有相应的空间使用。</p>
<p>MongoDB会使用预分配方式来保证写入性能的稳定（这种方式可以使用&#8211;noprealloc关 闭）。预分配在后台进行，并且每个预分配的文件都用0进行填充。这会让MongoDB始终保持额外的空间和空余的数据文件，从而避免了数据增长过快而带来 的分配磁盘空间引起的阻塞。</p>
<h2>名字空间和盘区</h2>
<p>每一个数据库都由多个名字空间组成，每一个名字空间存储了相应类型的 数据。数据库中的每一个Collection都有各自对应的名字空间，索引文件同样也有名字空间。所有名字空间的元数据都存储在.ns文件中。</p>
<p>名字空间中的数据在磁盘中分为多个区间，这个叫做盘区。在下图中，foo这个数据库包含3个数据文件，第三个数据文件属于空的预分配文件。头两个 数据文件被分为了相应的盘区对应不同的名字空间。</p>
<p><a title="我想网 MongoDB 的内部构造 From 《MongoDB The Definitive Guide》" href="http://images.uheed.com/iwanna/2010/10/06/aaa.jpg" target="_blank"><img title="我想网 MongoDB 的内部构造 From 《MongoDB The Definitive Guide》" src="http://images.uheed.com/iwanna/2010/10/06/aaa.jpg" border="0" alt="我想网 MongoDB 的内部构造 From 《MongoDB The Definitive Guide》" width="644" height="393" /></a></p>
<p>上图显示了名字空间和盘区的相关特点。每一个 名字空间可以包含多个不同的盘区，这些盘区并不是连续的。与数据文件的增长相同，每一个名字空间对应的盘区大小的也是随着分配的次数不断增长的。这样做的 目的是为了平衡名字空间浪费的空间与保持某一个名字空间中数据的连续性。上图中还有一个需要注意的名字空间：$freelist，这个名字空间用于记录不 再使用的盘区（被删除的Collection或索引）。每当名字空间需要分配新的盘区的时候，都会先查看$freelist是否有大小合适的盘区可以使 用。</p>
<h2>内存映射存储引擎</h2>
<p>MongoDB目前支持的存储引擎为内存映射引擎。当MongoDB启动的时候，会将所有 的数据文件映射到内存中，然后操作系统会托管所有的磁盘操作。这种存储引擎有以下几种特点：</p>
<blockquote><p>*  MongoDB中关于内存管理的代码非常精简，毕竟相关的工作已经有操作系统进行托管。<br />
*  MongoDB服务器使用的虚拟内存将非常巨大，并将超过整个数据文件的大小。不用担心，操作系统会去处理这一切。<br />
*  MongoDB无法控制数据写入磁盘的顺序，这样将导致MongoDB无法实现writeahead日志的特性。所以，如果MongoDB希望提供一种 durability的特性（这一特性可以参考我写的关于Cassandra文章：<a title="http://www.cnblogs.com/gpcuster/tag/Cassandra/" href="http://www.cnblogs.com/gpcuster/tag/Cassandra/" target="_blank">http://www.cnblogs.com/gpcuster/tag/Cassandra/</a>）， 需要实现另外一种存储引擎。<br />
*  32位系统的MongoDB服务器每一个Mongod实例只能使用2G的数据文件。这是由于地址指针只能支持32位。</p></blockquote>
<h2>其他</h2>
<p>在《MongoDB The Definitive  Guide》中介绍的MongoDB内部构造只有这么多，如果真要把它说清楚，可能需要另外一本书来专门讲述了。比如内部的JS解析，查询的优化，索引的 建立等等。有兴趣的朋友可以直接参考源代码:)</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/10/06/5499/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/10/06/5499/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/10/06/5499/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/10/06/5499/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/10/06/5499/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/10/06/5499/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL tablename MySQL之alter语句用法总结</title>
		<link>http://www.iwanna.cn/archives/2010/09/26/5401/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/26/5401/#comments</comments>
		<pubDate>Sun, 26 Sep 2010 09:32:41 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[Mysql]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5401</guid>
		<description><![CDATA[MySQL之alter语句用法总结 1：删除列 ALTER TABLE 【表名字】 DROP 【列名称】 2：增加列 ALTER TABLE 【表名字】 ADD 【列名称】 INT NOT NULL COMMENT &#8216;注释说明&#8217; 3：修改列的类型信息 ALTER TABLE 【表名字】 CHANGE 【列名称】【新列名称（这里可以用和原来列同名即可）】 BIGINT NOT NULL COMMENT &#8216;注释说明&#8217; 4：重命名列 ALTER TABLE 【表名字】 CHANGE 【列名称】【新列名称】 BIGINT NOT NULL COMMENT &#8216;注释说明&#8217; 5：重命名表 ALTER TABLE 【表名字】 RENAME 【表新名字】 6：删除表中主键 Alter TABLE 【表名字】 drop primary key 7：添加主键 ALTER [...]]]></description>
			<content:encoded><![CDATA[<p><em><a href="http://www.iwanna.cn/tags/mysql/" class="st_tag internal_tag" rel="tag" title="标签 MySQL 下的日志">MySQL</a></em>之alter语句用法总结</p>
<p>1：删除列</p>
<p>ALTER TABLE 【表名字】 DROP 【列名称】</p>
<p>2：增加列</p>
<p>ALTER TABLE 【表名字】 ADD 【列名称】 INT NOT NULL   COMMENT &#8216;注释说明&#8217;</p>
<p>3：修改列的类型信息</p>
<p>ALTER TABLE 【表名字】 CHANGE 【列名称】【新列名称（这里可以用和原来列同名即可）】 BIGINT NOT NULL   COMMENT &#8216;注释说明&#8217;<br />
<span id="more-5401"></span><br />
4：重命名列</p>
<p>ALTER TABLE 【表名字】 CHANGE 【列名称】【新列名称】 BIGINT NOT NULL   COMMENT &#8216;注释说明&#8217;</p>
<p>5：重命名表</p>
<p>ALTER TABLE 【表名字】 RENAME 【表新名字】</p>
<p>6：删除表中主键</p>
<p>Alter TABLE 【表名字】 drop primary key</p>
<p>7：添加主键</p>
<p>ALTER TABLE sj_resource_charges ADD CONSTRAINT PK_SJ_RESOURCE_CHARGES PRIMARY KEY (resid,resfromid)</p>
<p>8：添加索引</p>
<p>ALTER TABLE sj_resource_charges add index INDEX_NAME (name);</p>
<p>9: 添加唯一限制条件索引</p>
<p>ALTER TABLE sj_resource_charges add unique emp_name2(cardnumber);</p>
<p>10: 删除索引</p>
<p>alter table <em>tablename</em> drop index emp_name;</p>
<p><em>mysql</em> alter 语句用法,添加、修改、删除字段等</p>
<p>//主键</p>
<p>alter table tabelname add new_field_id int(5) unsigned default 0 not null auto_increment ,add primary key (new_field_id);</p>
<p>//增加一个新列</p>
<p>alter table t2 add d timestamp;</p>
<p>alter table infos add ex tinyint not null default &#8217;0&#8242;;</p>
<p>//删除列</p>
<p>alter table t2 drop column c;</p>
<p>//重命名列</p>
<p>alter table t1 change a b integer;</p>
<p>//改变列的类型</p>
<p>alter table t1 change b b bigint not null;</p>
<p>alter table infos change list list tinyint not null default &#8217;0&#8242;;</p>
<p>//重命名表</p>
<p>alter table t1 rename t2;</p>
<p>加索引</p>
<p>mysql&gt; alter table <em>tablename</em> change depno depno int(5) not null;</p>
<p>mysql&gt; alter table <em>tablename</em> add index 索引名 (字段名1[，字段名2 …]);</p>
<p>mysql&gt; alter table <em>tablename</em> add index emp_name (name);</p>
<p>加主关键字的索引</p>
<p>mysql&gt; alter table <em>tablename</em> add primary key(id);</p>
<p>加唯一限制条件的索引</p>
<p>mysql&gt; alter table <em>tablename</em> add unique emp_name2(cardnumber);</p>
<p>删除某个索引</p>
<p>mysql&gt;alter table <em>tablename</em> drop index emp_name;</p>
<p>修改表：</p>
<p>增加字段：</p>
<p>mysql&gt; ALTER TABLE table_name ADD field_name field_type;</p>
<p>修改原字段名称及类型：</p>
<p>mysql&gt; ALTER TABLE table_name CHANGE old_field_name new_field_name field_type;</p>
<p>删除字段：</p>
<p>mysql&gt; ALTER TABLE table_name DROP field_name;</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/26/5401/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/26/5401/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/26/5401/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/26/5401/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/26/5401/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/26/5401/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>深入MongoDB精髓技巧[PPT分享]</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5326/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5326/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:22:35 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Skill]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5326</guid>
		<description><![CDATA[标题貌似有点蛊惑人心，但不得不说，本文分享的PPT，是一篇非同一般的技巧和知识合集。 虽然MongoDB是开源软件，但由于其庞大的代码量和稳定的公司支持，使得深入内核去研究其源码的同学非常之少，对于很多内部机制，仅凭官方文档 的解释很难让我们了解。相信下面的PPT不会让你失望。 MongoDB: tips, trick and hacks View more presentations from Scott Hernandez. © 我想网 Akon 所有 , 2010. &#124; 永久链接 &#124; 没有评论 &#124; 提交到 Google Reader 鲜果 抓虾 Feed enhanced by Better Feed from Ozh]]></description>
			<content:encoded><![CDATA[<p>标题貌似有点蛊惑人心，但不得不说，本文分享的PPT，是一篇非同一般的技巧和知识合集。</p>
<p>虽然MongoDB是开源软件，但由于其庞大的代码量和稳定的公司支持，使得深入内核去研究其源码的同学非常之少，对于很多内部机制，仅凭官方文档 的解释很难让我们了解。相信下面的PPT不会让你失望。</p>
<p><span id="more-5326"></span></p>
<div><strong><a title="MongoDB: tips, trick and  hacks" href="http://www.slideshare.net/scotthernandez/mongodb-tips-trick-and-hacks">MongoDB:  tips, trick and hacks</a></strong><object id="__sse4880424" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="355" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><param name="src" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=mongoseattle-tipstrickandhacks-100731203134-phpapp02&amp;stripped_title=mongodb-tips-trick-and-hacks" /><param name="name" value="__sse4880424" /><param name="allowfullscreen" value="true" /><embed id="__sse4880424" type="application/x-shockwave-flash" width="425" height="355" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=mongoseattle-tipstrickandhacks-100731203134-phpapp02&amp;stripped_title=mongodb-tips-trick-and-hacks" name="__sse4880424" allowscriptaccess="always" allowfullscreen="true"></embed></object></div>
<div id="__ss_4880424">
<div>View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/scotthernandez">Scott Hernandez</a>.</div>
</div>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5326/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5326/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5326/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5326/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5326/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5326/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB系列文章推荐</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5294/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5294/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:16:34 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5294</guid>
		<description><![CDATA[今天才发现这么一系列的学习笔记，绝对算是一个失误。下面是一个长达十节的MongoDB系列文章，几乎可以称作简单的中文文档。非常实用而系统。 MongoDB: 1. Database MongoDB: 2. Basic Usage MongoDB: 3. Schema Design MongoDB: 4. Index MongoDB: 5. Admin MongoDB: 6. Optimization MongoDB: 7. Replication (1) MongoDB: 7. Replication (2) MongoDB: 8. Sharding (1) MongoDB: 8. Sharding (2) MongoDB: 9. Grid FS MongoDB: 10. MapReduce © 我想网 Akon 所有 , 2010. &#124; 永久链接 &#124; 没有评论 &#124; [...]]]></description>
			<content:encoded><![CDATA[<p>今天才发现这么一系列的学习笔记，绝对算是一个失误。下面是一个长达十节的MongoDB系列文章，几乎可以称作简单的中文文档。非常实用而系统。</p>
<ul>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5291/">MongoDB: 1.  Database</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5296/">MongoDB: 2.  Basic Usage</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5297/">MongoDB: 3.  Schema Design</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5300/">MongoDB: 4.  Index</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5301/">MongoDB: 5.  Admin</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5302/">MongoDB: 6.  Optimization</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5307/">MongoDB: 7.  Replication (1)</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5308/">MongoDB: 7.  Replication (2)</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5313/">MongoDB: 8.  Sharding (1)</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5310/">MongoDB: 8.  Sharding (2)</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5316/">MongoDB: 9.  Grid FS</a></li>
<li><a href="http://www.iwanna.cn/archives/2010/09/17/5319/">MongoDB: 10.  MapReduce</a></li>
</ul>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5294/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5294/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5294/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5294/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5294/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5294/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 10. MapReduce</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5319/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5319/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:15:52 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5319</guid>
		<description><![CDATA[在 MongoDB 上使用 Map/Reduce 进行并行 &#8220;统计&#8221; 很容易。 db.runCommand( { mapreduce : &#60;collection&#62;, map : &#60;mapfunction&#62;, reduce : &#60;reducefunction&#62; [, query : &#60;query filter object&#62;] [, sort : &#60;sort the query. useful optimization&#62;] for [, limit : &#60;number of objects to from collection&#62;] return [, out : &#60;output-collection name&#62;] [, keeptemp: &#60; &#124; &#62;] true false [...]]]></description>
			<content:encoded><![CDATA[<p>在 <a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> 上使用 Map/Reduce 进行并行 &#8220;统计&#8221; 很容易。</p>
<pre>db.runCommand(
{
    mapreduce : &lt;collection&gt;,
    map : &lt;mapfunction&gt;,
    reduce : &lt;reducefunction&gt;
    [, query : &lt;query filter object&gt;]
    [, sort : &lt;sort the query.  useful   optimization&gt;] for
    [, limit : &lt;number of objects to   from collection&gt;] return
    [, out : &lt;output-collection name&gt;]
    [, keeptemp: &lt; | &gt;] true false
    [, finalize : &lt;finalizefunction&gt;]
    [, scope : &lt;object where fields go into javascript global scope &gt;]
    [, verbose :  ] true
});</pre>
<p><span id="more-5319"></span><br />
参数说明:</p>
<ul>
<li> mapreduce: 要操作的目标集合。</li>
<li> map: 映射函数 (生成键值对序列，作为 reduce 函数参数)。</li>
<li> reduce: 统计函数。</li>
<li> query: 目标记录过滤。</li>
<li> sort: 目标记录排序。</li>
<li> limit: 限制目标记录数量。</li>
<li> out: 统计结果存放集合 (不指定则使用临时集合，在客户端断开后自动删除)。</li>
<li> keeptemp: 是否保留临时集合。</li>
<li> finalize: 最终处理函数 (对 reduce 返回结果进行最终整理后存入结果集合)。</li>
<li> scope: 向  map、reduce、finalize 导入外部变量。</li>
<li> verbose: 显示详细的时间统计信息。</li>
</ul>
<p>官方文档 有几句话很重要：</p>
<pre>map/reduce is invoked via a <a href="http://www.iwanna.cn/tags/db/" class="st_tag internal_tag" rel="tag" title="标签 Database 下的日志">database</a>.  The <a href="http://www.iwanna.cn/tags/db/" class="st_tag internal_tag" rel="tag" title="标签 Database 下的日志">database</a> creates a temporary collection to hold output of the operation. The collection is command cleaned up when the client connection closes, or when explicitly dropped. Alternatively, one can specify a permanent output collection name. map and reduce functions are written in JavaScript and execute on the server. 

In sharded environments, data processing of map/reduce operations runs in parallel on all shards.

MapReduce jobs on a single mongod process are single threaded. This is due to a design limitation in current JavaScript engines. We are looking into alternatives to solve this issue, but for now if you want to parallelize your MapReduce jobs, you will need to either use sharding or do the aggregation client-side in your code.</pre>
<p>先 准备点简单的数据练练手。</p>
<pre>&gt; for (var i = 0; i &lt; 1000; i++) {
... var u = { name : "user" + i, age : i % 40 + 1, sex : i % 2 };
... db.users.insert(u);
... }

&gt; db.users.ensureIndex({name:1})
&gt; db.users.ensureIndex({age:1})

&gt; db.users.count()
1000

&gt; db.users.find().limit(10)
{ "_id" : ObjectId("4c89bf5b24280691541787b8"), "name" : "user0", "age" : 1, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787b9"), "name" : "user1", "age" : 2, "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787ba"), "name" : "user2", "age" : 3, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787bb"), "name" : "user3", "age" : 4, "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787bc"), "name" : "user4", "age" : 5, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787bd"), "name" : "user5", "age" : 6, "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787be"), "name" : "user6", "age" : 7, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787bf"), "name" : "user7", "age" : 8, "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787c0"), "name" : "user8", "age" : 9, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787c1"), "name" : "user9", "age" : 10, "sex" : 1 }</pre>
<p><strong>1.  Map</strong></p>
<p>Map 函数必须调用 emit(key, value) 返回键值对，使用 this 访问当前待处理的  Document。</p>
<pre>&gt; m = function() { emit(this.age, 1) }

function () {
    emit(this.age, 1);
}</pre>
<p>value 可以使用 JSON Object 传递 (支持多个属性值)。</p>
<p>例如:</p>
<pre>emit(this.age, {count:1})</pre>
<p><strong>2.  Reduce</strong></p>
<p>Reduce 函数接收的参数类似 Group 效果，将 Map 返回的键值序列组合成 { key,  [value1, value2, value3, value...] } 传递给 reduce。</p>
<pre>&gt; r = function(key, values) {
... var x = 0;
... values.forEach(function(v) { x += v });
... return x;
... }

function (key, values) {
    var x = 0;
    values.forEach(function (v) {x += v;});
    return x;
}</pre>
<p>Reduce 函数对这些 values 进行 &#8220;统计&#8221; 操作，返回结果可以使用 JSON Object。</p>
<p><strong>3.  Result</strong></p>
<p>我们不必使用 runCommand，改用  db.&lt;collection&gt;.mapReduce() 更方便一些。</p>
<pre>&gt; res = db.users.mapReduce(m, r)
{
        "result" : "tmp.mr.mapreduce_1284097299_10",
        "timeMillis" : 156,
        "counts" : {
                "input" : 1000,
                "emit" : 1000,
                "output" : 40
        },
        "ok" : 1,
}

&gt; db[res.result].find()
{ "_id" : 1, "value" : 25 }
{ "_id" : 2, "value" : 25 }
{ "_id" : 3, "value" : 25 }
{ "_id" : 4, "value" : 25 }
{ "_id" : 5, "value" : 25 }
{ "_id" : 6, "value" : 25 }
{ "_id" : 7, "value" : 25 }
{ "_id" : 8, "value" : 25 }
{ "_id" : 9, "value" : 25 }
{ "_id" : 10, "value" : 25 }
{ "_id" : 11, "value" : 25 }
{ "_id" : 12, "value" : 25 }
{ "_id" : 13, "value" : 25 }
{ "_id" : 14, "value" : 25 }
{ "_id" : 15, "value" : 25 }
{ "_id" : 16, "value" : 25 }
{ "_id" : 17, "value" : 25 }
{ "_id" : 18, "value" : 25 }
{ "_id" : 19, "value" : 25 }
{ "_id" : 20, "value" : 25 }
has more</pre>
<p>mapReduce() 将结果存储在 &#8220;tmp.mr.mapreduce_1284097299_10&#8243;  临时集合中。</p>
<p><strong>4. Finalize</strong></p>
<p>利用 finalize() 我们可以对 reduce()  的结果做进一步处理。</p>
<pre>&gt; f = function(key, value) { return {age:key, count:value}; }

function (key, value) {
    return {age:key, count:value};
}

&gt; res = db.users.mapReduce(m, r, {finalize:f})
{
        "result" : "tmp.mr.mapreduce_1284098036_26",
        "timeMillis" : 158,
        "counts" : {
                "input" : 1000,
                "emit" : 1000,
                "output" : 40
        },
        "ok" : 1,
}

&gt; db[res.result].find()
{ "_id" : 1, "value" : { "age" : 1, "count" : 25 } }
{ "_id" : 2, "value" : { "age" : 2, "count" : 25 } }
{ "_id" : 3, "value" : { "age" : 3, "count" : 25 } }
{ "_id" : 4, "value" : { "age" : 4, "count" : 25 } }
{ "_id" : 5, "value" : { "age" : 5, "count" : 25 } }
{ "_id" : 6, "value" : { "age" : 6, "count" : 25 } }
{ "_id" : 7, "value" : { "age" : 7, "count" : 25 } }
{ "_id" : 8, "value" : { "age" : 8, "count" : 25 } }
{ "_id" : 9, "value" : { "age" : 9, "count" : 25 } }
{ "_id" : 10, "value" : { "age" : 10, "count" : 25 } }
{ "_id" : 11, "value" : { "age" : 11, "count" : 25 } }
{ "_id" : 12, "value" : { "age" : 12, "count" : 25 } }
{ "_id" : 13, "value" : { "age" : 13, "count" : 25 } }
{ "_id" : 14, "value" : { "age" : 14, "count" : 25 } }
{ "_id" : 15, "value" : { "age" : 15, "count" : 25 } }
{ "_id" : 16, "value" : { "age" : 16, "count" : 25 } }
{ "_id" : 17, "value" : { "age" : 17, "count" : 25 } }
{ "_id" : 18, "value" : { "age" : 18, "count" : 25 } }
{ "_id" : 19, "value" : { "age" : 19, "count" : 25 } }
{ "_id" : 20, "value" : { "age" : 20, "count" : 25 } }
has more</pre>
<p><strong>5. Options</strong></p>
<p>我们还可以添加更多的控制细节。</p>
<pre>&gt; res = db.users.mapReduce(m, r, {query:{age:{$lt:10}}, sort:{name:1}, limit:5})
{
        "result" : "tmp.mr.mapreduce_1284097888_25",
        "timeMillis" : 20,
        "counts" : {
                "input" : 5,
                "emit" : 5,
                "output" : 3
        },
        "ok" : 1,
}

&gt; db[res.result].find()
{ "_id" : 1, "value" : 2 }
{ "_id" : 2, "value" : 2 }
{ "_id" : 3, "value" : 1 }</pre>
<p><strong>6. Example</strong></p>
<p>MapReduce  的作用不仅仅是 &#8220;统计&#8221;，我们可以直接用这种在服务器端高速并发执行机制批量修改数据。</p>
<pre>&gt; m = function() { emit(this._id, this) }

function () {
    emit(this._id, this);
}

&gt; r = function(key, values) {
... update = function(v) {
...     db.users.update({_id:key}, {$inc:{age:1}}, false, false);
... }
... values.forEach(update);
... return key;
... }

function (key, values) {
    update = function (v) {db.users.update({_id:key}, {$inc:{age:1}}, false, false);};
    values.forEach(update);
    return key;
}

&gt; db.users.find().limit(10)
{ "_id" : ObjectId("4c89bf5b24280691541787b8"), "name" : "user0", "age" : 1, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787b9"), "name" : "user1", "age" : 2, "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787ba"), "name" : "user2", "age" : 3, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787bb"), "name" : "user3", "age" : 4, "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787bc"), "name" : "user4", "age" : 5, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787bd"), "name" : "user5", "age" : 6, "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787be"), "name" : "user6", "age" : 7, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787bf"), "name" : "user7", "age" : 8, "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787c0"), "name" : "user8", "age" : 9, "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787c1"), "name" : "user9", "age" : 10, "sex" : 1 }

&gt; res = db.users.mapReduce(m, r, {limit:10})
{
        "result" : "tmp.mr.mapreduce_1284098486_27",
        "timeMillis" : 28,
        "counts" : {
                "input" : 10,
                "emit" : 10,
                "output" : 10
        },
        "ok" : 1,
}

&gt; db.users.find().limit(10)
{ "_id" : ObjectId("4c89bf5b24280691541787b8"), "age" : 2, "name" : "user0", "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787b9"), "age" : 3, "name" : "user1", "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787ba"), "age" : 4, "name" : "user2", "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787bb"), "age" : 5, "name" : "user3", "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787bc"), "age" : 6, "name" : "user4", "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787bd"), "age" : 7, "name" : "user5", "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787be"), "age" : 8, "name" : "user6", "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787bf"), "age" : 9, "name" : "user7", "sex" : 1 }
{ "_id" : ObjectId("4c89bf5b24280691541787c0"), "age" : 10, "name" : "user8", "sex" : 0 }
{ "_id" : ObjectId("4c89bf5b24280691541787c1"), "age" : 11, "name" : "user9", "sex" : 1 }</pre>
<p><strong>7.  PyMongo</strong></p>
<p>最后当然得在 Python 调用一下。</p>
<pre>In [1]: from pymongo import *

In [2]: conn = Connection()

In [3]: db = conn.test

In [4]: m = "function() { emit(this.age, 1); }"

In [5]: r = "function(key, values) { var x = 0; values.forEach(function(v){ x += v }); return x; }"

In [6]: res = db.users.map_reduce(m, r, True)

In [7]: for k in db[res["result"]].find(): print k
   ....:
{u'_id': 1.0, u'value': 24.0}
{u'_id': 2.0, u'value': 25.0}
{u'_id': 3.0, u'value': 25.0}
{u'_id': 4.0, u'value': 25.0}
{u'_id': 5.0, u'value': 25.0}
{u'_id': 6.0, u'value': 25.0}
{u'_id': 7.0, u'value': 25.0}
{u'_id': 8.0, u'value': 25.0}
{u'_id': 9.0, u'value': 25.0}
{u'_id': 10.0, u'value': 25.0}
{u'_id': 11.0, u'value': 26.0}
{u'_id': 12.0, u'value': 25.0}
{u'_id': 13.0, u'value': 25.0}
{u'_id': 14.0, u'value': 25.0}
{u'_id': 15.0, u'value': 25.0}
{u'_id': 16.0, u'value': 25.0}
{u'_id': 17.0, u'value': 25.0}
{u'_id': 18.0, u'value': 25.0}
{u'_id': 19.0, u'value': 25.0}
{u'_id': 20.0, u'value': 25.0}
{u'_id': 21.0, u'value': 25.0}
{u'_id': 22.0, u'value': 25.0}
{u'_id': 23.0, u'value': 25.0}
{u'_id': 24.0, u'value': 25.0}
{u'_id': 25.0, u'value': 25.0}
{u'_id': 26.0, u'value': 25.0}
{u'_id': 27.0, u'value': 25.0}
{u'_id': 28.0, u'value': 25.0}
{u'_id': 29.0, u'value': 25.0}
{u'_id': 30.0, u'value': 25.0}
{u'_id': 31.0, u'value': 25.0}
{u'_id': 32.0, u'value': 25.0}
{u'_id': 33.0, u'value': 25.0}
{u'_id': 34.0, u'value': 25.0}
{u'_id': 35.0, u'value': 25.0}
{u'_id': 36.0, u'value': 25.0}
{u'_id': 37.0, u'value': 25.0}
{u'_id': 38.0, u'value': 25.0}
{u'_id': 39.0, u'value': 25.0}
{u'_id': 40.0, u'value': 25.0}</pre>
<p>附加参数也很容易。</p>
<pre>In [10]: res = db.users.map_reduce(m, r, True, limit=10)

In [11]: res
Out[11]:
{u'counts': {u'emit': 10, u'input': 10, u'output': 10},
 u'ok': 1.0,
 u'result': u'tmp.mr.mapreduce_1284099468_31',
 u'timeMillis': 20}

In [12]: for k in db[res["result"]].find(): print k
   ....:
{u'_id': 2.0, u'value': 1.0}
{u'_id': 3.0, u'value': 1.0}
{u'_id': 4.0, u'value': 1.0}
{u'_id': 5.0, u'value': 1.0}
{u'_id': 6.0, u'value': 1.0}
{u'_id': 7.0, u'value': 1.0}
{u'_id': 8.0, u'value': 1.0}
{u'_id': 9.0, u'value': 1.0}
{u'_id': 10.0, u'value': 1.0}
{u'_id': 11.0, u'value': 1.0}

In [13]: res = db.users.map_reduce(m, r, True, query={"age":{"$lt":20}})

In [14]: res
Out[14]:
{u'counts': {u'emit': 475, u'input': 475, u'output': 19},
 u'ok': 1.0,
 u'result': u'tmp.mr.mapreduce_1284099533_33',
 u'timeMillis': 77}

In [15]: for k in db[res["result"]].find(): print k
   ....:
{u'_id': 1.0, u'value': 24.0}
{u'_id': 2.0, u'value': 25.0}
{u'_id': 3.0, u'value': 25.0}
{u'_id': 4.0, u'value': 25.0}
{u'_id': 5.0, u'value': 25.0}
{u'_id': 6.0, u'value': 25.0}
{u'_id': 7.0, u'value': 25.0}
{u'_id': 8.0, u'value': 25.0}
{u'_id': 9.0, u'value': 25.0}
{u'_id': 10.0, u'value': 25.0}
{u'_id': 11.0, u'value': 26.0}
{u'_id': 12.0, u'value': 25.0}
{u'_id': 13.0, u'value': 25.0}
{u'_id': 14.0, u'value': 25.0}
{u'_id': 15.0, u'value': 25.0}
{u'_id': 16.0, u'value': 25.0}
{u'_id': 17.0, u'value': 25.0}
{u'_id': 18.0, u'value': 25.0}
{u'_id': 19.0, u'value': 25.0}</pre>
<p>更多细节参考官方文档。</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5319/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5319/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5319/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5319/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5319/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5319/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 9. Grid FS</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5316/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5316/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:14:45 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5316</guid>
		<description><![CDATA[GridFS 的文件同样是保存在 db.collection 中，通常使用 fs.files 存储文件元数据信息，fs.chunks 存储文件内容。 存储海量文件，得启用 Auto-Sharding。 &#62; admin.runCommand({ enablesharding: test }) &#62; admin.runCommand({ shardcollection: test.fs.chunks, key:{files_id:1} }) PyMongo API 中有个 GridFS 对象，使用方法很简单。 &#62;&#62;&#62; from pymongo import * &#62;&#62;&#62; from pymongo.objectid import ObjectId &#62;&#62;&#62; from gridfs import * &#62;&#62;&#62; from pprint import pprint &#62;&#62;&#62; conn = Connection() &#62;&#62;&#62; db = conn.test &#62;&#62;&#62; gfs [...]]]></description>
			<content:encoded><![CDATA[<p>GridFS 的文件同样是保存在 db.collection 中，通常使用 fs.files 存储文件元数据信息，fs.chunks  存储文件内容。</p>
<p>存储海量文件，得启用 Auto-Sharding。</p>
<pre>&gt; admin.runCommand({ enablesharding: test })
&gt; admin.runCommand({ shardcollection: test.fs.chunks, key:{files_id:1} })</pre>
<p><a title="http://api.mongodb.org/python/current/index.html" href="http://api.mongodb.org/python/current/index.html" target="_blank">PyMongo</a> API 中有个 <a title="http://api.mongodb.org/python/current/api/gridfs/index.html" href="http://api.mongodb.org/python/current/api/gridfs/index.html" target="_blank">GridFS</a> 对象，使用方法很简单。</p>
<pre>&gt;&gt;&gt; from pymongo import *
&gt;&gt;&gt; from pymongo.objectid import ObjectId
&gt;&gt;&gt; from gridfs import *
&gt;&gt;&gt; from pprint import pprint

&gt;&gt;&gt; conn = Connection()
&gt;&gt;&gt; db = conn.test
&gt;&gt;&gt; gfs = GridFS(db)</pre>
<p>随便找个文件存到 GridFS 中，除了必须的 filename  外，还可以附加任意属性。<br />
<span id="more-5316"></span></p>
<pre>&gt;&gt;&gt; with open("/home/yuhen/a.txt", "r") as file:
...     id = gfs.put(file.read(), filename = "/xxx/xxx/a.txt", upload = "q.yuhen", abc = 123)
...     print id
...
4c85e1f8499b144773000000

&gt;&gt;&gt; gfs.list()
[u'/xxx/xxx/a.txt']</pre>
<p>用 mongo 看看数据库中存储的具体信息。</p>
<pre>$ ./mongo

<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> shell version: 1.6.2
connecting to: test

&gt; show collections
fs.chunks
fs.files
system.indexes

&gt; db.fs.files.find()
{
    "_id" : ObjectId("4c85e1f8499b144773000000"),
    "abc" : 123,
    "chunkSize" : 262144,
    "upload" : "q.yuhen",
    "filename" : "/xxx/xxx/a.txt",
    "length" : 14,
    "uploadDate" : "Tue Sep 07 2010 14:55:52 GMT+0800 (CST)",
    "md5" : "284d1d15a9d681b288cb5915ada39f53"
}

&gt; db.fs.chunks.find()
{
    "_id" : ObjectId("4c85e1f8499b144773000001"),
    "n" : 0,
    "data" : BinData(2,"DgAAAGFiY2VmZywgaGVsbG8K"),
    "files_id" : ObjectId("4c85e1f8499b144773000000")
}</pre>
<p>我们可以用 file_id 读取文件数据(包括文件内容和附加属性)。</p>
<pre>&gt;&gt;&gt; with open("/home/yuhen/a2.txt", "w") as file:
...     out = gfs.get(ObjectId("4c85e1f8499b144773000000"))
...     file.write(out.read())
...     pprint(dir(out))
...     pprint(out._file.items())
...     print out.name
...     print out.upload
...     print out.abc
...

[
 ...,
 '_file',
 '_id',
 'aliases',
 'chunk_size',
 'content_type',
 'length',
 'md5',
 'metadata',
 'name',
 'read',
 'seek',
 'tell',
 'upload_date'
]

[
 (u'abc', 123),
 (u'chunkSize', 262144),
 (u'upload', u'q.yuhen'),
 (u'filename', u'/xxx/xxx/a.txt'),
 (u'length', 14),
 (u'uploadDate', datetime.datetime(2010, 9, 7, 6, 55, 52, 611000)),
 (u'_id', ObjectId('4c85e1f8499b144773000000')),
 (u'md5', u'284d1d15a9d681b288cb5915ada39f53')
]

/xxx/xxx/a.txt
q.yuhen
123</pre>
<p>还可以用 filename 读取文件。在应用中我们通常要确保 filename 唯一性。</p>
<pre>&gt;&gt;&gt; with open("/home/yuhen/a3.txt", "w") as file:
...     out = gfs.get_last_version("/xxx/xxx/a.txt")
...     file.write(out.read())
...     print out.name, out.length
...

/xxx/xxx/a.txt 14</pre>
<p>用相同的 filename  存储同一文件的不同版本是允许的，数据库中会保留所有历史数据。但用 get_last_version(filename)  只能取回最后一次的更新数据，可以直接从 db.fs.files 中查询 file_id 来获取不同版本内容。</p>
<pre>&gt;&gt;&gt; with open("/home/yuhen/a.txt", "r") as file:
...     id = gfs.put(file.read(), filename = "/xxx/xxx/a.txt", upload = "q.yuhen", abc = 456)
...     print id
...
4c85e70a499b144773000004

&gt;&gt;&gt; gfs.list()
[u'/xxx/xxx/a.txt']

&gt;&gt;&gt; with open("/home/yuhen/a3.txt", "w") as file:
...     out = gfs.get_last_version("/xxx/xxx/a.txt")
...     file.write(out.read())
...     print out.name, out.length, out.abc
...
/xxx/xxx/a.txt 22 456

&gt;&gt;&gt; for f in db.fs.files.find({ "filename":"/xxx/xxx/a.txt" }):
...     print f["_id"], f["length"], f["abc"]
...
4c85e70a499b144773000004 22 456
4c85e1f8499b144773000000 14 123</pre>
<p>GridFS 还提供了 exists、delete  等方法。delete 按 file_id 删除，也就是说是文件的最后一个版本。</p>
<pre>&gt;&gt;&gt; def clear():
...     for f in gfs.list():
...         id = gfs.get_last_version(f)._id
...         print id
...         if gfs.exists(id): gfs.delete(id)
...

&gt;&gt;&gt; clear()
4c85e70a499b144773000004

&gt;&gt;&gt; gfs.list()
[u'/xxx/xxx/a.txt']

&gt;&gt;&gt; clear()
4c85e1f8499b144773000000

&gt;&gt;&gt; gfs.list()
[]</pre>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5316/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5316/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5316/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5316/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5316/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5316/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 8. Sharding (2)</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5310/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5310/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:13:23 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5310</guid>
		<description><![CDATA[MongoDB Auto-Sharding 解决了海量存储和动态扩容的问题，但离实际生产环境所需的高可靠(high reliability)、高可用(high availability)还有些距离。 解决方案: Shard: 使用 Replica Sets，确保每个数据节点都具有备份、自动容错转移、自动恢复能力。 Config: 使用 3 个配置服务器，确保元数据完整性(two-phase commit)。 Route: 配合 LVS，实现负载平衡，提高接入性能(high performance)。 以下我们配置一个 Replica Sets + Sharding 测试环境。 装个配置过程建议都用 IP 地址，以免出错。 (1) 首先建好所有的数据库目录。 $ sudo mkdir -p /var/mongodb/10001 $ sudo mkdir -p /var/mongodb/10002 $ sudo mkdir -p /var/mongodb/10003 $ sudo mkdir -p /var/mongodb/10011 $ sudo mkdir -p /var/mongodb/10012 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> Auto-Sharding 解决了海量存储和动态扩容的问题，但离实际生产环境所需的高可靠(high  reliability)、高可用(high availability)还有些距离。</p>
<p>解决方案:</p>
<ul>
<li> Shard: 使用 Replica  Sets，确保每个数据节点都具有备份、自动容错转移、自动恢复能力。</li>
<li> Config: 使用 3  个配置服务器，确保元数据完整性(two-phase commit)。</li>
<li> Route: 配合  LVS，实现负载平衡，提高接入性能(high performance)。</li>
</ul>
<p>以下我们配置一个 Replica Sets +  Sharding 测试环境。<br />
装个配置过程建议都用 IP 地址，以免出错。<br />
<span id="more-5310"></span><br />
(1) 首先建好所有的数据库目录。</p>
<pre>$ sudo mkdir -p /var/mongodb/10001
$ sudo mkdir -p /var/mongodb/10002
$ sudo mkdir -p /var/mongodb/10003

$ sudo mkdir -p /var/mongodb/10011
$ sudo mkdir -p /var/mongodb/10012
$ sudo mkdir -p /var/mongodb/10013

$ sudo mkdir -p /var/mongodb/config1
$ sudo mkdir -p /var/mongodb/config2
$ sudo mkdir -p /var/mongodb/config3</pre>
<p>(2) 配置 Shard Replica Sets。</p>
<pre>$ sudo ./mongod --shardsvr --fork --logpath /dev/null --dbpath /var/mongodb/10001 --port 10001 --nohttpinterface --replSet set1
forked process: 4974
all output going to: /dev/null

$ sudo ./mongod --shardsvr --fork --logpath /dev/null --dbpath /var/mongodb/10002 --port 10002 --nohttpinterface --replSet set1
forked process: 4988
all output going to: /dev/null

$ sudo ./mongod --shardsvr --fork --logpath /dev/null --dbpath /var/mongodb/10003 --port 10003 --nohttpinterface --replSet set1
forked process: 5000
all output going to: /dev/null</pre>
<pre>$ ./mongo --port 10001

MongoDB shell version: 1.6.2
connecting to: 127.0.0.1:10001/test

&gt; cfg = { _id:'set1', members:[
... { _id:0, host:'192.168.1.202:10001' },
... { _id:1, host:'192.168.1.202:10002' },
... { _id:2, host:'192.168.1.202:10003' }
... ]};

&gt; rs.initiate(cfg)
{
        "info" : "Config now saved locally.  Should come online in about a minute.",
        "ok" : 1
}

&gt; rs.status()
{
        "set" : "set1",
        "date" : "Tue Sep 07 2010 10:25:28 GMT+0800 (CST)",
        "myState" : 5,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "yuhen-server64:10001",
                        "health" : 1,
                        "state" : 5,
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "192.168.1.202:10002",
                        "health" : -1,
                        "state" : 6,
                        "uptime" : 0,
                        "lastHeartbeat" : "Thu Jan 01 1970 08:00:00 GMT+0800 (CST)"
                },
                {
                        "_id" : 2,
                        "name" : "192.168.1.202:10003",
                        "health" : -1,
                        "state" : 6,
                        "uptime" : 0,
                        "lastHeartbeat" : "Thu Jan 01 1970 08:00:00 GMT+0800 (CST)"
                }
        ],
        "ok" : 1
}</pre>
<p>配置第二组 Shard Replica Sets。</p>
<pre>$ sudo ./mongod --shardsvr --fork --logpath /dev/null --dbpath /var/mongodb/10011 --port 10011 --nohttpinterface --replSet set2
forked process: 5086
all output going to: /dev/null

$ sudo ./mongod --shardsvr --fork --logpath /dev/null --dbpath /var/mongodb/10012 --port 10012 --nohttpinterface --replSet set2
forked process: 5098
all output going to: /dev/null

$ sudo ./mongod --shardsvr --fork --logpath /dev/null --dbpath /var/mongodb/10013 --port 10013 --nohttpinterface --replSet set2
forked process: 5112
all output going to: /dev/null</pre>
<pre>$ ./mongo --port 10011

MongoDB shell version: 1.6.2
connecting to: 127.0.0.1:10011/test

&gt; cfg = { _id:'set2', members:[
... { _id:0, host:'192.168.1.202:10011' },
... { _id:1, host:'192.168.1.202:10012' },
... { _id:2, host:'192.168.1.202:10013' }
... ]}

&gt; rs.initiate(cfg)
{
        "info" : "Config now saved locally.  Should come online in about a minute.",
        "ok" : 1
}

&gt; rs.status()
{
        "set" : "set2",
        "date" : "Tue Sep 07 2010 10:28:37 GMT+0800 (CST)",
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "yuhen-server64:10011",
                        "health" : 1,
                        "state" : 1,
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "192.168.1.202:10012",
                        "health" : 0,
                        "state" : 6,
                        "uptime" : 0,
                        "lastHeartbeat" : "Tue Sep 07 2010 10:28:36 GMT+0800 (CST)",
                        "errmsg" : "still initializing"
                },
                {
                        "_id" : 2,
                        "name" : "192.168.1.202:10013",
                        "health" : 1,
                        "state" : 5,
                        "uptime" : 1,
                        "lastHeartbeat" : "Tue Sep 07 2010 10:28:36 GMT+0800 (CST)",
                        "errmsg" : "."
                }
        ],
        "ok" : 1
}</pre>
<p>(3) 启动 Config Server。</p>
<p>我们可以只使用 1 个 Config Server，但 3  个理论上更有保障性。</p>
<pre>Chunk information is the main data stored by the config servers.  Each config server has a complete copy of all chunk information.  A two-phase
commit is used to ensure the consistency of the configuration data among the config servers.

If any of the config servers is down, the cluster's meta-data goes read only. However, even in such a failure state, the MongoDB cluster can still
be read from and written to.</pre>
<p>注意！这个不是 Replica Sets，不需要 &#8211;replSet  参数。</p>
<pre>$ sudo ./mongod --configsvr --fork --logpath /dev/null --dbpath /var/mongodb/config1 --port 20000 --nohttpinterface
forked process: 5177
all output going to: /dev/null

$ sudo ./mongod --configsvr --fork --logpath /dev/null --dbpath /var/mongodb/config2 --port 20001 --nohttpinterface
forked process: 5186
all output going to: /dev/null

$ sudo ./mongod --configsvr --fork --logpath /dev/null --dbpath /var/mongodb/config3 --port 20002 --nohttpinterface
forked process: 5195
all output going to: /dev/null</pre>
<pre>$ ps aux | grep configsvr | grep -v grep
root  ./mongod --configsvr --fork --logpath /dev/null --dbpath /var/mongodb/config1 --port 20000 --nohttpinterface
root  ./mongod --configsvr --fork --logpath /dev/null --dbpath /var/mongodb/config2 --port 20001 --nohttpinterface
root  ./mongod --configsvr --fork --logpath /dev/null --dbpath /var/mongodb/config3 --port 20002 --nohttpinterface</pre>
<p>(4)  启动 Route Server。</p>
<p>注意 &#8211;configdb 参数。</p>
<pre>$ sudo ./mongos --fork --logpath /dev/null --configdb "192.168.1.202:20000,192.168.1.202:20001,192.168.1.202:20002"
forked process: 5209
all output going to: /dev/null</pre>
<pre>$ ps aux | grep mongos | grep -v grep
root ./mongos --fork --logpath /dev/null --configdb 192.168.1.202:20000,192.168.1.202:20001,192.168.1.202:20002</pre>
<p>(5)  开始配置 Sharding。</p>
<p>注意 addshard 添加 Replica Sets 的格式。</p>
<pre>$ ./mongo

MongoDB shell version: 1.6.2
connecting to: test

&gt; use admin
switched to db admin

&gt; db.runCommand({ addshard:'set1/192.168.1.202:10001,192.168.1.202:10002,192.168.1.202:10003' })
{ "shardAdded" : "set1", "ok" : 1 }

&gt; db.runCommand({ addshard:'set2/192.168.1.202:10011,192.168.1.202:10012,192.168.1.202:10013' })
{ "shardAdded" : "set2", "ok" : 1 }

&gt; db.runCommand({ enablesharding:'test' })
{ "ok" : 1 }

&gt; db.runCommand({ shardcollection:'test.data', key:{_id:1} })
{ "collectionsharded" : "test.data", "ok" : 1 }

&gt; db.runCommand({ listshards:1 })
{
        "shards" : [
                {
                        "_id" : "set1",
                        "host" : "set1/192.168.1.202:10001,192.168.1.202:10002,192.168.1.202:10003"
                },
                {
                        "_id" : "set2",
                        "host" : "set2/192.168.1.202:10011,192.168.1.202:10012,192.168.1.202:10013"
                }
        ],
        "ok" : 1
}

&gt; printShardingStatus()

--- Sharding Status ---
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      {
        "_id" : "set1",
        "host" : "set1/192.168.1.202:10001,192.168.1.202:10002,192.168.1.202:10003"
      }
      {
        "_id" : "set2",
        "host" : "set2/192.168.1.202:10011,192.168.1.202:10012,192.168.1.202:10013"
      }
  databases:
        { "_id" : "admin", "partitioned" : false, "primary" : "config" }
        { "_id" : "test", "partitioned" : true, "primary" : "set1" }
                test.data chunks:
                        { "_id" : { $minKey : 1 } } --&gt;&gt; { "_id" : { $maxKey : 1 } } on : set1 { "t" : 1000, "i" : 0 }
</pre>
<p>&#8212;- 配置结束 &#8212;&#8212;</p>
<p>OK! 基本搞定，可以测试一下。</p>
<pre>&gt; use test
switched to db test

&gt; db.data.insert({name:1})
&gt; db.data.insert({name:2})
&gt; db.data.insert({name:3})

&gt; db.data.find()
{ "_id" : ObjectId("4c85a6d9ce93b9b1b302ebe7"), "name" : 1 }
{ "_id" : ObjectId("4c85a6dbce93b9b1b302ebe8"), "name" : 2 }
{ "_id" : ObjectId("4c85a6ddce93b9b1b302ebe9"), "name" : 3 }</pre>
<p>至于 为 Route 配置 LVS，可参考 <a title="article.asp?id=1024" href="http://www.rainsts.net/article.asp?id=1024" target="_blank">《LVS 负载均衡》</a>。       ﻿</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5310/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5310/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5310/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5310/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5310/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5310/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 8. Sharding (1)</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5313/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5313/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:12:31 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5313</guid>
		<description><![CDATA[将海量的数据水平或垂直切割，分区存储到多台服务器上是一个最基本的现实需求。从 1.6 版开始，MongoDB Sharding 总算打上了 &#8220;production-ready&#8221; 标记。 MongoDB 的数据分块称为 chunk。每个 chunk 都是 Collection 中一段连续的数据记录，通常最大尺寸是 200MB，超出则生成新的数据块。 要构建一个 MongoDB Sharding Cluster，需要三种角色： Shard Server: mongod 实例，用于存储实际的数据块。 Config Server: mongod 实例，存储了整个 Cluster Metadata，其中包括 chunk 信息。 Route Server: mongos 实例，前端路由，客户端由此接入，且让整个集群看上去像单一进程数据库。 Route 转发请求到实际的目标服务进程，并将多个结果合并回传给客户端。Route 本身并不存储任何数据和状态，仅在启动时从 Config Server 获取信息。Config Server 上的任何变动都会传递给所有的 Route Process。 在实际使用中，为了获取高可用、高 性能的集群方案，我们会将 Shard Server 和 Config Server 部署成 Replica Sets，然后用 [...]]]></description>
			<content:encoded><![CDATA[<p>将海量的数据水平或垂直切割，分区存储到多台服务器上是一个最基本的现实需求。从 1.6 版开始，<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> Sharding 总算打上了  &#8220;production-ready&#8221; 标记。</p>
<p>MongoDB 的数据分块称为 chunk。每个 chunk 都是  Collection 中一段连续的数据记录，通常最大尺寸是 200MB，超出则生成新的数据块。</p>
<p>要构建一个 MongoDB  Sharding Cluster，需要三种角色：</p>
<ul>
<li> <strong>Shard Server</strong>:  mongod 实例，用于存储实际的数据块。</li>
<li> <strong>Config Server</strong>: mongod 实例，存储了整个  Cluster Metadata，其中包括 chunk 信息。</li>
<li> <strong>Route Server</strong>: mongos  实例，前端路由，客户端由此接入，且让整个集群看上去像单一进程数据库。</li>
</ul>
<p>Route  转发请求到实际的目标服务进程，并将多个结果合并回传给客户端。Route 本身并不存储任何数据和状态，仅在启动时从 Config Server  获取信息。Config Server 上的任何变动都会传递给所有的 Route Process。<br />
<span id="more-5313"></span><br />
在实际使用中，为了获取高可用、高 性能的集群方案，我们会将 Shard Server 和 Config Server 部署成 Replica Sets，然后用 LVS 部署多个  Route。</p>
<p>我们先构建一个简单的 Sharding Cluster，以熟悉相关的配置。</p>
<p>(1) 启动 Shard  Server。</p>
<pre>$ sudo mkdir -p /var/mongodb/0
$ sudo mkdir -p /var/mongodb/1

$ sudo ./mongod --shardsvr --port 10000 --dbpath /var/mongodb/0 --fork --logpath /dev/null
forked process: 1414
all output going to: /dev/null

$ sudo ./mongod --shardsvr --port 10001 --dbpath /var/mongodb/1 --fork --logpath /dev/null
forked process: 1424
all output going to: /dev/null</pre>
<p>(2) 启动 Config Server。</p>
<pre>$ sudo mkdir -p /var/mongodb/config

$ sudo ./mongod --configsvr --port 20000 --dbpath /var/mongodb/config --fork --logpath /dev/null
forked process: 1434
all output going to: /dev/null</pre>
<p>(3) 启动 Route Process。</p>
<pre>$ sudo ./mongos --configdb localhost:20000 --fork --logpath /dev/null
forked process: 1443
all output going to: /dev/null</pre>
<p>可以用 &#8211;chunkSize 参数指定分块大小。</p>
<p>(4)  连接到 Route，开始配置。</p>
<pre>$ ./mongo

MongoDB shell version: 1.6.1
connecting to: test

&gt; use admin
switched to db admin

&gt; db.runCommand({ addshard:"localhost:10000" })
{ "shardAdded" : "shard0000", "ok" : 1 }

&gt; db.runCommand({ addshard:"localhost:10001" })
{ "shardAdded" : "shard0001", "ok" : 1 }

&gt; db.runCommand({ enablesharding:"test" })
{ "ok" : 1 }

&gt; db.runCommand({ shardcollection: "test.users", key: { _id:1 }})
{ "collectionsharded" : "test.users", "ok" : 1 }</pre>
<p>addshard: 添加  Shard Server，相关的命令还有 listshards 和 removeshard。<br />
enablesharding:  用于设置可以被分布存储的数据库。<br />
shardcollection: 用于设置具体被切块的集合名称，且必须指定 Shard  Key，系统会自动创建索引。</p>
<p>注: Sharded Collection 只能有一个 unique index，且必须是  shard key。</p>
<p>(5) 相关的管理命令。</p>
<p>listshards 命令列出所有的 Shard Server。</p>
<pre>&gt; db.runCommand({ listshards: 1 })
{
        "shards" : [
                {
                        "_id" : "shard0000",
                        "host" : "localhost:10000"
                },
                {
                        "_id" : "shard0001",
                        "host" : "localhost:10001"
                }
        ],
        "ok" : 1
}</pre>
<p>或用 printShardingStatus 命令查看 Sharding 信息。</p>
<pre>&gt; printShardingStatus()
--- Sharding Status ---
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:10000" }
      { "_id" : "shard0001", "host" : "localhost:10001" }
  databases:
      { "_id" : "admin", "partitioned" : false, "primary" : "config" }
      { "_id" : "test", "partitioned" : true, "primary" : "shard0000" }
           test.users chunks:
               { "_id" : { $minKey : 1 } } --&gt;&gt; { "_id" : { $maxKey : 1 } }
               on : shard0000 { "t" : 1000, "i" : 0 }</pre>
<p>用  db.&lt;collection_name&gt;.stats() 可以查看具体的 Shard 存储信息 (测试数据，与前文无关)。</p>
<pre>&gt; db.users.stats()
{
     "sharded" : true,
     "ns" : "test.users",
     "count" : 1141349,
     "size" : 63914760,
     "avgObjSize" : 55.99931309354106,
     "storageSize" : 97280256,
     "nindexes" : 1,
     "nchunks" : 3,
     "shards" : {
          "shard0000" : {
               "ns" : "test.users",
               "count" : 1013555,
               "size" : 56758296,
               "avgObjSize" : 55.99922648499588,
               "storageSize" : 86089984,
               "numExtents" : 11,
               "nindexes" : 1,
               "lastExtentSize" : 20872960,
               "paddingFactor" : 1,
               "flags" : 1,
               "totalIndexSize" : 41959424,
               "indexSizes" : {
                    "_id_" : 41959424
               },
               "ok" : 1
          },
          "shard0001" : {
               "ns" : "test.users",
               "count" : 127794,
               "size" : 7156464,
               "avgObjSize" : 56,
               "storageSize" : 11182080,
               "numExtents" : 6,
               "nindexes" : 1,
               "lastExtentSize" : 8388608,
               "paddingFactor" : 1,
               "flags" : 1,
               "totalIndexSize" : 5308416,
               "indexSizes" : {
                    "_id_" : 5308416
               },
               "ok" : 1
          }
     },
     "ok" : 1
}</pre>
<p>isdbgrid 用来确认当前是否是 Sharding Cluster。</p>
<pre>&gt; db.runCommand({ isdbgrid:1 })
{ "isdbgrid" : 1, "hostname" : "yuhen-server64", "ok" : 1 }

&gt; db.runCommand({ ismaster:1 })
{ "ismaster" : 1, "msg" : "isdbgrid", "ok" : 1 }</pre>
<p>&#8212;&#8212;&#8212;  华丽的分隔线 &#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>测试一下效果：</p>
<pre>$ ipython

In [1]: from pymongo import *

In [2]: conn = Connection()

In [3]: db = conn.test

In [4]: for i in xrange(2000000):
   ...:     u = dict(name = "user" + str(i))
   ...:     print i, db.users.insert(u)

# 呼啦啦，好多东东啊，喝茶去...

In [5]: db.users.count()
Out[5]: 2000000</pre>
<p>从 test.users chunks 可以看到分块存储的范围。</p>
<pre>&gt; printShardingStatus()
--- Sharding Status ---
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:10000" }
      { "_id" : "shard0001", "host" : "localhost:10001" }
  databases:
      { "_id" : "admin", "partitioned" : false, "primary" : "config" }
      { "_id" : "test", "partitioned" : true, "primary" : "shard0000" }
          test.users chunks:
              { "_id" : { $minKey : 1 } } --&gt;&gt; { "_id" : ObjectId("4c7...000") }
              on : shard0001 { "t" : 2000, "i" : 0 }
              { "_id" : ObjectId("4c7...000") } --&gt;&gt; { "_id" : ObjectId("4c7...d3b") }
              on : shard0000 { "t" : 3000, "i" : 1 }
              { "_id" : ObjectId("4c7...d3b") } --&gt;&gt; { "_id" : { $maxKey : 1 } }
              on : shard0001 { "t" : 3000, "i" : 0 }</pre>
<p>看看存储目录使用情况  (删除了一些信息，便于阅读)。</p>
<pre>$ du -h /var/mongodb | grep -v _tmp

1.2G    /var/mongodb
465M    /var/mongodb/0
465M    /var/mongodb/1
209M    /var/mongodb/config</pre>
<p>更详细一些 (删除了一些信息，便于阅读)。</p>
<pre>$ ls -lhR /var/mongodb

/var/mongodb:
total 12K
drwxr-xr-x 4 root root 4.0K 2010-08-23 19:21 0
drwxr-xr-x 3 root root 4.0K 2010-08-23 19:23 1
drwxr-xr-x 3 root root 4.0K 2010-08-23 18:33 config

/var/mongodb/0:
total 465M
-rwxr-xr-x 1 root root    5 2010-08-23 18:32 mongod.lock
drwxr-xr-x 3 root root 4.0K 2010-08-23 19:21 moveChunk
-rw------- 1 root root  64M 2010-08-23 19:21 test.0
-rw------- 1 root root 128M 2010-08-23 19:21 test.1
-rw------- 1 root root 256M 2010-08-23 19:14 test.2
-rw------- 1 root root  16M 2010-08-23 19:21 test.ns
drwxr-xr-x 2 root root 4.0K 2010-08-23 18:36 _tmp

/var/mongodb/1:
total 465M
-rwxr-xr-x 1 root root    5 2010-08-23 18:32 mongod.lock
-rw------- 1 root root  64M 2010-08-23 19:25 test.0
-rw------- 1 root root 128M 2010-08-23 19:25 test.1
-rw------- 1 root root 256M 2010-08-23 19:23 test.2
-rw------- 1 root root  16M 2010-08-23 19:25 test.ns
drwxr-xr-x 2 root root 4.0K 2010-08-23 19:21 _tmp

/var/mongodb/config:
total 209M
-rw------- 1 root root  64M 2010-08-23 19:26 config.0
-rw------- 1 root root 128M 2010-08-23 18:33 config.1
-rw------- 1 root root  16M 2010-08-23 19:21 config.ns
-rw-r--r-- 1 root root  41K 2010-08-23 19:22 diaglog.4c724e45
-rwxr-xr-x 1 root root    5 2010-08-23 18:32 mongod.lock
drwxr-xr-x 2 root root 4.0K 2010-08-23 18:36 _tmp</pre>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5313/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5313/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5313/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5313/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5313/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5313/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 7. Replication (2)</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5308/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5308/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:10:54 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5308</guid>
		<description><![CDATA[2. Master/Slave Master/Slave 是一种典型的备份方案，MongoDB 支持 &#8220;One Master Multi Salver&#8221; 和 &#8220;Multi Master One Slave&#8221; 等多种部署方式。 先从简单的 &#8220;镜像备份&#8221; 开始。 $ sudo mkdir -p /var/mongodb/0 $ sudo mkdir -p /var/mongodb/1 $ sudo ./mongod --fork --logpath /dev/null --dbpath /var/mongodb/0 --master forked process: 1388 all output going to: /dev/null $ sudo ./mongod --fork --logpath /dev/null --dbpath /var/mongodb/1 --port 27018 [...]]]></description>
			<content:encoded><![CDATA[<p><strong>2. Master/Slave</strong></p>
<p>Master/Slave 是一种典型的备份方案，<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> 支持 &#8220;One  Master Multi Salver&#8221; 和 &#8220;Multi Master One Slave&#8221; 等多种部署方式。</p>
<p>先从简单的  &#8220;镜像备份&#8221; 开始。</p>
<pre>$ sudo mkdir -p /var/mongodb/0
$ sudo mkdir -p /var/mongodb/1

$ sudo ./mongod --fork --logpath /dev/null --dbpath /var/mongodb/0 --master
forked process: 1388
all output going to: /dev/null

$ sudo ./mongod --fork --logpath /dev/null --dbpath /var/mongodb/1 --port 27018 --slave --source localhost:27017 --autoresync
forked process: 1401
all output going to: /dev/null</pre>
<p>autoresync  参数会在系统发生意外情况造成主从数据不同步时，自动启动复制操作 (同步复制 10 分钟内仅执行一次)。除此之外，还可以用  &#8211;slavedelay 设定更新频率(秒)。<br />
<span id="more-5308"></span><br />
使用  isMaster、printReplicationInfo、printSlaveReplicationInfo 等命令获取相关状态信息。</p>
<pre>$ ./mongo 

&gt; db.isMaster()
{ "ismaster" : true, "ok" : 1 }

&gt; db.printReplicationInfo()
configured oplog size:   990MB
log length start to end: 1164secs (0.32hrs)
oplog first event time:  Mon Aug 23 2010 12:23:54 GMT+0800 (CST)
oplog last event time:   Mon Aug 23 2010 12:43:18 GMT+0800 (CST)
now:                     Mon Aug 23 2010 12:43:24 GMT+0800 (CST)

$ ./mongo localhost:27018

&gt; db.isMaster()
{ "ismaster" : false, "ok" : 1 }

&gt; db.printSlaveReplicationInfo()
source:   localhost:27017
         syncedTo: Mon Aug 23 2010 12:43:58 GMT+0800 (CST)
                 = 10secs ago (0hrs)

&gt; show dbs
admin
local
test

&gt; use local
switched to db local

&gt; show collections
me
pair.sync
sources
system.indexes

&gt; db.sources.find()
{ "_id" : ObjectId("4c71f8178d806ad3f54dd89a"), "host" : "localhost:27017", "source" : "main", "syncedTo" : { "t" : 1282538738000, "i" : 1 }, "localLogTs" : { "t" : 0, "i" : 0 } }</pre>
<p>Slave  的配置信息保存在 local.sources 中。</p>
<p>通常我们会使用主从方案实现读写分离，但需要设置 Slave_OK。</p>
<pre>$ ipython

IPython 0.10 -- An enhanced Interactive Python.

In [1]: from pymongo import *

In [2]: m_conn = Connection()

In [3]: s_conn = Connection(host="localhost:27018", slave_okay=True)

In [4]: m_db = m_conn.test

In [5]: s_db = s_conn.test

# ----------------------------------------- #

In [6]: m_db.users.insert({"name":"user3"})
Out[6]: ObjectId('4c71feb0499b140632000000')

In [7]: s_db.users.find_one({"name":"user3"}) # 数据被复制到 Slave
Out[7]: {u'_id': ObjectId('4c71feb0499b140632000000'), u'name': u'user3'}

In [8]: m_db.users.remove({"name":"user3"}) # 删除 Master 数据

In [9]: for u in m_db.users.find(): print u
   ....:
{u'_id': ObjectId('4c71fa4d5e01e1ba6d62398f'), u'name': u'user1'}

In [10]: for u in s_db.users.find(): print u # Slave 记录被同步删除
   ....:
{u'_id': ObjectId('4c71fa4d5e01e1ba6d62398f'), u'name': u'user1'}

# ----------------------------------------- #

In [11]: s_db.users.insert({"name":"userx"}) # 在 Slave 插入数据
Out[11]: ObjectId('4c71ff2c499b140632000001')

In [12]: for u in m_db.users.find(): print u # Master 上无该数据
   ....:
{u'_id': ObjectId('4c71fa4d5e01e1ba6d62398f'), u'name': u'user1'}

In [13]: for u in s_db.users.find(): print u # Slave 同样没有
   ....:
{u'_id': ObjectId('4c71fa4d5e01e1ba6d62398f'), u'name': u'user1'}
</pre>
<p>可见 Slave 读操作正常，但写无效果。</p>
<p>我们还可以部署 &#8220;One Slave Two Master&#8221;  方案。</p>
<pre>$ sudo mkdir -p /var/mongodb/m1
$ sudo mkdir -p /var/mongodb/m2
$ sudo mkdir -p /var/mongodb/s

$ sudo ./mongod --fork --dbpath /var/mongodb/m1 --logpath /dev/null --master
forked process: 1616
all output going to: /dev/null

$ sudo ./mongod --fork --dbpath /var/mongodb/m2 --logpath /dev/null --port 27018 --master
forked process: 1627
all output going to: /dev/null

$ sudo ./mongod --fork --dbpath /var/mongodb/s --logpath /dev/null --port 27019 --slave
forked process: 1638
all output going to: /dev/null</pre>
<p>连接 Slave，添加配置。</p>
<pre>$ ./mongo localhost:27019

MongoDB shell version: 1.6.1
connecting to: localhost:27019/test

&gt; use local
switched to db local

&gt; db.sources.insert({host:"localhost:27017"})
&gt; db.sources.insert({host:"localhost:27018"})

&gt; db.printSlaveReplicationInfo()
source:   localhost:27017
         syncedTo: Mon Aug 23 2010 13:06:03 GMT+0800 (CST)
                 = 17secs ago (0hrs)
source:   localhost:27018
         doing initial sync</pre>
<p>在两台 Master 上添加数据，查看复制效果。</p>
<pre>$ ./mongo localhost:27017

MongoDB shell version: 1.6.1
connecting to: localhost:27017/test

&gt; use db1
switched to db db1

&gt; db.users.insert({name:"user1"})
&gt; db.users.insert({name:"user2"})

&gt; db.users.find()
{ "_id" : ObjectId("4c720211ade34e85c5380eab"), "name" : "user1" }
{ "_id" : ObjectId("4c720214ade34e85c5380eac"), "name" : "user2" }

&gt; exit
bye</pre>
<pre>$ ./mongo localhost:27018

MongoDB shell version: 1.6.1
connecting to: localhost:27018/test

&gt; use db2
switched to db db2

&gt; db.blogs.insert({title:"aaa"})
&gt; db.blogs.insert({title:"bbb"})
&gt; db.blogs.insert({title:"ccc"})

&gt; db.blogs.find()
{ "_id" : ObjectId("4c720236f24118cb2f59218d"), "title" : "aaa" }
{ "_id" : ObjectId("4c720239f24118cb2f59218e"), "title" : "bbb" }
{ "_id" : ObjectId("4c72023cf24118cb2f59218f"), "title" : "ccc" }

&gt; exit
bye</pre>
<pre>$ ./mongo localhost:27019

MongoDB shell version: 1.6.1
connecting to: localhost:27019/test

&gt; show dbs
admin
db1
db2
local

&gt; use db1
switched to db db1

&gt; db.users.find()
{ "_id" : ObjectId("4c720211ade34e85c5380eab"), "name" : "user1" }
{ "_id" : ObjectId("4c720214ade34e85c5380eac"), "name" : "user2" }

&gt; use db2
switched to db db2

&gt; db.blogs.find()
{ "_id" : ObjectId("4c720236f24118cb2f59218d"), "title" : "aaa" }
{ "_id" : ObjectId("4c720239f24118cb2f59218e"), "title" : "bbb" }
{ "_id" : ObjectId("4c72023cf24118cb2f59218f"), "title" : "ccc" }
&gt;</pre>
<p>一切正常。</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5308/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5308/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5308/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5308/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5308/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5308/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 7. Replication (1)</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5307/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5307/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:10:09 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5307</guid>
		<description><![CDATA[最新的 1.6 版总算提供了 Replica Sets，比起有点莫名其妙的 Replica Pairs，这才是高可用集群所需要的。 1. Replica Sets Replica Sets 使用 n 个 Mongod 节点，构建具备自动容错转移(auto-failover)、自动恢复(auto-recovery) 的高可用方案。通常使用 3 个 mongod 实例，或者 2 mongod + 1 arbiter 方案。 (1) 首先启动所需的 Mongod 节点。注意使用 replSet 参数指定 Sets Name。 $ sudo mkdir -p /var/mongodb/0 $ sudo mkdir -p /var/mongodb/1 $ sudo mkdir -p /var/mongodb/2 $ sudo ./mongod --fork [...]]]></description>
			<content:encoded><![CDATA[<p>最新的 1.6 版总算提供了 Replica Sets，比起有点莫名其妙的 Replica Pairs，这才是高可用集群所需要的。</p>
<p><strong>1.  Replica Sets</strong></p>
<p>Replica Sets 使用 n 个 Mongod  节点，构建具备自动容错转移(auto-failover)、自动恢复(auto-recovery) 的高可用方案。通常使用 3 个 mongod  实例，或者 2 mongod + 1 arbiter 方案。</p>
<p>(1) 首先启动所需的 Mongod 节点。注意使用 replSet  参数指定 Sets Name。</p>
<pre>$ sudo mkdir -p /var/<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">mongodb</a>/0
$ sudo mkdir -p /var/<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">mongodb</a>/1
$ sudo mkdir -p /var/<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">mongodb</a>/2

$ sudo ./mongod --fork --logpath /dev/null --dbpath /var/<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">mongodb</a>/0 --port 27017 --replSet myset
forked process: 1166
all output going to: /dev/null

$ sudo ./mongod --fork --logpath /dev/null --dbpath /var/<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">mongodb</a>/1 --port 27018 --replSet myset
forked process: 1173
all output going to: /dev/null

$ sudo ./mongod --fork --logpath /dev/null --dbpath /var/<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">mongodb</a>/2 --port 27019 --replSet myset
forked process: 1180
all output going to: /dev/null</pre>
<p><span id="more-5307"></span><br />
(2) 使用 mongo 配置 Replica Sets。</p>
<pre>$ ./mongo

MongoDB shell version: 1.6.1
connecting to: test

&gt; cfg = { _id: "myset", members: [
... { _id:0, host:"localhost:27017" },
... { _id:1, host:"localhost:27018" },
... { _id:2, host:"localhost:27019" }
... ]}

&gt; rs.initiate(cfg)
{
        "info" : "Config now saved locally.  Should come online in about a minute.",
        "ok" : 1
}

&gt; rs.conf()
{
        "_id" : "myset",
        "version" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "localhost:27017"
                },
                {
                        "_id" : 1,
                        "host" : "localhost:27018"
                },
                {
                        "_id" : 2,
                        "host" : "localhost:27019"
                }
        ]
}</pre>
<p>如此 Replica Sets 就算配置成功。</p>
<p>相关配置数据保存在 local 数据库中。</p>
<pre>&gt; show dbs
admin
local

&gt; use local
switched to db local

&gt; show collections
oplog.rs
slaves
system.indexes
system.replset

&gt; db.system.replset.find()
{ "_id" : "myset", "version" : 1, "members" : [
        {
                "_id" : 0,
                "host" : "localhost:27017"
        },
        {
                "_id" : 1,
                "host" : "localhost:27018"
        },
        {
                "_id" : 2,
                "host" : "localhost:27019"
        }
] }</pre>
<p>oplog.rs 是一个固定长度的 capped collection，用于记录 Replica Sets 操作日志。</p>
<p>(3)  可以用 isMaster 和 status 命令查看 Replica Sets 状态。</p>
<pre>&gt; rs.isMaster()
{
        "ismaster" : true,
        "secondary" : false,
        "hosts" : [
                "localhost:27017",
                "localhost:27019",
                "localhost:27018"
        ],
        "ok" : 1
}

&gt; rs.status()
{
        "set" : "myset",
        "date" : "Sat Aug 21 2010 15:21:13 GMT+0800 (CST)",
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "yuhen-server64:27017",
                        "health" : 1,
                        "state" : 1,
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "localhost:27018",
                        "health" : 1,
                        "state" : 2,
                        "uptime" : 280,
                        "lastHeartbeat" : "Sat Aug 21 2010 15:21:11 GMT+0800 (CST)"
                },
                {
                        "_id" : 2,
                        "name" : "localhost:27019",
                        "health" : 1,
                        "state" : 2,
                        "uptime" : 284,
                        "lastHeartbeat" : "Sat Aug 21 2010 15:21:11 GMT+0800 (CST)"
                }
        ],
        "ok" : 1
}</pre>
<p>在同一时刻，每组 Replica Sets 只有一个  Primary，用于接受写操作。而后会异步复制到其他成员数据库中。一旦 primary 死掉，会自动投票选出接任的 primary  来，原服务器恢复后成为普通成员。如果数据尚未从先前的 primary 复制到成员服务器，有可能会丢失数据。</p>
<p>(4)  为了观察数据复制和容错迁移，我们可以开几个终端，在前台运行 mongod。</p>
<pre>$ sudo ./mongod --port 27017 --dbpath /var/mongodb/0 --replSet myset</pre>
<p>在  mongo 中向 primary (27017) 插入数据。</p>
<pre>&gt; use test
switched to db test

&gt; db.users.insert({name:"user1"})</pre>
<p>会在所有 mongod 输出记录中看到相关信息。</p>
<pre># 27017 #
Sat Aug 21 15:35:50 [conn2] building new index on { _id: 1 } for test.users
Sat Aug 21 15:35:50 [conn2] Buildindex test.users idxNo:0 { name: "_id_", ns: "test.users", key: { _id: 1 } }
Sat Aug 21 15:35:50 [conn2] done for 0 records 0.01secs
Sat Aug 21 15:35:50 [conn12] getmore local.oplog.rs cid:4961370576520295111 getMore: { ts: { $gte: new Date(5507762976880328705) } }  bytes:118 nreturned:1 3782ms
Sat Aug 21 15:35:50 [conn2] insert test.users 1029ms

# 27018 #
Sat Aug 21 15:35:51 [rs_sync] building new index on { _id: 1 } for test.users
Sat Aug 21 15:35:51 [rs_sync] Buildindex test.users idxNo:0 { name: "_id_", ns: "test.users", key: { _id: 1 } }

# 27019 #
Sat Aug 21 15:35:51 [rs_sync] building new index on { _id: 1 } for test.users
Sat Aug 21 15:35:51 [rs_sync] Buildindex test.users idxNo:0 { name: "_id_", ns: "test.users", key: { _id: 1 } }</pre>
<p>我 们用 CTRL + C 关掉 primary mongd。</p>
<pre># 27018 #
Sat Aug 21 15:40:16 [ReplSetHealthPollTask] replSet info localhost:27017 is now down (or slow to respond)
Sat Aug 21 15:40:27 [rs_sync] replSet SECONDARY

# 27019 #
Sat Aug 21 15:40:16 [ReplSetHealthPollTask] replSet info localhost:27017 is now down (or slow to respond)
Sat Aug 21 15:40:16 [rs_sync] replSet syncThread: 10278 dbclient error communicating with server
Sat Aug 21 15:40:16 [rs Manager] replSet info electSelf 2
Sat Aug 21 15:40:16 [rs Manager] replSet PRIMARY</pre>
<p>Mongod 27019  被选为 Primary。</p>
<p>因为 27107 的 socket 关闭，所以 mongo 需要重新连接。</p>
<pre>$ ./mongo localhost:27019

MongoDB shell version: 1.6.1
connecting to: localhost:27018/test

&gt; rs.status()
{
        "set" : "myset",
        "date" : "Sat Aug 21 2010 15:41:40 GMT+0800 (CST)",
        "myState" : 2,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27017",
                        "health" : 0,
                        "state" : 1,
                        "uptime" : 0,
                        "lastHeartbeat" : "Sat Aug 21 2010 15:40:14 GMT+0800 (CST)",
                        "errmsg" : "connect/transport error"
                },
                {
                        "_id" : 1,
                        "name" : "yuhen-server64:27018",
                        "health" : 1,
                        "state" : 2,
                        "self" : true
                },
                {
                        "_id" : 2,
                        "name" : "localhost:27019",
                        "health" : 1,
                        "state" : 1,
                        "uptime" : 486,
                        "lastHeartbeat" : "Sat Aug 21 2010 15:41:38 GMT+0800 (CST)"
                }
        ],
        "ok" : 1
}</pre>
<p>查询先前插入的记录正常。</p>
<pre>&gt; db.users.find()
{ "_id" : ObjectId("4c6f81d58e4719f03d5ccc65"), "name" : "user1" }</pre>
<p>我 们也可以将 mongo 连接到 27018，不过无法查询数据。</p>
<p>重新启动 27017。</p>
<pre># 27017 #
Sat Aug 21 15:44:54 [rs Manager] replSet can't see a majority, will not try to elect self
Sat Aug 21 15:44:56 [ReplSetHealthPollTask] replSet info localhost:27019 is now up
Sat Aug 21 15:44:56 [ReplSetHealthPollTask] replSet info localhost:27018 is now up
Sat Aug 21 15:44:56 [rs_sync] building new index on { _id: 1 } for local.me
Sat Aug 21 15:44:56 [rs_sync] Buildindex local.me idxNo:0 { name: "_id_", ns: "local.me", key: { _id: 1 } }
Sat Aug 21 15:44:56 [rs_sync] done for 0 records 0.002secs
Sat Aug 21 15:44:56 [rs_sync] replSet SECONDARY

# 27018 #
Sat Aug 21 15:44:55 [ReplSetHealthPollTask] replSet info localhost:27017 is now up

# 27019 #
Sat Aug 21 15:44:54 [ReplSetHealthPollTask] replSet info localhost:27017 is now up</pre>
<p>rs.status  中 heartbeat 恢复正常，但不再是 Primary。</p>
<pre>&gt; rs.status()
{
        "set" : "myset",
        "date" : "Sat Aug 21 2010 15:46:44 GMT+0800 (CST)",
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27017",
                        "health" : 1,
                        "state" : 2,
                        "uptime" : 110,
                        "lastHeartbeat" : "Sat Aug 21 2010 15:46:44 GMT+0800 (CST)"
                },
                {
                        "_id" : 1,
                        "name" : "localhost:27018",
                        "health" : 1,
                        "state" : 2,
                        "uptime" : 790,
                        "lastHeartbeat" : "Sat Aug 21 2010 15:46:44 GMT+0800 (CST)"
                },
                {
                        "_id" : 2,
                        "name" : "yuhen-server64:27019",
                        "health" : 1,
                        "state" : 1,
                        "self" : true
                }
        ],
        "ok" : 1
}</pre>
<p>(5) 我们还可以在运行时添加成员。</p>
<pre>$ sudo ./mongod --fork --port 27020 --dbpath /var/mongodb/3 --logpath /dev/null --replSet myset
forked process: 2139
all output going to: /dev/null</pre>
<p>注意: 必须连接到 primary 才能添加成员。</p>
<pre>$ ./mongo localhost:27019
MongoDB shell version: 1.6.1
connecting to: localhost:27020/test

&gt; rs.add("localhost:27029")
{ "ok" : 1 }

&gt; rs.conf()
{
        "_id" : "myset",
        "version" : 2,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "localhost:27017"
                },
                {
                        "_id" : 1,
                        "host" : "localhost:27018"
                },
                {
                        "_id" : 2,
                        "host" : "localhost:27019"
                },
                {
                        "_id" : 3,
                        "host" : "localhost:27020"
                }
        ]
}</pre>
<p>查看状态。</p>
<pre>&gt; rs.status()
{
        "set" : "myset",
        "date" : "Sat Aug 21 2010 15:52:54 GMT+0800 (CST)",
        "myState" : 2,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27017",
                        "health" : 1,
                        "state" : 2,
                        "uptime" : 94,
                        "lastHeartbeat" : "Sat Aug 21 2010 15:52:54 GMT+0800 (CST)"
                },
                {
                        "_id" : 1,
                        "name" : "localhost:27018",
                        "health" : 1,
                        "state" : 1,
                        "uptime" : 94,
                        "lastHeartbeat" : "Sat Aug 21 2010 15:52:54 GMT+0800 (CST)"
                },
                {
                        "_id" : 2,
                        "name" : "yuhen-server64:27019",
                        "health" : 1,
                        "state" : 2,
                        "self" : true
                },
                {
                        "_id" : 3,
                        "name" : "localhost:27020",
                        "health" : 1,
                        "state" : 2,
                        "uptime" : 92,
                        "lastHeartbeat" : "Sat Aug 21 2010 15:52:54 GMT+0800 (CST)"
                }
        ],
        "ok" : 1
}</pre>
<p>(6) 从客户端连接 Replica Sets，需要 drivers 支持。</p>
<pre>$ ipython

Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
IPython 0.10 -- An enhanced Interactive Python.

In [1]: import pymongo

In [2]: conn = pymongo.Connection(host = ["localhost:27017", "localhost:27018", "localhost:27019", "localhost:27020"])

In [3]: db = conn.test

In [4]: for u in db.users.find(): print u
   ...:
{u'_id': ObjectId('4c6f81d58e4719f03d5ccc65'), u'name': u'user1'}
{u'_id': ObjectId('4c6f82ad8e4719f03d5ccc66'), u'name': u'user2'}

In [5]: db.users.insert({"name":"user3"})
Out[5]: ObjectId('4c6f8872499b1408b7000000')</pre>
<p>从其他终端关掉  primary，看看效果。</p>
<pre>In [6]: db.users.count()
---------------------------------------------------------------------------
AutoReconnect                             Traceback (most recent call last)

AutoReconnect: connection closed</pre>
<p>由于连接被中断，引发异常也很正常，不过 pymongo  会自动重新连接，再次操作时一切正常。</p>
<pre>In [7]: db.users.count()
Out[7]: 3

In [8]: for u in db.users.find(): print u
   ...:
{u'_id': ObjectId('4c6f81d58e4719f03d5ccc65'), u'name': u'user1'}
{u'_id': ObjectId('4c6f82ad8e4719f03d5ccc66'), u'name': u'user2'}
{u'_id': ObjectId('4c6f8872499b1408b7000000'), u'name': u'user3'}</pre>
<p>因 此编码时切记需要做异常保护。</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5307/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5307/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5307/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5307/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5307/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5307/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 6. Optimization</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5302/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5302/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:08:13 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5302</guid>
		<description><![CDATA[1. Profiler MongoDB 自带 Profiler，可以非常方便地记录下所有耗时过长操作，以便于调优。 &#62; db.setProfilingLevel(n) n: 0: Off; 1: Log Slow Operations; 2: Log All Operations. 通常我们只关心 Slow Operation，Level 1 默认记录 &#62;100ms 的操作，当然我们也可以自己调整 &#8220;db.setProfilingLevel(2, 300)&#8221;。 Profiler 信息保存在 system.profile (Capped Collection) 中。 准备 1000000 条数据测试一下。 &#62;&#62;&#62; from pymongo import * &#62;&#62;&#62; from random import randint &#62;&#62;&#62; conn = Connection() &#62;&#62;&#62; db = conn.blog &#62;&#62;&#62; [...]]]></description>
			<content:encoded><![CDATA[<p><strong>1. Profiler</strong></p>
<p><a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> 自带 Profiler，可以非常方便地记录下所有耗时过长操作，以便于调优。</p>
<pre>&gt; db.setProfilingLevel(n)

n:
   0: Off;
   1: Log Slow Operations;
   2: Log All Operations.</pre>
<p>通常我们只关心 Slow Operation，Level 1 默认记录  &gt;100ms 的操作，当然我们也可以自己调整 &#8220;db.setProfilingLevel(2, 300)&#8221;。<br />
Profiler  信息保存在 system.profile (Capped Collection) 中。</p>
<p>准备 1000000 条数据测试一下。</p>
<pre>&gt;&gt;&gt; from pymongo import *
&gt;&gt;&gt; from random import randint
&gt;&gt;&gt; conn = Connection()
&gt;&gt;&gt; db = conn.blog

&gt;&gt;&gt; for i in xrange(1000000):
    u = dict(name = "user" + str(i), age = randint(10, 90))
    db.users.insert(u)</pre>
<p>开始调优操作。<br />
<span id="more-5302"></span></p>
<pre>&gt; db.setProfilingLevel(1)
{ "was" : 0, "ok" : 1 }

&gt; db.users.find().sort({age:-1}).limit(10000)
{ "_id" : ObjectId("4c50dc07499b1404c60f42e5"), "age" : 90, "name" : "user165" }
{ "_id" : ObjectId("4c50dc07499b1404c60f42e8"), "age" : 90, "name" : "user168" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4350"), "age" : 90, "name" : "user272" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4358"), "age" : 90, "name" : "user280" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4375"), "age" : 90, "name" : "user309" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4433"), "age" : 90, "name" : "user499" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4480"), "age" : 90, "name" : "user576" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4484"), "age" : 90, "name" : "user580" }
{ "_id" : ObjectId("4c50dc07499b1404c60f44cf"), "age" : 90, "name" : "user655" }
{ "_id" : ObjectId("4c50dc07499b1404c60f44fb"), "age" : 90, "name" : "user699" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4517"), "age" : 90, "name" : "user727" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4688"), "age" : 90, "name" : "user1096" }
{ "_id" : ObjectId("4c50dc07499b1404c60f46a8"), "age" : 90, "name" : "user1128" }
{ "_id" : ObjectId("4c50dc07499b1404c60f46ae"), "age" : 90, "name" : "user1134" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4740"), "age" : 90, "name" : "user1280" }
{ "_id" : ObjectId("4c50dc07499b1404c60f479b"), "age" : 90, "name" : "user1371" }
{ "_id" : ObjectId("4c50dc07499b1404c60f479d"), "age" : 90, "name" : "user1373" }
{ "_id" : ObjectId("4c50dc07499b1404c60f480f"), "age" : 90, "name" : "user1487" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4842"), "age" : 90, "name" : "user1538" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4844"), "age" : 90, "name" : "user1540" }
has more

&gt; db.system.profile.find()
{
    "ts" : "Thu Jul 29 2010 09:47:47 GMT+0800 (CST)",
    "info" : "query blog.users
        ntoreturn:10000 scanAndOrder
        reslen:518677
        nscanned:1000000
        query: { query: {}, orderby: { age: -1.0 } }
        nreturned:10000 1443ms",
    "millis" : 1443
}</pre>
<p>system.profile 中记录下一条耗时过长的操作。</p>
<ul>
<li> ts:  操作执行时间。</li>
<li> info: 操作详细信息。</li>
<li> info.query: 查询目标(数据库.集合)。</li>
<li> info.ntoreturn: 客户端期望返回的文档数量。</li>
<li> info.nscanned: 服务器实际扫描的文档数量。</li>
<li> info.reslen: 查询结果字节长度。</li>
<li> info.nreturnned: 查询返回文档数。</li>
<li> millis: 操作耗时(毫秒)。</li>
</ul>
<p>很显然，该操作扫描的文档过多(info.nscanned)，通常是没有使用索引造成的。我们 用 explain() 看看服务器如何执行执行该命令。</p>
<pre>&gt; db.users.find().sort({age:-1}).limit(10000).explain()
{
        "cursor" : "BasicCursor",
        "nscanned" : 1000000,
        "nscannedObjects" : 1000000,
        "n" : 10000,
        "scanAndOrder" : true,
        "millis" : 1412,
        "indexBounds" : {

        }
}</pre>
<p>没有索引自然很慢了，建个索引看看效果。</p>
<pre>&gt; db.users.ensureIndex({age:-1})

&gt; db.users.find().sort({age:-1}).limit(10000).explain()
{
        "cursor" : "BtreeCursor age_-1",
        "nscanned" : 10000,
        "nscannedObjects" : 10000,
        "n" : 10000,
        "millis" : 211,
        "indexBounds" : {
                "age" : [
                        [
                                {
                                        "$maxElement" : 1
                                },
                                {
                                        "$minElement" : 1
                                }
                        ]
                ]
        }
}</pre>
<p>速度提升非常明显。最后别忘了 Profiler 本身也会影响服务器性能，不用的时候要关掉。</p>
<pre>&gt; db.setProfilingLevel(0)
{ "was" : 1, "ok" : 1 }</pre>
<p>除了使用 setProfilingLevel 命令外，也可以在 mongod  参数中启用 profiler，不推荐。</p>
<pre>--profile arg             0=off 1=slow, 2=all
--slowms arg (=100)       value of slow for profile and console log</pre>
<p><strong>2.  Optimization</strong></p>
<p>优化建议:</p>
<ul>
<li> 如果 nscanned 远大于  nreturned，那么需要使用索引。</li>
<li> 如果 reslen 返回字节非常大，那么考虑只获取所需的字段。</li>
<li> 执行  update 操作时同样检查一下 nscanned，并使用索引减少文档扫描数量。</li>
<li> 使用 db.eval()  在服务端执行某些统计操作。</li>
<li> 减少返回文档数量，使用 skip &amp; limit 分页。</li>
</ul>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5302/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5302/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5302/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5302/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5302/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5302/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 5. Admin</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5301/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5301/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:06:39 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5301</guid>
		<description><![CDATA[Mongod 是 MongoDB 核心程序，通常情况下我们只需折腾该程序即可。 1. dbpath &#38; port 默 认数据存储路径是 /data/db，默认端口 27017，默认 HTTP 端口 28017。用 &#8211;dbpath 和 &#8211;port 改吧。 $ sudo ./mongod --dbpath /var/mongodb --port 1234 Sat Jul 24 22:58:50 MongoDB starting : pid=1683 port=1234 dbpath=/var/mongodb 64-bit ** NOTE: This is a development version (1.5.4) of MongoDB. ** Not recommended for production. Sat Jul 24 [...]]]></description>
			<content:encoded><![CDATA[<p>Mongod 是 <a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> 核心程序，通常情况下我们只需折腾该程序即可。</p>
<p><strong>1. dbpath &amp; port</strong></p>
<p>默 认数据存储路径是 /data/db，默认端口 27017，默认 HTTP 端口 28017。用 &#8211;dbpath 和 &#8211;port 改吧。</p>
<pre>$ sudo ./mongod --dbpath /var/mongodb --port 1234

Sat Jul 24 22:58:50 MongoDB starting : pid=1683 port=1234 dbpath=/var/mongodb 64-bit

** NOTE: This is a development version (1.5.4) of MongoDB.
**       Not recommended for production.

Sat Jul 24 22:58:50 db version v1.5.4, pdfile version 4.5
Sat Jul 24 22:58:50 git version: 6c1361df41d9cabf9026364427a7df44b3c304fd
Sat Jul 24 22:58:50 sys info: Linux domU-12-31-39-06-79-A1 2.6.21.7-2.ec2.v1.2.fc8xen
[initandlisten] Sat Jul 24 22:58:50 waiting for connections on port 1234
[websvr] Sat Jul 24 22:58:50 web admin interface listening on port 2234</pre>
<p>从启动信息可以看到  Web 管理端口 2234，CTRL + C 即可停止该进程。&#8211;bind_ip 用于设定监听绑定 IP。<br />
<span id="more-5301"></span></p>
<pre>Sat Jul 24 22:58:53 got kill or ctrl c signal 2 (Interrupt), will terminate after current cmd ends
[interruptThread] Sat Jul 24 22:58:53 now exiting
Sat Jul 24 22:58:53  dbexit:
[interruptThread] Sat Jul 24 22:58:53    shutdown: going to close listening sockets...
[interruptThread] Sat Jul 24 22:58:53    going to close listening socket: 5
[interruptThread] Sat Jul 24 22:58:53    going to close listening socket: 6
[interruptThread] Sat Jul 24 22:58:53    going to close listening socket: 7
[interruptThread] Sat Jul 24 22:58:53    going to close listening socket: 8
[interruptThread] Sat Jul 24 22:58:53    shutdown: going to flush oplog...
[interruptThread] Sat Jul 24 22:58:53    shutdown: going to close sockets...
[interruptThread] Sat Jul 24 22:58:53    shutdown: waiting for fs preallocator...
[interruptThread] Sat Jul 24 22:58:53    shutdown: closing all files...
Sat Jul 24 22:58:53      closeAllFiles() finished
[interruptThread] Sat Jul 24 22:58:53    shutdown: removing fs lock...
Sat Jul 24 22:58:53  dbexit: really exiting now</pre>
<p>默认情况下，所有的 DB  都存储到 &#8211;dbpath 指定的目录中。</p>
<pre>$ sudo ./mongod --dbpath /var/mongodb --fork --logpath /dev/null
forked process: 2018
all output going to: /dev/null

$ ./mongo
MongoDB shell version: 1.5.4
connecting to: test
&gt; db.c1.insert({a:1})
&gt; use blog
switched to db blog
&gt; db.c2.insert({b:1})
&gt; exit
bye

$ ls /var/mongodb/
blog  blog.0  blog.1  blog.ns  mongod.lock  test  test.0  test.1  test.ns  _tmp

$ sudo kill -INT 2018</pre>
<p>可以用 &#8211;directoryperdb 参数让系统为每个 DB  创建一个独立子目录。</p>
<pre>$ sudo ./mongod --dbpath /var/mongodb --fork --logpath /dev/null --directoryperdb
forked process: 2060
all output going to: /dev/null

$ ./mongo
MongoDB shell version: 1.5.4
connecting to: test
&gt; db.c1.insert({a:1})
&gt; use blog
switched to db blog
&gt; db.c2.insert({b:1})
&gt; exit
bye

$ ls -R /var/mongodb
/var/mongodb:
blog  mongod.lock  test  _tmp

/var/mongodb/blog:
blog.0  blog.1  blog.ns

/var/mongodb/test:
test.0  test.1  test.ns

/var/mongodb/_tmp:</pre>
<p><strong>2. daemon</strong></p>
<p>如果想以 Daemon  方式运行，需要同时使用 &#8211;fork、&#8211;logpath 参数。</p>
<pre>$ sudo ./mongod --dbpath /var/mongodb --fork --logpath /dev/null

forked process: 1797
all output going to: /dev/null</pre>
<p>想要停止服务，可以发送 INT 或 TERM 信号。</p>
<pre>$ sudo kill -INT 1797</pre>
<p>或 者使用 mongo 连接到服务器，然后执行 shutdownServer 命令。</p>
<pre>$ ./mongo

MongoDB shell version: 1.5.4
connecting to: test

&gt; use admin
switched to db admin

&gt; db.shutdownServer()
Sat Jul 24 23:11:36 query failed : admin.$cmd { shutdown: 1.0 } to: 127.0.0.1
server should be down...
Sat Jul 24 23:11:36 trying reconnect to 127.0.0.1
Sat Jul 24 23:11:36 reconnect 127.0.0.1 failed couldn't connect to server {ip: "127.0.0.1", port: 27017}
Sat Jul 24 23:11:36 MessagingPort say send() errno:9 Bad file descriptor 127.0.0.1:27017
Sat Jul 24 23:11:36 Error: error doing query: unknown (anon):1421

&gt; exit
bye</pre>
<p><strong>3. logging</strong></p>
<p>与 Logging 有关的参数除了 &#8211;logpath，还有  &#8211;logappend 和 &#8211;verbose。</p>
<p>默认情况下，Logging 是覆盖模式(overwrite)，通过  &#8211;logappend 可以添加模式记录日志。<br />
参数 &#8211;verbose 设置记录等级，相当于 -v，更多的级别包括 -vv 直到  -vvvvv。与之相对的是  &#8211;quiet，生成最少的日志信息。<br />
还可以用 &#8211;cpu 记录 CPU 的相关信息。</p>
<pre>$ sudo ./mongod --dbpath /var/mongodb -vvvvv

Sun Jul 25 00:41:22 MongoDB starting : pid=2113 port=27017 dbpath=/var/mongodb 64-bit

** NOTE: This is a development version (1.5.4) of MongoDB.
**       Not recommended for production.

Sun Jul 25 00:41:22 db version v1.5.4, pdfile version 4.5
Sun Jul 25 00:41:22 git version: 6c1361df41d9cabf9026364427a7df44b3c304fd
Sun Jul 25 00:41:22 sys info: Linux domU-12-31-39-06-79-A1 2.6.21.7-2.ec2.v1.2.fc8xen #1 ...
[initandlisten] Sun Jul 25 00:41:22 query: local.system.namespaces{ name: /^local.temp./ }
[initandlisten] Sun Jul 25 00:41:22 Accessing: local for the first time
[initandlisten] Sun Jul 25 00:41:22    used cursor: 0x1300d30
[initandlisten] Sun Jul 25 00:41:22 query local.system.namespaces reslen:36 nreturned:0 0ms
[initandlisten] Sun Jul 25 00:41:22 enter repairDatabases
[initandlisten] Sun Jul 25 00:41:22 done repairDatabases
[initandlisten] Sun Jul 25 00:41:22 waiting for connections on port 27017
[websvr] Sun Jul 25 00:41:22 web admin interface listening on port 28017
[initandlisten] Sun Jul 25 00:41:30 connection accepted from 127.0.0.1:46032 #1
[conn1] Sun Jul 25 00:41:30 query: admin.$cmd{ whatsmyuri: 1 }
[conn1] Sun Jul 25 00:41:30 run command admin.$cmd { whatsmyuri: 1 }
[conn1] Sun Jul 25 00:41:30 query admin.$cmd ntoreturn:1 command: { whatsmyuri: 1 } reslen:71 0ms
[DataFileSync] Sun Jul 25 00:42:22 flushing mmap took 0ms
[DataFileSync] Sun Jul 25 00:43:22 flushing mmap took 0ms
[DataFileSync] Sun Jul 25 00:44:22 flushing mmap took 0ms

^C

Sun Jul 25 00:45:22 got kill or ctrl c signal 2 (Interrupt), will terminate after current cmd ends
[interruptThread] Sun Jul 25 00:45:22 now exiting
Sun Jul 25 00:45:22  dbexit:
[interruptThread] Sun Jul 25 00:45:22    shutdown: going to close listening sockets...
[interruptThread] Sun Jul 25 00:45:22    going to close listening socket: 5
[interruptThread] Sun Jul 25 00:45:22    going to close listening socket: 6
[interruptThread] Sun Jul 25 00:45:22    going to close listening socket: 7
[interruptThread] Sun Jul 25 00:45:22    going to close listening socket: 8
[interruptThread] Sun Jul 25 00:45:22    shutdown: going to flush oplog...
[interruptThread] Sun Jul 25 00:45:22    shutdown: going to close sockets...
[interruptThread] Sun Jul 25 00:45:22    shutdown: waiting for fs preallocator...
[interruptThread] Sun Jul 25 00:45:22    shutdown: closing all files...
Sun Jul 25 00:45:22      closeAllFiles() finished
[interruptThread] Sun Jul 25 00:45:22    shutdown: removing fs lock...
Sun Jul 25 00:45:22  dbexit: really exiting now</pre>
<p><strong>4.  configuration file</strong></p>
<p>如果嫌命令行参数太长，可以考虑使用配置文件。</p>
<pre>$ cat test.conf
dbpath = /var/mongodb
logpath = /var/test.log
logappend = true
fork = true
port = 1234

$ sudo ./mongod --config test.conf
forked process: 2262
all output going to: /var/test.log</pre>
<p><strong>5. db.serverStatus</strong></p>
<p>在  mongo 中执行 admin.serverStatus() 命令可以获取 MongoDB 的运行统计信息。</p>
<pre>$ ./mongo

MongoDB shell version: 1.5.4
connecting to: test

&gt; use admin
switched to db admin

&gt; db.serverStatus()
{
        "version" : "1.5.4",
        "uptime" : 23,
        "uptimeEstimate" : 0,
        "localTime" : "Sun Jul 25 2010 01:07:18 GMT+0800 (CST)",
        "globalLock" : {
                "totalTime" : 23074558,
                "lockTime" : 470,
                "ratio" : 0.000020368754192387997
        },
        "mem" : {
                "bits" : 64,
                "resident" : 2,
                "virtual" : 75,
                "supported" : true,
                "mapped" : 0
        },
        "connections" : {
                "current" : 1,
                "available" : 19999
        },
        "extra_info" : {
                "note" : "fields vary by platform",
                "heap_usage_bytes" : 162672,
                "page_faults" : 0
        },
        "indexCounters" : {
                "btree" : {
                        "accesses" : 0,
                        "hits" : 0,
                        "misses" : 0,
                        "resets" : 0,
                        "missRatio" : 0
                }
        },
        "backgroundFlushing" : {
                "flushes" : 0,
                "total_ms" : 0,
                "average_ms" : 0,
                "last_ms" : 0,
                "last_finished" : "Thu Jan 01 1970 08:00:00 GMT+0800 (CST)"
        },
        "opcounters" : {
                "insert" : 0,
                "query" : 1,
                "update" : 0,
                "delete" : 0,
                "getmore" : 0,
                "command" : 2
        },
        "asserts" : {
                "regular" : 0,
                "warning" : 0,
                "msg" : 0,
                "user" : 0,
                "rollovers" : 0
        },
        "ok" : true
}</pre>
<p>相关字段说明：</p>
<ul>
<li> uptime: 服务器运行时间(秒)。</li>
<li> localTime: 服务器本地时间。</li>
<li> mem: 服务器内存信息。</li>
<li> connections: 当前连接数。</li>
<li> opcounters: 操作统计。</li>
</ul>
<p><strong>6. db.stats</strong></p>
<p>db.stats  查看数据库状态信息。</p>
<pre>&gt; db.stats()
{
        "collections" : 5,
        "objects" : 18,
        "avgObjSize" : 50.888888888888886,
        "dataSize" : 916,
        "storageSize" : 26112,
        "numExtents" : 5,
        "indexes" : 5,
        "indexSize" : 40960,
        "fileSize" : 201326592,
        "ok" : 1
}</pre>
<p><strong>7. http console</strong></p>
<p>Mongod 默认会打开一个 HTTP  监听端口，通过浏览器我们能获取 MongoDB 服务器的相关状态信息。如果不想启动 HTTP Listening，可以使用  &#8211;nohttpinterface 参数。</p>
<pre>$ sudo ./mongod --dbpath /var/mongodb --nohttpinterface

Sun Jul 25 01:46:53 MongoDB starting : pid=2406 port=27017 dbpath=/var/mongodb 64-bit

** NOTE: This is a development version (1.5.4) of MongoDB.
**       Not recommended for production.

Sun Jul 25 01:46:53 db version v1.5.4, pdfile version 4.5
Sun Jul 25 01:46:53 git version: 6c1361df41d9cabf9026364427a7df44b3c304fd
Sun Jul 25 01:46:53 sys info: Linux domU-12-31-39-06-79-A1 2.6.21.7-2.ec2.v1.2.fc8xen #1 ...
[initandlisten] Sun Jul 25 01:46:53 waiting for connections on port 27017

^C

Sun Jul 25 01:47:19 got kill or ctrl c signal 2 (Interrupt), will terminate after current cmd ends
[interruptThread] Sun Jul 25 01:47:19 now exiting
Sun Jul 25 01:47:19  dbexit:
[interruptThread] Sun Jul 25 01:47:19    shutdown: going to close listening sockets...
[interruptThread] Sun Jul 25 01:47:19    going to close listening socket: 5
[interruptThread] Sun Jul 25 01:47:19    going to close listening socket: 6
[interruptThread] Sun Jul 25 01:47:19    shutdown: going to flush oplog...
[interruptThread] Sun Jul 25 01:47:19    shutdown: going to close sockets...
[interruptThread] Sun Jul 25 01:47:19    shutdown: waiting for fs preallocator...
[interruptThread] Sun Jul 25 01:47:19    shutdown: closing all files...
Sun Jul 25 01:47:19      closeAllFiles() finished
[interruptThread] Sun Jul 25 01:47:19    shutdown: removing fs lock...
Sun Jul 25 01:47:19  dbexit: really exiting now</pre>
<p>除了用浏览器查看状态信息外，还 可以使用 &#8211;rest 参数打开 RESTful 操作。</p>
<pre>$ sudo ./mongod --dbpath /var/mongodb --rest --fork --logpath /dev/null
forked process: 2451
all output going to: /dev/null

$ curl <a title="http://localhost:28017/serverStatus?text" href="http://localhost:28017/serverStatus?text" target="_blank">http://localhost:28017/serverStatus?text</a>
{ "version" : "1.5.4",
  "uptime" : 13,
  "uptimeEstimate" : 0,
  "localTime" : Date( "Thu May 24 14:12:39 4253" ),
  "globalLock" : { "totalTime" : 13207178,
    "lockTime" : 400,
    "ratio" : 3.028656083835623e-05 },
  "mem" : { "bits" : 64,
    "resident" : 2,
    "virtual" : 67,
    "supported" : true,
    "mapped" : 0 },
  "connections" : { "current" : 0,
    "available" : 20000 },
  "extra_info" : { "note" : "fields vary by platform",
    "heap_usage_bytes" : 158000,
    "page_faults" : 0 },
  "indexCounters" : { "btree" : { "accesses" : 0,
      "hits" : 0,
      "misses" : 0,
      "resets" : 0,
      "missRatio" : 0 } },
  "backgroundFlushing" : { "flushes" : 0,
    "total_ms" : 0,
    "average_ms" : 0,
    "last_ms" : 0,
    "last_finished" : Date( 0 ) },
  "opcounters" : { "insert" : 0,
    "query" : 1,
    "update" : 0,
    "delete" : 0,
    "getmore" : 0,
    "command" : 0 },
  "asserts" : { "regular" : 0,
    "warning" : 0,
    "msg" : 0,
    "user" : 0,
    "rollovers" : 0 } }

$ curl <a title="http://localhost:28017/blog/users/" href="http://localhost:28017/blog/users/" target="_blank">http://localhost:28017/blog/users/</a>
{
  "offset" : 0,
  "rows": [
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e53" }, "name" : "user0" } ,
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e54" }, "name" : "user1" } ,
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e55" }, "name" : "user2" } ,
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e56" }, "name" : "user3" } ,
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e57" }, "name" : "user4" } ,
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e58" }, "name" : "user5" } ,
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e59" }, "name" : "user6" } ,
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e5a" }, "name" : "user7" } ,
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e5b" }, "name" : "user8" } ,
    { "_id" : { "$oid" : "4c4b2998a09020a0e7681e5c" }, "name" : "user9" }
  ],

  "total_rows" : 10 ,
  "query" : {} ,
  "millis" : 0
}</pre>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5301/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5301/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5301/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5301/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5301/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5301/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 4. Index</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5300/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5300/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:05:41 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5300</guid>
		<description><![CDATA[MongoDB 提供了多样性的索引支持。 &#62; for (var i = 0; i &#60; 30; i++) { ... u = { name : "user" + i, ... age : 20 + i, ... contact : { ... address : ["address1_" + i, "address2_" + i], ... postcode : 100000 + i, ... } ... }; ... db.users.insert(u); ... } [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> 提供了多样性的索引支持。</p>
<pre>&gt; for (var i = 0; i &lt; 30; i++) {
...     u = { name : "user" + i,
...           age : 20 + i,
...           contact : {
...              address : ["address1_" + i, "address2_" + i],
...              postcode : 100000 + i,
...           }
...     };
...     db.users.insert(u);
... }</pre>
<p>索引信息被保存在 system.indexes 中，且默认总是为 _id 创建索引。<br />
<span id="more-5300"></span></p>
<pre>&gt; show collections
system.indexes
users

&gt; db.system.indexes.find()
{ "name" : "_id_", "ns" : "blog.users", "key" : { "_id" : 1 } }</pre>
<p><strong>1.  ensureIndex / dropIndex / reIndex</strong></p>
<p>使用 ensureIndex  创建索引，dropIndex() 删除索引，dropIndexes() 删除全部索引(不包括 _id 等系统索引)。</p>
<pre>&gt; db.users.ensureIndex({name:1})
&gt; db.users.ensureIndex({age:1})

&gt; db.system.indexes.find()
{ "name" : "_id_", "ns" : "blog.users", "key" : { "_id" : 1 } }
{ "_id" : ObjectId("4c4a...b798"), "ns" : "blog.users", "key" : { "name" : 1 }, "name" : "name_1" }
{ "_id" : ObjectId("4c4a...b799"), "ns" : "blog.users", "key" : { "age" : 1 }, "name" : "age_1" }

&gt; db.users.dropIndex({age:1})
{ "nIndexesWas" : 3, "ok" : true }

&gt; db.users.dropIndexes()
{
        "nIndexesWas" : 2,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : true
}

&gt; db.system.indexes.find()
{ "name" : "_id_", "ns" : "blog.users", "key" : { "_id" : 1 } }</pre>
<p>reIndex  则是重建索引。</p>
<pre>&gt; db.users.ensureIndex({name:1})
&gt; db.users.ensureIndex({age:1})

&gt; db.system.indexes.find()
{ "name" : "_id_", "ns" : "blog.users", "key" : { "_id" : 1 } }
{ "_id" : ObjectId("4c4a...b82a"), "ns" : "blog.users", "key" : { "name" : 1 }, "name" : "name_1" }
{ "_id" : ObjectId("4c4a...b82b"), "ns" : "blog.users", "key" : { "age" : 1 }, "name" : "age_1" }

&gt; db.users.reIndex()
{
        "nIndexesWas" : 3,
        "msg" : "indexes dropped for collection",
        "ok" : 1,
        "nIndexes" : 3,
        "indexes" : [
                {
                        "name" : "_id_",
                        "ns" : "blog.users",
                        "key" : {
                                "_id" : 1
                        }
                },
                {
                        "_id" : ObjectId("4c4a...b82a"),
                        "ns" : "blog.users",
                        "key" : {
                                "name" : 1
                        },
                        "name" : "name_1"
                },
                {
                        "_id" : ObjectId("4c4a...b82b"),
                        "ns" : "blog.users",
                        "key" : {
                                "age" : 1
                        },
                        "name" : "age_1"
                }
        ],
        "ok" : 1
}</pre>
<p>当系统已有大量数据时，创建索引就是个非常耗时的活，我们可以在后台执行。</p>
<pre>&gt; db.users.dropIndexes()
{
        "nIndexesWas" : 3,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : true
}

&gt; db.users.ensureIndex({name:1}, {backgroud:true})

&gt; db.users.reIndex({backgroud:true})
{
        "nIndexesWas" : 2,
        "msg" : "indexes dropped for collection",
        "ok" : 1,
        "nIndexes" : 2,
        "indexes" : [
                {
                        "name" : "_id_",
                        "ns" : "blog.users",
                        "key" : {
                                "_id" : 1
                        }
                },
                {
                        "_id" : ObjectId("4c4a...b79c"),
                        "ns" : "blog.users",
                        "key" : {
                                "name" : 1
                        },
                        "name" : "name_1",
                        "backgroud" : true
                }
        ],
        "ok" : 1
}</pre>
<p><strong>2. explain</strong></p>
<p>MongoDB 提供了一个 explain  命令让我们获知系统如何处理查询请求。</p>
<pre>&gt; db.users.ensureIndex({name:1})
&gt; db.users.ensureIndex({age:1})

&gt; db.users.find({age:{$gt:45}}, {name:1, age:1})
{ "_id" : ObjectId("4c4a8edeeb257107735eb826"), "name" : "user26", "age" : 46 }
{ "_id" : ObjectId("4c4a8edeeb257107735eb827"), "name" : "user27", "age" : 47 }
{ "_id" : ObjectId("4c4a8edeeb257107735eb828"), "name" : "user28", "age" : 48 }
{ "_id" : ObjectId("4c4a8edeeb257107735eb829"), "name" : "user29", "age" : 49 }

&gt; db.users.find({age:{$gt:45}}, {name:1, age:1}).explain()
{
        "cursor" : "BtreeCursor age_1",
        "nscanned" : 5,
        "nscannedObjects" : 4,
        "n" : 4,
        "millis" : 0,
        "indexBounds" : [
                [
                        {
                                "age" : 45
                        },
                        {
                                "age" : 1.7976931348623157e+308
                        }
                ]
        ]
}</pre>
<p>返回结果信息包括:</p>
<ul>
<li> cursor: 返回游标类型(BasicCursor  或 BtreeCursor)。</li>
<li> nscanned: 被扫描的文档数量。</li>
<li> n: 返回的文档数量。</li>
<li> millis: 耗时(毫秒)。</li>
<li> indexBounds: 所使用的索引。</li>
</ul>
<p>利用 explain  命令，我们可以很好地观察系统如何使用索引来加快检索，同时可以针对性优化索引。</p>
<p><strong>3. Embedded Keys Index</strong></p>
<p>我 们可以创建深层索引，甚至直接用文档(sub-document)作为索引键。</p>
<pre>&gt; db.users.ensureIndex({"contact.postcode":1})

&gt; db.users.find({"contact.postcode":{$lt:100009}}, {name:1, "contact.postcode":1})
{ "_id" : ObjectId("4c4a8edeeb257107735eb80c"), "name" : "user0", "contact" : { "postcode" : 100000 } }
{ "_id" : ObjectId("4c4a8edeeb257107735eb80d"), "name" : "user1", "contact" : { "postcode" : 100001 } }
{ "_id" : ObjectId("4c4a8edeeb257107735eb80e"), "name" : "user2", "contact" : { "postcode" : 100002 } }
{ "_id" : ObjectId("4c4a8edeeb257107735eb80f"), "name" : "user3", "contact" : { "postcode" : 100003 } }
{ "_id" : ObjectId("4c4a8edeeb257107735eb810"), "name" : "user4", "contact" : { "postcode" : 100004 } }
{ "_id" : ObjectId("4c4a8edeeb257107735eb811"), "name" : "user5", "contact" : { "postcode" : 100005 } }
{ "_id" : ObjectId("4c4a8edeeb257107735eb812"), "name" : "user6", "contact" : { "postcode" : 100006 } }
{ "_id" : ObjectId("4c4a8edeeb257107735eb813"), "name" : "user7", "contact" : { "postcode" : 100007 } }
{ "_id" : ObjectId("4c4a8edeeb257107735eb814"), "name" : "user8", "contact" : { "postcode" : 100008 } }

&gt; db.users.find({"contact.postcode":{$lt:100009}}, {name:1, "contact.postcode":1}).explain()
{
        "cursor" : "BtreeCursor contact.postcode_1",
        "nscanned" : 10,
        "nscannedObjects" : 9,
        "n" : 9,
        "millis" : 0,
        "indexBounds" : [
                [
                        {
                                "contact.postcode" : -1.7976931348623157e+308
                        },
                        {
                                "contact.postcode" : 100009
                        }
                ]
        ]
}</pre>
<p>我们直接用 contact 创建索引，查找其下属性时可使用该索引，但需注意语法。</p>
<p>(附注: 在 1.5.4  mongo 中，一直无法使用 contact:{postcode:xxx} 这样的 SubObject 语法查询数据，只能用  &#8220;contact.postcode&#8221; DotNotation 语法)</p>
<pre>&gt; db.users.dropIndex({"contact.postcode":1})
{ "nIndexesWas" : 4, "ok" : true }

&gt; db.users.ensureIndex({contact:1})

&gt; db.system.indexes.find()
{ "name" : "_id_", "ns" : "blog.users", "key" : { "_id" : 1 } }
{ "_id" : ObjectId("4c4a...b82a"), "ns" : "blog.users", "key" : { "name" : 1 }, "name" : "name_1" }
{ "_id" : ObjectId("4c4a...b82b"), "ns" : "blog.users", "key" : { "age" : 1 }, "name" : "age_1" }
{ "_id" : ObjectId("4c4a...b82d"), "ns" : "blog.users", "key" : { "contact" : 1 }, "name" : "contact_1" }

&gt; db.users.find({contact:{postcode:{$lt:100009}}}).explain()
{
        "cursor" : "BtreeCursor contact_1",
        "nscanned" : 0,
        "nscannedObjects" : 0,
        "n" : 0,
        "millis" : 0,
        "indexBounds" : [
                [
                        {
                                "contact" : {
                                        "postcode" : {
                                                "$lt" : 100009
                                        }
                                }
                        },
                        {
                                "contact" : {
                                        "postcode" : {
                                                "$lt" : 100009
                                        }
                                }
                        }
                ]
        ]
}

&gt; db.users.find({"contact.postcode":{$lt:100009}}).explain() // 无法使用索引
{
        "cursor" : "BasicCursor",
        "nscanned" : 30,
        "nscannedObjects" : 30,
        "n" : 9,
        "millis" : 0,
        "indexBounds" : [ ]
}

&gt; db.users.find({contact:{address:"address2_23"}}).explain()
{
        "cursor" : "BtreeCursor contact_1",
        "nscanned" : 0,
        "nscannedObjects" : 0,
        "n" : 0,
        "millis" : 0,
        "indexBounds" : [
                [
                        {
                                "contact" : {
                                        "address" : "address2_23"
                                }
                        },
                        {
                                "contact" : {
                                        "address" : "address2_23"
                                }
                        }
                ]
        ]
}

&gt; db.users.find({"contact.address":"address2_23"}).explain() // 无法使用索引
{
        "cursor" : "BasicCursor",
        "nscanned" : 30,
        "nscannedObjects" : 30,
        "n" : 1,
        "millis" : 0,
        "indexBounds" : [ ]
}</pre>
<p>同样的语法问题在 Python 中一样生效。</p>
<pre>&gt;&gt;&gt; db.users.find({"contact":{"postcode":{"$lt":100009}}}).explain()
{u'allPlans': [{u'cursor': u'BtreeCursor contact_1',
                u'indexBounds': [[{u'contact': {u'postcode': {u'$lt': 100009}}},
                                  {u'contact': {u'postcode': {u'$lt': 100009}}}]]}],
 u'cursor': u'BtreeCursor contact_1',
 u'indexBounds': [[{u'contact': {u'postcode': {u'$lt': 100009}}},
                   {u'contact': {u'postcode': {u'$lt': 100009}}}]],
 u'millis': 0,
 u'n': 0,
 u'nscanned': 0,
 u'nscannedObjects': 0,
 u'oldPlan': {u'cursor': u'BtreeCursor contact_1',
              u'indexBounds': [[{u'contact': {u'postcode': {u'$lt': 100009}}},
                                {u'contact': {u'postcode': {u'$lt': 100009}}}]]}}

&gt;&gt;&gt; db.users.find({"contact.postcode":{"$lt":100009}}).explain()
{u'allPlans': [{u'cursor': u'BasicCursor', u'indexBounds': []}],
 u'cursor': u'BasicCursor',
 u'indexBounds': [],
 u'millis': 0,
 u'n': 8,
 u'nscanned': 30,
 u'nscannedObjects': 30,
 u'oldPlan': {u'cursor': u'BasicCursor', u'indexBounds': []}}</pre>
<p><strong>4.  Compound Keys Index</strong></p>
<p>创建复合索引也很简单 (1: ascending; -1:  descending)。</p>
<pre>&gt; db.users.ensureIndex({name:1, age:-1})

&gt; db.system.indexes.find()
{ "name" : "_id_", "ns" : "blog.users", "key" : { "_id" : 1 } }
{ "_id" : ..., "ns" : "blog.users", "key" : { "name" : 1 }, "name" : "name_1" }
{ "_id" : ..., "ns" : "blog.users", "key" : { "age" : 1 }, "name" : "age_1" }
{ "_id" : ..., "ns" : "blog.users", "key" : { "contact" : 1 }, "name" : "contact_1" }
{ "_id" : ..., "ns" : "blog.users", "key" : { "name" : 1, "age" : -1 }, "name" : "name_1_age_-1" }

&gt; db.users.find({age:{$lt:25}, name:"user2"}, {name:1, age:1})
{ "_id" : ObjectId("4c4a8edeeb257107735eb80e"), "name" : "user2", "age" : 22 }

&gt; db.users.find({age:{$lt:25}, name:"user2"}, {name:1, age:1}).explain()
{
        "cursor" : "BtreeCursor name_1_age_-1",
        "nscanned" : 1,
        "nscannedObjects" : 1,
        "n" : 1,
        "millis" : 0,
        "indexBounds" : [
                [
                        {
                                "name" : "user2",
                                "age" : 25
                        },
                        {
                                "name" : "user2",
                                "age" : -1.7976931348623157e+308
                        }
                ]
        ]
}</pre>
<p>复合索引同样可用于局部属性的搜索，但必须依照索引字段顺序。比如创建索引字段顺序 &#8220;a,b,c&#8221;，那么仅对  &#8220;a,b,c&#8221;、&#8221;a,b&#8221;、&#8221;a&#8221; 查询有效，而对 &#8220;b,c&#8221; 之类的组合无效。</p>
<pre>&gt; db.users.dropIndex({name:1})
{ "nIndexesWas" : 5, "ok" : true }

&gt; db.users.dropIndex({age:1})
{ "nIndexesWas" : 4, "ok" : true }

&gt; db.users.dropIndex({contact:1})
{ "nIndexesWas" : 3, "ok" : true }

&gt; db.system.indexes.find()
{ "name" : "_id_", "ns" : "blog.users", "key" : { "_id" : 1 } }
{ "_id" : ObjectId("4c4a9...b82e"), "ns" : "blog.users", "key" : { "name" : 1, "age" : -1 }, "name" : "name_1_age_-1" }

&gt; db.users.find({name:"user12"}).explain() // 索引有效
{
        "cursor" : "BtreeCursor name_1_age_-1",
        "nscanned" : 1,
        "nscannedObjects" : 1,
        "n" : 1,
        "millis" : 0,
        "indexBounds" : [
                [
                        {
                                "name" : "user12",
                                "age" : {
                                        "$maxElement" : 1
                                }
                        },
                        {
                                "name" : "user12",
                                "age" : {
                                        "$minElement" : 1
                                }
                        }
                ]
        ]
}

&gt; db.users.find({age:18}).explain() // 索引无效
{
        "cursor" : "BasicCursor",
        "nscanned" : 30,
        "nscannedObjects" : 30,
        "n" : 0,
        "millis" : 0,
        "indexBounds" : [ ]
}</pre>
<p><strong>5. Unique Index</strong></p>
<p>只需在 ensureIndex 命令中指定 unique  即可创建唯一索引。</p>
<pre>&gt; db.users.ensureIndex({name:1}, {unique:true})

&gt; db.system.indexes.find()
{ "name" : "_id_", "ns" : "blog.users", "key" : { "_id" : 1 } }
{ "_id" : ..., "ns" : "blog.users", "key" : { "name" : 1, "age" : -1 }, "name" : "name_1_age_-1" }
{ "_id" : ..., "ns" : "blog.users", "key" : { "name" : 1 }, "name" : "name_1", "unique" : true }

&gt; db.users.insert({name:"user1"})
E11000 duplicate key error index: blog.users.$name_1  dup key: { : "user1" }</pre>
<p>如 果创建唯一索引前已经有重复文档，那么可以用 dropDups 删除多余的数据。</p>
<pre>&gt; db.users.dropIndexes()
{
        "nIndexesWas" : 3,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : true
}

&gt; db.users.insert({name:"user1"})

&gt; db.users.find({name:"user1"}, {name:1})
{ "_id" : ObjectId("4c4a9573eb257107735eb831"), "name" : "user1" }
{ "_id" : ObjectId("4c4a8edeeb257107735eb80d"), "name" : "user1" }

&gt; db.users.ensureIndex({name:1}, {unique:true, dropDups:true})
E11000 duplicate key error index: blog.users.$name_1  dup key: { : "user1" }

&gt; db.users.find({name:"user1"}, {name:1})
{ "_id" : ObjectId("4c4a9573eb257107735eb831"), "name" : "user1" }</pre>
<p><strong>6.  Multikeys</strong></p>
<p>对于数组类型属性，会自动索引全部数组元素。</p>
<pre>&gt; db.users.dropIndexes()
{
        "nIndexesWas" : 1,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : true
}

&gt; db.users.ensureIndex({"contact.address":1})

&gt; db.users.find({"contact.address":"address2_13"}).explain()
{
        "cursor" : "BtreeCursor contact.address_1",
        "nscanned" : 1,
        "nscannedObjects" : 1,
        "n" : 1,
        "millis" : 0,
        "indexBounds" : [
                [
                        {
                                "contact.address" : "address2_13"
                        },
                        {
                                "contact.address" : "address2_13"
                        }
                ]
        ]
}</pre>
<p><strong>7. hint</strong></p>
<p>hint 命令可以强制使用某个索引。</p>
<pre>&gt; db.users.dropIndexes()
{
        "nIndexesWas" : 2,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : true
}

&gt; db.users.ensureIndex({name:1, age:1})

&gt; db.users.find({age:{$lt:30}}).explain()
{
        "cursor" : "BasicCursor",
        "nscanned" : 30,
        "nscannedObjects" : 30,
        "n" : 9,
        "millis" : 0,
        "indexBounds" : [ ]
}

&gt; db.users.find({age:{$lt:30}}).hint({name:1, age:1}).explain()
{
        "cursor" : "BtreeCursor name_1_age_1",
        "nscanned" : 30,
        "nscannedObjects" : 9,
        "n" : 9,
        "millis" : 0,
        "indexBounds" : [
                [
                        {
                                "name" : {
                                        "$minElement" : 1
                                },
                                "age" : -1.7976931348623157e+308
                        },
                        {
                                "name" : {
                                        "$maxElement" : 1
                                },
                                "age" : 30
                        }
                ]
        ]
}</pre>
<p>注意 Python 代码中写法。</p>
<pre>&gt;&gt;&gt; db.users.find({"age":{"$lt":30}}).hint([("name", ASCENDING), ("age", ASCENDING)]).explain()
{u'cursor': u'BtreeCursor name_1_age_1',
 u'indexBounds': [[{u'age': -1.7976931348623157e+308,
                    u'name': {u'$minElement': 1}},
                   {u'age': 30, u'name': {u'$maxElement': 1}}]],
 u'millis': 0,
 u'n': 9,
 u'nscanned': 30,
 u'nscannedObjects': 9}</pre>
<p><strong>8. totalIndexSize</strong></p>
<p>MongoDB  会将索引数据载入内存，以提高查询速度。我们可以用 totalIndexSize 获取全部索引数据大小。</p>
<pre>&gt; db.users.totalIndexSize()
16384</pre>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5300/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5300/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5300/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5300/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5300/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5300/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 3. Schema Design</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5297/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5297/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:03:53 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5297</guid>
		<description><![CDATA[1. Document-Oriented MongoDB 是一种面向文档(document-oriented)的数据库，其内存储的是一种 JSON-like 结构化数据。尽管拥有和关系型数据库 Database/Table 类似的的 DB/Collection 概念，但同一 Collection 内的 Document 可以拥有不同的属性。 (注: 以下 &#62; 提示符表示 mongo JS 代码，&#62;&#62;&#62; 为 Python 代码) &#62; use blog switched to db blog &#62; db.users.insert({name:"user1", age:15}) &#62; db.users.insert({name:"user2", age:20, sex:1}) &#62; db.users.find() { "_id" : ObjectId("4c479885089df9b53474170a"), "name" : "user1", "age" : 15 } { "_id" : ObjectId("4c479896089df9b53474170b"), [...]]]></description>
			<content:encoded><![CDATA[<p><strong>1. Document-Oriented</strong></p>
<p><a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a>  是一种面向文档(document-oriented)的数据库，其内存储的是一种 JSON-like 结构化数据。尽管拥有和关系型数据库  <a href="http://www.iwanna.cn/tags/db/" class="st_tag internal_tag" rel="tag" title="标签 Database 下的日志">Database</a>/Table 类似的的 DB/Collection 概念，但同一 Collection 内的 Document  可以拥有不同的属性。</p>
<p>(注: 以下 &gt; 提示符表示 mongo JS 代码，&gt;&gt;&gt; 为 Python  代码)</p>
<pre>&gt; use blog
switched to db blog

&gt; db.users.insert({name:"user1", age:15})
&gt; db.users.insert({name:"user2", age:20, sex:1})

&gt; db.users.find()
{ "_id" : ObjectId("4c479885089df9b53474170a"), "name" : "user1", "age" : 15 }
{ "_id" : ObjectId("4c479896089df9b53474170b"), "name" : "user2", "age" : 20, "sex" : 1 }</pre>
<p><span id="more-5297"></span><br />
可以通过 $exists 判断某个字段是否存在。</p>
<pre>&gt; db.users.find({sex:{$exists:true}})
{ "_id" : ObjectId("4c479896089df9b53474170b"), "name" : "user2", "age" : 20, "sex" : 1 }</pre>
<p><strong>2.  Embed vs. Reference</strong></p>
<p>Document 采取 JSON-like  这种层级结构，因此我们可以直接用嵌入(Embed)代替传统关系型数据库的关联引用(Reference)。</p>
<pre>&gt; u = db.users.findOne({name:"user1"})
{
        "_id" : ObjectId("4c479885089df9b53474170a"),
        "name" : "user1",
        "age" : 15
}

&gt; u.address = ["address1", "address2"]
[ "address1", "address2" ]

&gt; db.users.save(u)

&gt; db.users.findOne({name:"user1"})
{
        "_id" : ObjectId("4c479885089df9b53474170a"),
        "name" : "user1",
        "age" : 15,
        "address" : [
                "address1",
                "address2"
        ]
}

&gt; db.users.findOne({address:"address1"})
{
        "_id" : ObjectId("4c479885089df9b53474170a"),
        "name" : "user1",
        "age" : 15,
        "address" : [
                "address1",
                "address2"
        ]
}</pre>
<p>MongoDB 支持以 &#8220;.&#8221; 分割的 namespace 路径，但需要注意 key 不能以 &#8220;$&#8221; 开头，不能包含  &#8220;.&#8221; 字符 (条件表达式中的多级路径须用引号)。</p>
<pre>&gt; u = db.users.findOne({address:"address1"})
{
        "_id" : ObjectId("4c479885089df9b53474170a"),
        "name" : "user1",
        "age" : 15,
        "address" : [
                "address1",
                "address2"
        ]
}

&gt; u.im = {msn:"user1@hotmail.com", qq:12345678}
{ "msn" : "user1@hotmail.com", "qq" : 12345678 }

&gt; db.users.save(u)

&gt; u = db.users.findOne({"im.qq":12345678})
{
        "_id" : ObjectId("4c479885089df9b53474170a"),
        "name" : "user1",
        "age" : 15,
        "address" : [
                "address1",
                "address2"
        ],
        "im" : {
                "msn" : "user1@hotmail.com",
                "qq" : 12345678
        }
}

&gt; u.im.qq
12345678

&gt; u.im.msn
user1@hotmail.com

&gt; db.users.update({"im.qq":12345678}, {$set:{"im.qq":12345}})

&gt; u = db.users.findOne({"im.qq":12345})
{
        "_id" : ObjectId("4c479885089df9b53474170a"),
        "name" : "user1",
        "age" : 15,
        "address" : [
                "address1",
                "address2"
        ],
        "im" : {
                "msn" : "user1@hotmail.com",
                "qq" : 12345
        }
}

&gt; u = db.users.find({"im.qq":{$exists:true}}, {"im.qq":1})
{ "_id" : ObjectId("4c479885089df9b53474170a"), "im" : { "qq" : 12345 } }</pre>
<p>注 意: 由于每篇文档都包含完整的 key 数据，因此使用尽可能短的 key 可以有效节省存储空间。</p>
<p><strong>3. Operator</strong></p>
<p>(1)  $all: 判断数组属性是否包含全部条件。</p>
<pre>&gt; db.users.insert({name:"user3", data:[1,2,3,4,5,6,7]})
&gt; db.users.insert({name:"user4", data:[1,2,3]})

&gt; db.users.find({data:{$all:[2,3,4]}})
{ "_id" : ObjectId("4c47a133b48cde79c6780df0"), "name" : "user3", "data" : [ 1, 2, 3, 4, 5, 6, 7 ] }</pre>
<p>注 意和 $in 的区别。$in 是检查目标属性值是条件表达式中的一员，而 $all 则要求属性值包含全部条件元素。</p>
<p>(2)  $size: 匹配数组属性元素数量。</p>
<pre>&gt; db.users.insert({name:"user3", data:[1,2,3,4,5,6,7]})
&gt; db.users.insert({name:"user4", data:[1,2,3]})

&gt; db.users.find({data:{$size:3}})
{ "_id" : ObjectId("4c47a13bb48cde79c6780df1"), "name" : "user4", "data" : [ 1, 2, 3 ] }</pre>
<p>(3)  $type: 判断属性类型。</p>
<pre>&gt; db.users.insert({name:"user5", t:1})
&gt; db.users.insert({name:"user6", t:1.34})
&gt; db.users.insert({name:"user7", t:"abc"})

&gt; db.users.find({t:{$type:1}})
{ "_id" : ObjectId("4c47a231b48cde79c6780df2"), "name" : "user5", "t" : 1 }
{ "_id" : ObjectId("4c47a23eb48cde79c6780df3"), "name" : "user6", "t" : 1.34 }

&gt; db.users.find({t:{$type:2}})
{ "_id" : ObjectId("4c47a258b48cde79c6780df4"), "name" : "user7", "t" : "abc" }</pre>
<p>类 型值:</p>
<ul>
<li> double:1</li>
<li> string: 2</li>
<li> object: 3</li>
<li> array: 4</li>
<li> binary data: 5</li>
<li> object id: 7</li>
<li> boolean: 8</li>
<li> date: 9</li>
<li> null: 10</li>
<li> regular  expression: 11</li>
<li> javascript code: 13</li>
<li> symbol: 14</li>
<li> javascript code with scope: 15</li>
<li> 32-bit integer: 16</li>
<li> timestamp: 17</li>
<li> 64-bit integer: 18</li>
<li> min key: 255</li>
<li> max key: 127</li>
</ul>
<p>(4) 使用正则表达式进行查询。</p>
<pre>&gt; db.users.find({name:/user[135]/i}, {name:1})
{ "_id" : ObjectId("4c479885089df9b53474170a"), "name" : "user1" }
{ "_id" : ObjectId("4c47a133b48cde79c6780df0"), "name" : "user3" }
{ "_id" : ObjectId("4c47a231b48cde79c6780df2"), "name" : "user5" }

&gt;&gt;&gt; users = db.users.find({"name" : {"$regex" : r"(?i)user[135]"}}, ["name"])
&gt;&gt;&gt; for u in users: print u
...
{u'_id': ObjectId('4c479885089df9b53474170a'), u'name': u'user1'}
{u'_id': ObjectId('4c47a133b48cde79c6780df0'), u'name': u'user3'}
{u'_id': ObjectId('4c47a231b48cde79c6780df2'), u'name': u'user5'}</pre>
<p>正 则表达式标记:</p>
<p>i: 忽略大小写。<br />
m: 默认为单行处理，此标记表示多行。<br />
x: 扩展。</p>
<p>(5)  数组属性元素值匹配。</p>
<pre>&gt; db.users.find({data:"abc"})
{ "_id" : ObjectId("4c47a481b48cde79c6780df5"), "name" : "user8", "data" : [ { "a" : 1, "b" : 10 }, 3, "abc" ] }

&gt; db.users.find({data:{$elemMatch:{a:1, b:{$gt:5}}}})
{ "_id" : ObjectId("4c47a481b48cde79c6780df5"), "name" : "user8", "data" : [ { "a" : 1, "b" : 10 }, 3, "abc" ] }</pre>
<p>{data:&#8221;abc&#8221;}  仅简单匹配数组属性是否包含该元素。$elemMatch 则可以处理更复杂的元素查找条件。当然也可以写成如下方式。</p>
<pre>&gt; db.users.find({"data.a":1, "data.b":{$gt:5}})
{ "_id" : ObjectId("4c47a481b48cde79c6780df5"), "name" : "user8", "data" : [ { "a" : 1, "b" : 10 }, 3, "abc" ] }</pre>
<p>还 可以直接使用序号进行操作。</p>
<pre>&gt; db.users.find({"data.2":3})
{ "_id" : ObjectId("4c47a133b48cde79c6780df0"), "name" : "user3", "data" : [ 1, 2, 3, 4, 5, 6, 7 ] }
{ "_id" : ObjectId("4c47a13bb48cde79c6780df1"), "name" : "user4", "data" : [ 1, 2, 3 ] }</pre>
<p>(6)  $not: 取反，表示返回条件不成立的文档。</p>
<p>似乎只能跟正则和 $mod 一起使用？？？？</p>
<pre>&gt; u = db.users.find({name:{$not:/user3/}}, {name:1})
{ "_id" : ObjectId("4c479885089df9b53474170a"), "name" : "user1" }
{ "_id" : ObjectId("4c47a13bb48cde79c6780df1"), "name" : "user4" }
{ "_id" : ObjectId("4c47a231b48cde79c6780df2"), "name" : "user5" }
{ "_id" : ObjectId("4c47a23eb48cde79c6780df3"), "name" : "user6" }
{ "_id" : ObjectId("4c47a258b48cde79c6780df4"), "name" : "user7" }
{ "_id" : ObjectId("4c47a481b48cde79c6780df5"), "name" : "user8" }
{ "_id" : ObjectId("4c479896089df9b53474170b"), "name" : "user2" }</pre>
<p>(7)  $unset: 和 $set 相反，表示移除文档属性。</p>
<pre>&gt; u = db.users.find({name:"user1"})
{ "_id" : ObjectId("4c479885089df9b53474170a"), "name" : "user1", "age" : 15, "address" : [ "address1", "address2" ], "im" : { "msn" : "user1@hotmail.com", "qq" : 12345 } }

&gt; db.users.update({name:"user1"}, {$unset:{address:1, im:1}})

&gt; u = db.users.find({name:"user1"})
{ "_id" : ObjectId("4c479885089df9b53474170a"), "age" : 15, "name" : "user1" }</pre>
<p>(8)  $push: 和 $ pushAll 都是向数组属性添加元素。</p>
<pre>&gt; u = db.users.find({name:"user1"})
{ "_id" : ObjectId("4c479885089df9b53474170a"), "age" : 15, "name" : "user1" }

&gt; db.users.update({name:"user1"}, {$push:{data:1}})

&gt; u = db.users.find({name:"user1"})
{ "_id" : ObjectId("4c479885089df9b53474170a"), "age" : 15, "data" : [ 1 ], "name" : "user1" }

&gt; db.users.update({name:"user1"}, {$pushAll:{data:[2,3,4,5]}})

&gt; u = db.users.find({name:"user1"})
{ "_id" : ObjectId("4c479885089df9b53474170a"), "age" : 15, "data" : [ 1, 2, 3, 4, 5 ], "name" : "user1" }</pre>
<p>(9)  $addToSet: 和 $push 类似，不过仅在该元素不存在时才添加 (Set 表示不重复元素集合)。</p>
<pre>&gt; db.users.update({name:"user2"}, {$addToSet:{data:1}})
&gt; db.users.update({name:"user2"}, {$addToSet:{data:1}})

&gt; u = db.users.find({name:"user2"})
{ "_id" : ObjectId("4c479896089df9b53474170b"), "data" : [ 1 ], "name" : "user2" }
&gt; db.users.update({name:"user2"}, {$push:{data:1}})

&gt; u = db.users.find({name:"user2"})
{ "_id" : ObjectId("4c479896089df9b53474170b"), "data" : [ 1, 1 ], "name" : "user2" }</pre>
<p>要 添加多个元素，使用 $each。</p>
<pre>&gt; db.users.update({name:"user2"}, {$addToSet:{data:{$each:[1,2,3,4]}}})

&gt; u = db.users.find({name:"user2"})
{ "_id" : ObjectId("4c479896089df9b53474170b"), "data" : [ 1, 2, 3, 4 ], "name" : "user2" }</pre>
<p>(10)  $pop: 移除数组属性的元素，$pull 按值移除，$pullAll 移除所有符合提交的元素。</p>
<pre>&gt; u = db.users.find({name:"user3"})
{ "_id" : ObjectId("4c47a133b48cde79c6780df0"), "data" : [ 1, 2, 3, 4, 5, 6, 7, 2, 3 ], "name" : "user3" }

&gt; db.users.update({name:"user3"}, {$pop:{data:1}}) // 移除最后一个元素

&gt; u = db.users.find({name:"user3"})
{ "_id" : ObjectId("4c47a133b48cde79c6780df0"), "data" : [ 1, 2, 3, 4, 5, 6, 7, 2 ], "name" : "user3" }

&gt; db.users.update({name:"user3"}, {$pop:{data:-1}}) // 移除第一个元素

&gt; u = db.users.find({name:"user3"})
{ "_id" : ObjectId("4c47a133b48cde79c6780df0"), "data" : [ 2, 3, 4, 5, 6, 7, 2 ], "name" : "user3" }

&gt; db.users.update({name:"user3"}, {$pull:{data:2}}) // 移除全部 2

&gt; u = db.users.find({name:"user3"})
{ "_id" : ObjectId("4c47a133b48cde79c6780df0"), "data" : [ 3, 4, 5, 6, 7 ], "name" : "user3" }

&gt; db.users.update({name:"user3"}, {$pullAll:{data:[4,5,6]}}) // 移除 4,5,6

&gt; u = db.users.find({name:"user3"})
{ "_id" : ObjectId("4c47a133b48cde79c6780df0"), "data" : [ 3, 7 ], "name" : "user3" }</pre>
<p>(11)  $where: 用 JS 代码来代替有些丑陋的 $lt、$gt。</p>
<p>MongoDB 内置了 Javascript Engine  (SpiderMonkey)。可直接使用 JS Expression，甚至使用 JS Function 写更复杂的 Code Block。</p>
<pre>&gt; for (var i = 0; i &lt; 10; i++) db.users.insert({name:"user"+i, age:i})

&gt; db.users.find()
{ "_id" : ObjectId("4c47b3372a9b2be866da226e"), "name" : "user0", "age" : 0 }
{ "_id" : ObjectId("4c47b3372a9b2be866da226f"), "name" : "user1", "age" : 1 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2270"), "name" : "user2", "age" : 2 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2271"), "name" : "user3", "age" : 3 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2272"), "name" : "user4", "age" : 4 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2273"), "name" : "user5", "age" : 5 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2274"), "name" : "user6", "age" : 6 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2275"), "name" : "user7", "age" : 7 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2276"), "name" : "user8", "age" : 8 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2277"), "name" : "user9", "age" : 9 }

&gt; db.users.find({$where:"this.age &gt; 7 || this.age &lt; 3"})
{ "_id" : ObjectId("4c47b3372a9b2be866da226e"), "name" : "user0", "age" : 0 }
{ "_id" : ObjectId("4c47b3372a9b2be866da226f"), "name" : "user1", "age" : 1 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2270"), "name" : "user2", "age" : 2 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2276"), "name" : "user8", "age" : 8 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2277"), "name" : "user9", "age" : 9 }

&gt; db.users.find("this.age &gt; 7 || this.age &lt; 3")
{ "_id" : ObjectId("4c47b3372a9b2be866da226e"), "name" : "user0", "age" : 0 }
{ "_id" : ObjectId("4c47b3372a9b2be866da226f"), "name" : "user1", "age" : 1 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2270"), "name" : "user2", "age" : 2 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2276"), "name" : "user8", "age" : 8 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2277"), "name" : "user9", "age" : 9 }

&gt; db.users.find({$where: function(){ return this.age &gt; 7 || this.age &lt; 3;}})
{ "_id" : ObjectId("4c47b3372a9b2be866da226e"), "name" : "user0", "age" : 0 }
{ "_id" : ObjectId("4c47b3372a9b2be866da226f"), "name" : "user1", "age" : 1 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2270"), "name" : "user2", "age" : 2 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2276"), "name" : "user8", "age" : 8 }
{ "_id" : ObjectId("4c47b3372a9b2be866da2277"), "name" : "user9", "age" : 9 }

&gt;&gt;&gt; for u in db.users.find({"$where":"this.age &gt; 7 || this.age &lt; 3"}): print u
...
{u'age': 0.0, u'_id': ObjectId('4c47b3372a9b2be866da226e'), u'name': u'user0'}
{u'age': 1.0, u'_id': ObjectId('4c47b3372a9b2be866da226f'), u'name': u'user1'}
{u'age': 2.0, u'_id': ObjectId('4c47b3372a9b2be866da2270'), u'name': u'user2'}
{u'age': 8.0, u'_id': ObjectId('4c47b3372a9b2be866da2276'), u'name': u'user8'}
{u'age': 9.0, u'_id': ObjectId('4c47b3372a9b2be866da2277'), u'name': u'user9'}

&gt;&gt;&gt; for u in db.users.find().where("this.age &gt; 7 || this.age &lt; 3"): print u
...
{u'age': 0.0, u'_id': ObjectId('4c47b3372a9b2be866da226e'), u'name': u'user0'}
{u'age': 1.0, u'_id': ObjectId('4c47b3372a9b2be866da226f'), u'name': u'user1'}
{u'age': 2.0, u'_id': ObjectId('4c47b3372a9b2be866da2270'), u'name': u'user2'}
{u'age': 8.0, u'_id': ObjectId('4c47b3372a9b2be866da2276'), u'name': u'user8'}
{u'age': 9.0, u'_id': ObjectId('4c47b3372a9b2be866da2277'), u'name': u'user9'}

&gt;&gt;&gt; for u in db.users.find().where("function() { return this.age &gt; 7 || this.age &lt; 3;}"): print u
...
{u'age': 0.0, u'_id': ObjectId('4c47b3372a9b2be866da226e'), u'name': u'user0'}
{u'age': 1.0, u'_id': ObjectId('4c47b3372a9b2be866da226f'), u'name': u'user1'}
{u'age': 2.0, u'_id': ObjectId('4c47b3372a9b2be866da2270'), u'name': u'user2'}
{u'age': 8.0, u'_id': ObjectId('4c47b3372a9b2be866da2276'), u'name': u'user8'}
{u'age': 9.0, u'_id': ObjectId('4c47b3372a9b2be866da2277'), u'name': u'user9'}</pre>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5297/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5297/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5297/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5297/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5297/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5297/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 2. Basic Usage</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5296/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5296/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:02:56 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5296</guid>
		<description><![CDATA[须安装 PyMongo (Documentation)。 $ sudo easy_install -U pymongo (注: 以下 &#62; 提示符表示 mongo JS 代码，&#62;&#62;&#62; 为 Python 代码) 1. INSERT 使 用 insert 插入文档。 &#62; use blog switched to db blog &#62; u = { name:"user1", age:23 } { "name" : "user1", "age" : 23 } &#62; db.users.insert(u) &#62; u2 = db.users.findOne({name:"user1"}) { "_id" : [...]]]></description>
			<content:encoded><![CDATA[<p>须安装 PyMongo (<a title="http://api.mongodb.org/python/current/index.html" href="http://api.mongodb.org/python/current/index.html" target="_blank">Documentation</a>)。</p>
<pre>$ sudo easy_install -U pymongo</pre>
<p>(注:  以下 &gt; 提示符表示 mongo JS 代码，&gt;&gt;&gt; 为 Python 代码)</p>
<p><strong>1. INSERT</strong></p>
<p>使 用 insert 插入文档。</p>
<pre>&gt; use blog
switched to db blog

&gt; u = { name:"user1", age:23 }
{ "name" : "user1", "age" : 23 }

&gt; db.users.insert(u)

&gt; u2 = db.users.findOne({name:"user1"})
{
        "_id" : ObjectId("4c44fe0edef8f3492fe67d60"),
        "name" : "user1",
        "age" : 23
}

&gt; u2.age += 3
26

&gt; db.users.save(u2)

&gt; db.users.find()
{ "_id" : ObjectId("4c44fe0edef8f3492fe67d60"), "name" : "user1", "age" : 26 }</pre>
<p><span id="more-5296"></span><br />
save()  可插入新文档，也可以更新(update)一个已有的文档。下面是用 PyMongo 写的相同的代码。</p>
<pre>&gt;&gt;&gt; import pymongo
&gt;&gt;&gt; conn = pymongo.Connection(host="192.168.1.202")
&gt;&gt;&gt; db = conn.blog

&gt;&gt;&gt; u = {"name":"user1", "age":23}

&gt;&gt;&gt; db.users.save(u)
ObjectId('4c456e0a499b14047e000000')

&gt;&gt;&gt; u2 = db.users.find_one({"name":"user1"})

&gt;&gt;&gt; u2
{u'age': 23, u'_id': ObjectId('4c456e0a499b14047e000000'), u'name': u'user1'}

&gt;&gt;&gt; u2["age"] += 3

&gt;&gt;&gt; db.users.save(u2)
ObjectId('4c456e0a499b14047e000000')

&gt;&gt;&gt; for u in db.users.find(): print u
...
{u'age': 26, u'_id': ObjectId('4c456e0a499b14047e000000'), u'name': u'user1'}</pre>
<p><strong>2.  Query</strong></p>
<p><a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> 支持多种复杂的查询方式，能实现大多数 T-SQL 功能，远不是 Key-Value 之类的  <a href="http://www.iwanna.cn/tags/nosql/" class="st_tag internal_tag" rel="tag" title="标签 NoSQL 下的日志">NoSQL</a> DB 所能比拟的。<br />
相关函数操作看上去非常像 .NET/C# Linq Method Syntax。<br />
有关查询优化和索引 的细节请参考后文。</p>
<p>先准备好所需的测试数据。</p>
<pre>&gt; use blog
switched to db blog

&gt; for (var i = 0; i &lt; 16; i++) db.users.insert({name:"user" + i, age:20 + i, sex:i % 2})

&gt; db.users.find()
{ "_id" : ObjectId("4c452c343d48c8f284b388df"), "name" : "user0", "age" : 20, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e0"), "name" : "user1", "age" : 21, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e1"), "name" : "user2", "age" : 22, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e2"), "name" : "user3", "age" : 23, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e3"), "name" : "user4", "age" : 24, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e4"), "name" : "user5", "age" : 25, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e5"), "name" : "user6", "age" : 26, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e6"), "name" : "user7", "age" : 27, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e7"), "name" : "user8", "age" : 28, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e8"), "name" : "user9", "age" : 29, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e9"), "name" : "user10", "age" : 30, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ea"), "name" : "user11", "age" : 31, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388eb"), "name" : "user12", "age" : 32, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ec"), "name" : "user13", "age" : 33, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ed"), "name" : "user14", "age" : 34, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ee"), "name" : "user15", "age" : 35, "sex" : 1 }</pre>
<p>主 要用到的查询函数式 find() 和 findOne()，前者返回一个迭代器 cursor，后者返回单个文档。</p>
<p><strong>(1)  WHERE</strong></p>
<p># select * from users where name = &#8216;user1&#8242;</p>
<pre>&gt; db.users.find({name:"user1"})
{ "_id" : ObjectId("4c4528a0b55f2224d447e4b0"), "name" : "user1", "age" : 21, "sex" : 1 }

&gt;&gt;&gt; for u in db.users.find({"name":"user1"}): print u
...
{u'age': 21.0, u'_id': ObjectId('4c4528a0b55f2224d447e4b0'), u'name': u'user1', u'sex': 1.0}</pre>
<p>#  select * from users where name = &#8216;user1&#8242; and age = 21</p>
<pre>&gt; db.users.find({name:"user1", age:21})
{ "_id" : ObjectId("4c4528a0b55f2224d447e4b0"), "name" : "user1", "age" : 21, "sex" : 1 }

&gt;&gt;&gt; for u in db.users.find({"name":"user1", "age":21}): print u
...
{u'age': 21.0, u'_id': ObjectId('4c4528a0b55f2224d447e4b0'), u'name': u'user1', u'sex': 1.0}</pre>
<p><strong>(2)  FIELDS</strong></p>
<p># select name, age from users where age = 21</p>
<pre>&gt; db.users.find({age:21}, {'name':1, 'age':1})
{ "_id" : ObjectId("4c452c343d48c8f284b388e0"), "name" : "user1", "age" : 21 }

&gt;&gt;&gt; for u in db.users.find({"age":21}, ["name", "age"]): print u
...
{u'age': 21.0, u'_id': ObjectId('4c452c343d48c8f284b388e0'), u'name': u'user1'}</pre>
<p>#  select name, age from users</p>
<pre>&gt; db.users.find({}, {'name':1, 'age':1})
{ "_id" : ObjectId("4c452c343d48c8f284b388df"), "name" : "user0", "age" : 20 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e0"), "name" : "user1", "age" : 21 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e1"), "name" : "user2", "age" : 22 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e2"), "name" : "user3", "age" : 23 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e3"), "name" : "user4", "age" : 24 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e4"), "name" : "user5", "age" : 25 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e5"), "name" : "user6", "age" : 26 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e6"), "name" : "user7", "age" : 27 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e7"), "name" : "user8", "age" : 28 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e8"), "name" : "user9", "age" : 29 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e9"), "name" : "user10", "age" : 30 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ea"), "name" : "user11", "age" : 31 }
{ "_id" : ObjectId("4c452c343d48c8f284b388eb"), "name" : "user12", "age" : 32 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ec"), "name" : "user13", "age" : 33 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ed"), "name" : "user14", "age" : 34 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ee"), "name" : "user15", "age" : 35 }

&gt;&gt;&gt; for u in db.users.find(fields = ["name", "age"]): print u
...
{u'age': 20.0, u'_id': ObjectId('4c452c343d48c8f284b388df'), u'name': u'user0'}
{u'age': 21.0, u'_id': ObjectId('4c452c343d48c8f284b388e0'), u'name': u'user1'}
{u'age': 22.0, u'_id': ObjectId('4c452c343d48c8f284b388e1'), u'name': u'user2'}
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3'}
{u'age': 24.0, u'_id': ObjectId('4c452c343d48c8f284b388e3'), u'name': u'user4'}
{u'age': 25.0, u'_id': ObjectId('4c452c343d48c8f284b388e4'), u'name': u'user5'}
{u'age': 26.0, u'_id': ObjectId('4c452c343d48c8f284b388e5'), u'name': u'user6'}
{u'age': 27.0, u'_id': ObjectId('4c452c343d48c8f284b388e6'), u'name': u'user7'}
{u'age': 28.0, u'_id': ObjectId('4c452c343d48c8f284b388e7'), u'name': u'user8'}
{u'age': 29.0, u'_id': ObjectId('4c452c343d48c8f284b388e8'), u'name': u'user9'}
{u'age': 30.0, u'_id': ObjectId('4c452c343d48c8f284b388e9'), u'name': u'user10'}
{u'age': 31.0, u'_id': ObjectId('4c452c343d48c8f284b388ea'), u'name': u'user11'}
{u'age': 32.0, u'_id': ObjectId('4c452c343d48c8f284b388eb'), u'name': u'user12'}
{u'age': 33.0, u'_id': ObjectId('4c452c343d48c8f284b388ec'), u'name': u'user13'}
{u'age': 34.0, u'_id': ObjectId('4c452c343d48c8f284b388ed'), u'name': u'user14'}
{u'age': 35.0, u'_id': ObjectId('4c452c343d48c8f284b388ee'), u'name': u'user15'}</pre>
<p><strong>(3)  SORT</strong></p>
<p># select * from users order by age</p>
<pre>&gt; db.users.find().sort({age:1})
{ "_id" : ObjectId("4c452c343d48c8f284b388df"), "name" : "user0", "age" : 20, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e0"), "name" : "user1", "age" : 21, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e1"), "name" : "user2", "age" : 22, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e2"), "name" : "user3", "age" : 23, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e3"), "name" : "user4", "age" : 24, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e4"), "name" : "user5", "age" : 25, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e5"), "name" : "user6", "age" : 26, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e6"), "name" : "user7", "age" : 27, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e7"), "name" : "user8", "age" : 28, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e8"), "name" : "user9", "age" : 29, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e9"), "name" : "user10", "age" : 30, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ea"), "name" : "user11", "age" : 31, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388eb"), "name" : "user12", "age" : 32, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ec"), "name" : "user13", "age" : 33, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ed"), "name" : "user14", "age" : 34, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ee"), "name" : "user15", "age" : 35, "sex" : 1 }

&gt; db.users.find().sort({age:-1})
{ "_id" : ObjectId("4c452c343d48c8f284b388ee"), "name" : "user15", "age" : 35, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ed"), "name" : "user14", "age" : 34, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ec"), "name" : "user13", "age" : 33, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388eb"), "name" : "user12", "age" : 32, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ea"), "name" : "user11", "age" : 31, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e9"), "name" : "user10", "age" : 30, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e8"), "name" : "user9", "age" : 29, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e7"), "name" : "user8", "age" : 28, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e6"), "name" : "user7", "age" : 27, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e5"), "name" : "user6", "age" : 26, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e4"), "name" : "user5", "age" : 25, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e3"), "name" : "user4", "age" : 24, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e2"), "name" : "user3", "age" : 23, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e1"), "name" : "user2", "age" : 22, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e0"), "name" : "user1", "age" : 21, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388df"), "name" : "user0", "age" : 20, "sex" : 0 }

&gt;&gt;&gt; for u in db.users.find().sort([("age", ASCENDING)]): print u
...
{u'age': 20.0, u'_id': ObjectId('4c452c343d48c8f284b388df'), u'name': u'user0', u'sex': 0.0}
{u'age': 21.0, u'_id': ObjectId('4c452c343d48c8f284b388e0'), u'name': u'user1', u'sex': 1.0}
{u'age': 22.0, u'_id': ObjectId('4c452c343d48c8f284b388e1'), u'name': u'user2', u'sex': 0.0}
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3', u'sex': 1.0}
{u'age': 24.0, u'_id': ObjectId('4c452c343d48c8f284b388e3'), u'name': u'user4', u'sex': 0.0}
{u'age': 25.0, u'_id': ObjectId('4c452c343d48c8f284b388e4'), u'name': u'user5', u'sex': 1.0}
{u'age': 26.0, u'_id': ObjectId('4c452c343d48c8f284b388e5'), u'name': u'user6', u'sex': 0.0}
{u'age': 27.0, u'_id': ObjectId('4c452c343d48c8f284b388e6'), u'name': u'user7', u'sex': 1.0}
{u'age': 28.0, u'_id': ObjectId('4c452c343d48c8f284b388e7'), u'name': u'user8', u'sex': 0.0}
{u'age': 29.0, u'_id': ObjectId('4c452c343d48c8f284b388e8'), u'name': u'user9', u'sex': 1.0}
{u'age': 30.0, u'_id': ObjectId('4c452c343d48c8f284b388e9'), u'name': u'user10', u'sex': 0.0}
{u'age': 31.0, u'_id': ObjectId('4c452c343d48c8f284b388ea'), u'name': u'user11', u'sex': 1.0}
{u'age': 32.0, u'_id': ObjectId('4c452c343d48c8f284b388eb'), u'name': u'user12', u'sex': 0.0}
{u'age': 33.0, u'_id': ObjectId('4c452c343d48c8f284b388ec'), u'name': u'user13', u'sex': 1.0}
{u'age': 34.0, u'_id': ObjectId('4c452c343d48c8f284b388ed'), u'name': u'user14', u'sex': 0.0}
{u'age': 35.0, u'_id': ObjectId('4c452c343d48c8f284b388ee'), u'name': u'user15', u'sex': 1.0}

&gt;&gt;&gt; for u in db.users.find().sort([("age", DESCENDING)]): print u
...
{u'age': 35.0, u'_id': ObjectId('4c452c343d48c8f284b388ee'), u'name': u'user15', u'sex': 1.0}
{u'age': 34.0, u'_id': ObjectId('4c452c343d48c8f284b388ed'), u'name': u'user14', u'sex': 0.0}
{u'age': 33.0, u'_id': ObjectId('4c452c343d48c8f284b388ec'), u'name': u'user13', u'sex': 1.0}
{u'age': 32.0, u'_id': ObjectId('4c452c343d48c8f284b388eb'), u'name': u'user12', u'sex': 0.0}
{u'age': 31.0, u'_id': ObjectId('4c452c343d48c8f284b388ea'), u'name': u'user11', u'sex': 1.0}
{u'age': 30.0, u'_id': ObjectId('4c452c343d48c8f284b388e9'), u'name': u'user10', u'sex': 0.0}
{u'age': 29.0, u'_id': ObjectId('4c452c343d48c8f284b388e8'), u'name': u'user9', u'sex': 1.0}
{u'age': 28.0, u'_id': ObjectId('4c452c343d48c8f284b388e7'), u'name': u'user8', u'sex': 0.0}
{u'age': 27.0, u'_id': ObjectId('4c452c343d48c8f284b388e6'), u'name': u'user7', u'sex': 1.0}
{u'age': 26.0, u'_id': ObjectId('4c452c343d48c8f284b388e5'), u'name': u'user6', u'sex': 0.0}
{u'age': 25.0, u'_id': ObjectId('4c452c343d48c8f284b388e4'), u'name': u'user5', u'sex': 1.0}
{u'age': 24.0, u'_id': ObjectId('4c452c343d48c8f284b388e3'), u'name': u'user4', u'sex': 0.0}
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3', u'sex': 1.0}
{u'age': 22.0, u'_id': ObjectId('4c452c343d48c8f284b388e1'), u'name': u'user2', u'sex': 0.0}
{u'age': 21.0, u'_id': ObjectId('4c452c343d48c8f284b388e0'), u'name': u'user1', u'sex': 1.0}
{u'age': 20.0, u'_id': ObjectId('4c452c343d48c8f284b388df'), u'name': u'user0', u'sex': 0.0}</pre>
<p>#  select * from users order by sex asce, age desc</p>
<pre>&gt; db.users.find().sort({sex:1, age:-1})
{ "_id" : ObjectId("4c452c343d48c8f284b388ed"), "name" : "user14", "age" : 34, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388eb"), "name" : "user12", "age" : 32, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e9"), "name" : "user10", "age" : 30, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e7"), "name" : "user8", "age" : 28, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e5"), "name" : "user6", "age" : 26, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e3"), "name" : "user4", "age" : 24, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e1"), "name" : "user2", "age" : 22, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388df"), "name" : "user0", "age" : 20, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ee"), "name" : "user15", "age" : 35, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ec"), "name" : "user13", "age" : 33, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ea"), "name" : "user11", "age" : 31, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e8"), "name" : "user9", "age" : 29, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e6"), "name" : "user7", "age" : 27, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e4"), "name" : "user5", "age" : 25, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e2"), "name" : "user3", "age" : 23, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e0"), "name" : "user1", "age" : 21, "sex" : 1 }

&gt;&gt;&gt; for u in db.users.find().sort([("sex", ASCENDING), ("age", DESCENDING)]): print u

...
{u'age': 34.0, u'_id': ObjectId('4c452c343d48c8f284b388ed'), u'name': u'user14', u'sex': 0.0}
{u'age': 32.0, u'_id': ObjectId('4c452c343d48c8f284b388eb'), u'name': u'user12', u'sex': 0.0}
{u'age': 30.0, u'_id': ObjectId('4c452c343d48c8f284b388e9'), u'name': u'user10', u'sex': 0.0}
{u'age': 28.0, u'_id': ObjectId('4c452c343d48c8f284b388e7'), u'name': u'user8', u'sex': 0.0}
{u'age': 26.0, u'_id': ObjectId('4c452c343d48c8f284b388e5'), u'name': u'user6', u'sex': 0.0}
{u'age': 24.0, u'_id': ObjectId('4c452c343d48c8f284b388e3'), u'name': u'user4', u'sex': 0.0}
{u'age': 22.0, u'_id': ObjectId('4c452c343d48c8f284b388e1'), u'name': u'user2', u'sex': 0.0}
{u'age': 20.0, u'_id': ObjectId('4c452c343d48c8f284b388df'), u'name': u'user0', u'sex': 0.0}
{u'age': 35.0, u'_id': ObjectId('4c452c343d48c8f284b388ee'), u'name': u'user15', u'sex': 1.0}
{u'age': 33.0, u'_id': ObjectId('4c452c343d48c8f284b388ec'), u'name': u'user13', u'sex': 1.0}
{u'age': 31.0, u'_id': ObjectId('4c452c343d48c8f284b388ea'), u'name': u'user11', u'sex': 1.0}
{u'age': 29.0, u'_id': ObjectId('4c452c343d48c8f284b388e8'), u'name': u'user9', u'sex': 1.0}
{u'age': 27.0, u'_id': ObjectId('4c452c343d48c8f284b388e6'), u'name': u'user7', u'sex': 1.0}
{u'age': 25.0, u'_id': ObjectId('4c452c343d48c8f284b388e4'), u'name': u'user5', u'sex': 1.0}
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3', u'sex': 1.0}
{u'age': 21.0, u'_id': ObjectId('4c452c343d48c8f284b388e0'), u'name': u'user1', u'sex': 1.0}

&gt;&gt;&gt; for u in db.users.find(sort = [("sex", ASCENDING), ("age", DESCENDING)]): print u
...
{u'age': 34.0, u'_id': ObjectId('4c452c343d48c8f284b388ed'), u'name': u'user14', u'sex': 0.0}
{u'age': 32.0, u'_id': ObjectId('4c452c343d48c8f284b388eb'), u'name': u'user12', u'sex': 0.0}
{u'age': 30.0, u'_id': ObjectId('4c452c343d48c8f284b388e9'), u'name': u'user10', u'sex': 0.0}
{u'age': 28.0, u'_id': ObjectId('4c452c343d48c8f284b388e7'), u'name': u'user8', u'sex': 0.0}
{u'age': 26.0, u'_id': ObjectId('4c452c343d48c8f284b388e5'), u'name': u'user6', u'sex': 0.0}
{u'age': 24.0, u'_id': ObjectId('4c452c343d48c8f284b388e3'), u'name': u'user4', u'sex': 0.0}
{u'age': 22.0, u'_id': ObjectId('4c452c343d48c8f284b388e1'), u'name': u'user2', u'sex': 0.0}
{u'age': 20.0, u'_id': ObjectId('4c452c343d48c8f284b388df'), u'name': u'user0', u'sex': 0.0}
{u'age': 35.0, u'_id': ObjectId('4c452c343d48c8f284b388ee'), u'name': u'user15', u'sex': 1.0}
{u'age': 33.0, u'_id': ObjectId('4c452c343d48c8f284b388ec'), u'name': u'user13', u'sex': 1.0}
{u'age': 31.0, u'_id': ObjectId('4c452c343d48c8f284b388ea'), u'name': u'user11', u'sex': 1.0}
{u'age': 29.0, u'_id': ObjectId('4c452c343d48c8f284b388e8'), u'name': u'user9', u'sex': 1.0}
{u'age': 27.0, u'_id': ObjectId('4c452c343d48c8f284b388e6'), u'name': u'user7', u'sex': 1.0}
{u'age': 25.0, u'_id': ObjectId('4c452c343d48c8f284b388e4'), u'name': u'user5', u'sex': 1.0}
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3', u'sex': 1.0}
{u'age': 21.0, u'_id': ObjectId('4c452c343d48c8f284b388e0'), u'name': u'user1', u'sex': 1.0}</pre>
<p><strong>(4)  SLICE</strong></p>
<p># select * from users skip 2 limit 3</p>
<pre>&gt; db.users.find().skip(2).limit(3)
{ "_id" : ObjectId("4c452c343d48c8f284b388e1"), "name" : "user2", "age" : 22, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e2"), "name" : "user3", "age" : 23, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e3"), "name" : "user4", "age" : 24, "sex" : 0 }

&gt;&gt;&gt; for u in db.users.find().skip(2).limit(3): print u
...
{u'age': 22.0, u'_id': ObjectId('4c452c343d48c8f284b388e1'), u'name': u'user2', u'sex': 0.0}
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3', u'sex': 1.0}
{u'age': 24.0, u'_id': ObjectId('4c452c343d48c8f284b388e3'), u'name': u'user4', u'sex': 0.0}

&gt;&gt;&gt; for u in db.users.find(skip = 2, limit = 3): print u
...
{u'age': 22.0, u'_id': ObjectId('4c452c343d48c8f284b388e1'), u'name': u'user2', u'sex': 0.0}
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3', u'sex': 1.0}
{u'age': 24.0, u'_id': ObjectId('4c452c343d48c8f284b388e3'), u'name': u'user4', u'sex': 0.0}</pre>
<p>可 以用切片代替 skip &amp; limit (mongo 中的 $slice 貌似有点问题)。</p>
<pre>&gt;&gt;&gt; for u in db.users.find()[2:5]: print u
...
{u'age': 22.0, u'_id': ObjectId('4c452c343d48c8f284b388e1'), u'name': u'user2', u'sex': 0.0}
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3', u'sex': 1.0}
{u'age': 24.0, u'_id': ObjectId('4c452c343d48c8f284b388e3'), u'name': u'user4', u'sex': 0.0}</pre>
<p><strong>(5)  Conditional Operators</strong></p>
<p># select * from users where sex = 1  and age &gt; 23 and age &lt; 28</p>
<pre>&gt; db.users.find({sex:1, age:{$gt:23, $lt:28}})
{ "_id" : ObjectId("4c452c343d48c8f284b388e4"), "name" : "user5", "age" : 25, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e6"), "name" : "user7", "age" : 27, "sex" : 1 }

&gt;&gt;&gt; for u in db.users.find({"sex":1, "age":{"$gt":23, "$lt":28}}): print u
...
{u'age': 25.0, u'_id': ObjectId('4c452c343d48c8f284b388e4'), u'name': u'user5', u'sex': 1.0}
{u'age': 27.0, u'_id': ObjectId('4c452c343d48c8f284b388e6'), u'name': u'user7', u'sex': 1.0}</pre>
<p>比 较操作包括：$gt (&gt;)、$lt (&lt;)、$gte (&gt;=)、$lte(&lt;=)、$ne (!=)。</p>
<p><strong>(6)  IN</strong></p>
<p># select * from users where age in (23, 26, 32)</p>
<pre>&gt; db.users.find({age:{$in:[23,26,32]}})
{ "_id" : ObjectId("4c452c343d48c8f284b388e2"), "name" : "user3", "age" : 23, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e5"), "name" : "user6", "age" : 26, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388eb"), "name" : "user12", "age" : 32, "sex" : 0 }

&gt;&gt;&gt; for u in db.users.find({"age":{"$in":(23, 26, 32)}}): print u
...
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3', u'sex': 1.0}
{u'age': 26.0, u'_id': ObjectId('4c452c343d48c8f284b388e5'), u'name': u'user6', u'sex': 0.0}
{u'age': 32.0, u'_id': ObjectId('4c452c343d48c8f284b388eb'), u'name': u'user12', u'sex': 0.0}</pre>
<p>对 应的操作符有 $nin (not in)。</p>
<p><strong>(7) COUNT</strong></p>
<p># select count(*)  from users where age &gt; 30</p>
<pre>&gt; db.users.find({age:{$gt:30}}).count()
5

&gt;&gt;&gt; db.users.find({"age":{"$gt":30}}).count()
5</pre>
<p><strong>(8) OR</strong></p>
<p># select * from users where age = 25 or  age = 28<br />
# select * from users where age &lt;= 23 or age &gt;= 33</p>
<pre>&gt; db.users.find({$or:[{age:25}, {age:28}]})
{ "_id" : ObjectId("4c452c343d48c8f284b388e4"), "name" : "user5", "age" : 25, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e7"), "name" : "user8", "age" : 28, "sex" : 0 }

&gt; db.users.find({$or:[{age:{$lte:23}}, {age:{$gte:33}}]})
{ "_id" : ObjectId("4c452c343d48c8f284b388df"), "name" : "user0", "age" : 20, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e0"), "name" : "user1", "age" : 21, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e1"), "name" : "user2", "age" : 22, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388e2"), "name" : "user3", "age" : 23, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ec"), "name" : "user13", "age" : 33, "sex" : 1 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ed"), "name" : "user14", "age" : 34, "sex" : 0 }
{ "_id" : ObjectId("4c452c343d48c8f284b388ee"), "name" : "user15", "age" : 35, "sex" : 1 }

&gt;&gt;&gt; for u in db.users.find({"$or":[{"age":25}, {"age":28}]}): print u
...
{u'age': 25.0, u'_id': ObjectId('4c452c343d48c8f284b388e4'), u'name': u'user5', u'sex': 1.0}
{u'age': 28.0, u'_id': ObjectId('4c452c343d48c8f284b388e7'), u'name': u'user8', u'sex': 0.0}

&gt;&gt;&gt; for u in db.users.find({"$or":[{"age":{"$lte":23}}, {"age":{"$gte":33}}]}): print u
...
{u'age': 20.0, u'_id': ObjectId('4c452c343d48c8f284b388df'), u'name': u'user0', u'sex': 0.0}
{u'age': 21.0, u'_id': ObjectId('4c452c343d48c8f284b388e0'), u'name': u'user1', u'sex': 1.0}
{u'age': 22.0, u'_id': ObjectId('4c452c343d48c8f284b388e1'), u'name': u'user2', u'sex': 0.0}
{u'age': 23.0, u'_id': ObjectId('4c452c343d48c8f284b388e2'), u'name': u'user3', u'sex': 1.0}
{u'age': 33.0, u'_id': ObjectId('4c452c343d48c8f284b388ec'), u'name': u'user13', u'sex': 1.0}
{u'age': 34.0, u'_id': ObjectId('4c452c343d48c8f284b388ed'), u'name': u'user14', u'sex': 0.0}
{u'age': 35.0, u'_id': ObjectId('4c452c343d48c8f284b388ee'), u'name': u'user15', u'sex': 1.0}</pre>
<p>还 有一些复杂或专用的查询另文记录。</p>
<p><strong>3. Update</strong></p>
<p>可直接用类似 T-SQL 条件表达式更新，或用  Save() 更新从数据库返回到文档对象。</p>
<p># update users set age = 100, sex = 0 where  name = &#8216;user1&#8242;</p>
<pre>&gt; db.users.update({name:"user1"}, {$set:{age:100, sex:0}})

&gt;&gt;&gt; db.users.update({"name":"user1"}, {"$set":{"age":100, "sex":0}})</pre>
<p>update()  有几个参数需要注意。</p>
<pre>db.collection.update(criteria, objNew, upsert, mult)</pre>
<p>criteria:  需要被更新的条件表达式<br />
objNew: 更新表达式<br />
upsert: 如目标记录不存在，是否插入新文档。<br />
multi:  是否更新多个文档。</p>
<p># update users set age = age + 10</p>
<pre>&gt; db.users.update({}, {$inc:{age:10}}, false, true)

&gt;&gt;&gt; db.users.update({}, {"$inc":{"age":10}}, multi=True)</pre>
<p>#  update users set age = age + 10, sex = 1 where name = &#8216;user1&#8242;</p>
<pre>&gt; db.users.update({name:"user1"}, {$inc:{age:10}, $set:{sex:1}})

&gt;&gt;&gt; db.users.update({"name":"user1"}, {"$inc":{"age":10}, "$set":{"sex":1}})</pre>
<p>还 有一些针对集合的更新操作另文记录。</p>
<p><strong>4. Remove</strong></p>
<p>remove()  用于删除单个或全部文档，删除后的文档无法恢复。</p>
<pre>&gt; use blog
switched to db blog

&gt; for (var i = 0; i &lt; 10; i++) db.users.insert({name : "user" + i, age : 20 + i})

&gt; db.users.find()
{ "_id" : ObjectId("4c4508818c4a1e0bf570460d"), "name" : "user0", "age" : 20 }
{ "_id" : ObjectId("4c4508818c4a1e0bf570460e"), "name" : "user1", "age" : 21 }
{ "_id" : ObjectId("4c4508818c4a1e0bf570460f"), "name" : "user2", "age" : 22 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704610"), "name" : "user3", "age" : 23 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704611"), "name" : "user4", "age" : 24 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704612"), "name" : "user5", "age" : 25 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704613"), "name" : "user6", "age" : 26 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704614"), "name" : "user7", "age" : 27 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704615"), "name" : "user8", "age" : 28 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704616"), "name" : "user9", "age" : 29 }

&gt; id = db.users.findOne({name:"user2"})._id
ObjectId("4c4508818c4a1e0bf570460f")

&gt; db.users.remove(id)

&gt; db.users.find()
{ "_id" : ObjectId("4c4508818c4a1e0bf570460d"), "name" : "user0", "age" : 20 }
{ "_id" : ObjectId("4c4508818c4a1e0bf570460e"), "name" : "user1", "age" : 21 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704610"), "name" : "user3", "age" : 23 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704611"), "name" : "user4", "age" : 24 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704612"), "name" : "user5", "age" : 25 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704613"), "name" : "user6", "age" : 26 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704614"), "name" : "user7", "age" : 27 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704615"), "name" : "user8", "age" : 28 }
{ "_id" : ObjectId("4c4508818c4a1e0bf5704616"), "name" : "user9", "age" : 29 }

&gt; db.users.remove()

&gt; db.users.find()</pre>
<p>Python:</p>
<pre>&gt;&gt;&gt; for i in range(10): db.users.insert({"name":"user"+str(i), "age":20+i})
...
ObjectId('4c456e8b499b14047e000001')
ObjectId('4c456e8b499b14047e000002')
ObjectId('4c456e8b499b14047e000003')
ObjectId('4c456e8b499b14047e000004')
ObjectId('4c456e8b499b14047e000005')
ObjectId('4c456e8b499b14047e000006')
ObjectId('4c456e8b499b14047e000007')
ObjectId('4c456e8b499b14047e000008')
ObjectId('4c456e8b499b14047e000009')
ObjectId('4c456e8b499b14047e00000a')

&gt;&gt;&gt; id = db.users.find_one({"name":"user2"})["_id"]

&gt;&gt;&gt; id
ObjectId('4c456e8b499b14047e000003')

&gt;&gt;&gt; db.users.remove(id)

&gt;&gt;&gt; for u in db.users.find(): print u
...
{u'age': 20, u'_id': ObjectId('4c456e8b499b14047e000001'), u'name': u'user0'}
{u'age': 21, u'_id': ObjectId('4c456e8b499b14047e000002'), u'name': u'user1'}
{u'age': 23, u'_id': ObjectId('4c456e8b499b14047e000004'), u'name': u'user3'}
{u'age': 24, u'_id': ObjectId('4c456e8b499b14047e000005'), u'name': u'user4'}
{u'age': 25, u'_id': ObjectId('4c456e8b499b14047e000006'), u'name': u'user5'}
{u'age': 26, u'_id': ObjectId('4c456e8b499b14047e000007'), u'name': u'user6'}
{u'age': 27, u'_id': ObjectId('4c456e8b499b14047e000008'), u'name': u'user7'}
{u'age': 28, u'_id': ObjectId('4c456e8b499b14047e000009'), u'name': u'user8'}
{u'age': 29, u'_id': ObjectId('4c456e8b499b14047e00000a'), u'name': u'user9'}

&gt;&gt;&gt; db.users.remove()

&gt;&gt;&gt; for u in db.users.find(): print u
...
&gt;&gt;&gt;</pre>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5296/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5296/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5296/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5296/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5296/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5296/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB: 1. Database</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5291/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5291/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 17:00:22 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5291</guid>
		<description><![CDATA[mongo 是 MongoDB 自带的交互式 Javascript shell，用来对 Mongod 进行操作和管理的交互式环境。 使用 &#8220;./mongo &#8211;help&#8221; 可查看相关连接参数。 $ ./mongo --help MongoDB shell version: 1.5.3 usage: ./mongo [options] [db address] [file names (ending in .js)] db address can be: foo foo database on local machine 192.169.0.5/foo foo database on 192.168.0.5 machine 192.169.0.5:9999/foo foo database on 192.168.0.5 machine on port 9999 options: [...]]]></description>
			<content:encoded><![CDATA[<p>mongo 是 <a title="http://www.mongodb.org/" href="http://www.mongodb.org/" target="_blank">MongoDB</a> 自带的交互式  Javascript shell，用来对 Mongod 进行操作和管理的交互式环境。</p>
<p>使用 &#8220;./mongo &#8211;help&#8221;  可查看相关连接参数。</p>
<pre>$ ./mongo --help

<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">MongoDB</a> shell version: 1.5.3

usage: ./mongo [options] [db address] [file names (ending in .js)]

db address can be:
  foo                   foo <a href="http://www.iwanna.cn/tags/db/" class="st_tag internal_tag" rel="tag" title="标签 Database 下的日志">database</a> on local machine
  192.169.0.5/foo       foo <a href="http://www.iwanna.cn/tags/db/" class="st_tag internal_tag" rel="tag" title="标签 Database 下的日志">database</a> on 192.168.0.5 machine
  192.169.0.5:9999/foo  foo <a href="http://www.iwanna.cn/tags/db/" class="st_tag internal_tag" rel="tag" title="标签 Database 下的日志">database</a> on 192.168.0.5 machine on port 9999

options:
  --shell               run the shell after executing files
  --nodb                don't connect to mongod on startup - no 'db address'
                        arg expected
  --quiet               be less chatty
  --port arg            port to connect to
  --host arg            server to connect to
  --eval arg            evaluate javascript
  -u [ --username ] arg username for authentication
  -p [ --password ] arg password for authentication
  -h [ --help ]         show this usage information
  --version             show version information
  --ipv6                enable IPv6 support (disabled by default)

file names: a list of files to run. files have to end in .js and will exit after unless --shell is specified</pre>
<p><span id="more-5291"></span><br />
相关命令很多，要习惯使用 &#8220;help&#8221;。</p>
<pre>$ ./mongo

MongoDB shell version: 1.5.3
connecting to: test
type "help" for help

&gt; help
        help admin                   misc shell commands
        show dbs                     show database names
        show collections             show collections in current database
        show users                   show users in current database
        show profile                 show most recent system.profile entries with time &gt;= 1ms
        use &lt;db name&gt;                set current database to &lt;db name&gt;
        db.help()                    help on DB methods
        db.foo.help()                help on collection methods
        db.foo.find()                list objects in collection foo
        db.foo.find( { a : 1 } )     list objects in foo where a == 1
        it                           result of the last line evaluated; use to further iterate
        exit                         quit the mongo shell</pre>
<p>(1)  MongoDB 会自动创建数据库(db)和集合(collection)，无需显式执行。</p>
<pre>$ ./mongo

MongoDB shell version: 1.5.3
connecting to: test
type "help" for help

&gt; show dbs // 查看当前数据库列表
admin
local

&gt; use blog // 切换到工作数据库
switched to db blog

&gt; db // 当前数据库
blog

&gt; for (var i = 0; i &lt; 10; i++) db.users.save({name : "user" + i, age : i}) // 插入数据

&gt; show dbs // 数据库 blog 被创建
admin
blog
local

&gt; show collections // 列表 users 被创建
system.indexes
users

&gt; db.copyDatabase("blog", "blog2") // 复制数据库
{ "ok" : true }

&gt; show dbs // 数据库 blog2 被创建
admin
blog
blog2
local

&gt; use blog2 // 切换到 blog2
switched to db blog2

&gt; show collections // 查看集合列表
system.indexes
users

&gt; db.users.find() // 查看被复制的数据
{ "_id" : ObjectId("4c33f8fcecf2b9320ac2981a"), "name" : "user0", "age" : 0 }
{ "_id" : ObjectId("4c33f8fcecf2b9320ac2981b"), "name" : "user1", "age" : 1 }
{ "_id" : ObjectId("4c33f8fcecf2b9320ac2981c"), "name" : "user2", "age" : 2 }
{ "_id" : ObjectId("4c33f8fcecf2b9320ac2981d"), "name" : "user3", "age" : 3 }
{ "_id" : ObjectId("4c33f8fcecf2b9320ac2981e"), "name" : "user4", "age" : 4 }
{ "_id" : ObjectId("4c33f8fcecf2b9320ac2981f"), "name" : "user5", "age" : 5 }
{ "_id" : ObjectId("4c33f8fcecf2b9320ac29820"), "name" : "user6", "age" : 6 }
{ "_id" : ObjectId("4c33f8fcecf2b9320ac29821"), "name" : "user7", "age" : 7 }
{ "_id" : ObjectId("4c33f8fcecf2b9320ac29822"), "name" : "user8", "age" : 8 }
{ "_id" : ObjectId("4c33f8fcecf2b9320ac29823"), "name" : "user9", "age" : 9 }

&gt; db.dropDatabase() // 删除数据库 blog2
{ "dropped" : "blog2", "ok" : true }

&gt; show dbs // 确认数据库删除成功
admin
blog
local

&gt; use blog // 切换回 blog
switched to db blog

&gt; db.users.drop()  // 删除集合 users
true

&gt; show collections // 确认集合被删除
system.indexes

&gt; exit
bye</pre>
<p>(2) 还可以在多台服务器之间复制数据库。</p>
<pre>server64$ ./mongo

MongoDB shell version: 1.5.3
connecting to: test
type "help" for help

&gt; use blog
switched to db blog

&gt; for (var i = 0; i &lt; 10; i++) db.users.save({name : "user" + i, age : i})

&gt; use news
switched to db news

&gt; for (var i = 0; i &lt; 10; i++) db.articles.save({title : "title" + i})

&gt; show dbs
admin
blog
local
news

&gt; exit
bye</pre>
<p>准备好源数据库后，我们开始在复制。</p>
<pre>server32:$ ./mongo

MongoDB shell version: 1.5.4
connecting to: test

&gt; db.copyDatabase("blog", "blog", "192.168.1.202") // 从源服务器复制 blog 数据库
{ "ok" : true }

&gt; show dbs // 复制成功
admin
blog
local

&gt; use blog
switched to db blog

&gt; show collections
system.indexes
users

&gt; db.users.find()
{ "_id" : ObjectId("4c33fadb15b7f104d297e644"), "name" : "user0", "age" : 0 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e645"), "name" : "user1", "age" : 1 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e646"), "name" : "user2", "age" : 2 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e647"), "name" : "user3", "age" : 3 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e648"), "name" : "user4", "age" : 4 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e649"), "name" : "user5", "age" : 5 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e64a"), "name" : "user6", "age" : 6 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e64b"), "name" : "user7", "age" : 7 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e64c"), "name" : "user8", "age" : 8 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e64d"), "name" : "user9", "age" : 9 }

&gt; use news
switched to db news

&gt; db.cloneDatabase("192.168.1.202") // 从源服务器克隆当前数据库(news)
{ "ok" : true }

&gt; show dbs
admin
blog
local
news

&gt; show collections
articles
system.indexes

&gt; db.articles.find()
{ "_id" : ObjectId("4c33fb6215b7f104d297e64e"), "title" : "title0" }
{ "_id" : ObjectId("4c33fb6215b7f104d297e64f"), "title" : "title1" }
{ "_id" : ObjectId("4c33fb6215b7f104d297e650"), "title" : "title2" }
{ "_id" : ObjectId("4c33fb6215b7f104d297e651"), "title" : "title3" }
{ "_id" : ObjectId("4c33fb6215b7f104d297e652"), "title" : "title4" }
{ "_id" : ObjectId("4c33fb6215b7f104d297e653"), "title" : "title5" }
{ "_id" : ObjectId("4c33fb6215b7f104d297e654"), "title" : "title6" }
{ "_id" : ObjectId("4c33fb6215b7f104d297e655"), "title" : "title7" }
{ "_id" : ObjectId("4c33fb6215b7f104d297e656"), "title" : "title8" }
{ "_id" : ObjectId("4c33fb6215b7f104d297e657"), "title" : "title9" }

&gt; exit
bye</pre>
<p>(3) 当我们使用 use 切换到某个数据库时，变量 db 表示当前数据库。还可以用 getSisterDB()  函数获取其他数据库的引用。</p>
<pre>&gt; use admin
switched to db admin

&gt; db
admin

&gt; blog = db.getSisterDB("blog")
blog

&gt; blog.users.insert({name : "abc"})

&gt; blog.users.find({name : "abc"})
{ "_id" : ObjectId("4c3419b0492aa4cfbec11895"), "name" : "abc" }</pre>
<p>(4)  调用 fsync 命令，可以强制将内存中缓存数据写回数据库文件。如果不想等待，可添加 async 参数异步执行。</p>
<pre>&gt; use admin
switched to db admin

&gt; db.runCommand({fsync : 1})
{ "numFiles" : 6, "ok" : true }

&gt; db.runCommand({fsync : 1, async : true})
{ "numFiles" : 6, "ok" : true }</pre>
<p>(5)  某些时候需要锁定系统，阻塞所有写操作，诸如备份、整理数据库等等。锁定时读操作不受影响。</p>
<pre>$ ./mongo

MongoDB shell version: 1.5.3
connecting to: test
type "help" for help

&gt; use blog
switched to db blog

&gt; admin = db.getSisterDB("admin")
admin

&gt; admin.runCommand({fsync : 1, lock : 1}) // 锁定
{
        "info" : "now locked against writes, use db.$cmd.sys.unlock.findOne() to unlock",
        "ok" : true
}

&gt; db.users.find() // 读操作正常

{ "_id" : ObjectId("4c33fadb15b7f104d297e644"), "name" : "user0", "age" : 0 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e645"), "name" : "user1", "age" : 1 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e646"), "name" : "user2", "age" : 2 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e647"), "name" : "user3", "age" : 3 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e648"), "name" : "user4", "age" : 4 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e649"), "name" : "user5", "age" : 5 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e64a"), "name" : "user6", "age" : 6 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e64b"), "name" : "user7", "age" : 7 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e64c"), "name" : "user8", "age" : 8 }
{ "_id" : ObjectId("4c33fadb15b7f104d297e64d"), "name" : "user9", "age" : 9 }

&gt; db.users.save({name : "xyz" }) // 写操作被阻塞，等待 ...</pre>
<p>另开启一个终端，解除 锁定。</p>
<pre>&gt; use admin
switched to db admin

&gt; db.$cmd.sys.unlock.findOne()
{ "ok" : 1, "info" : "unlock requested" }</pre>
<p>解除后，前一终端被阻塞的写操作正确返回。</p>
<p>(6)  调用 validate() 验证集合是否存在错误。</p>
<pre>&gt; db.users.validate()
{
        "ns" : "blog.users",
        "result" : "
validate
  firstExtent:0:2600 ns:blog.users
  lastExtent:0:23d00 ns:blog.users
  # extents:2
  datasize?:4640 nrecords?:116 lastExtentSize:9216
  padding:1
  first extent:
    loc:0:2600 xnext:0:23d00 xprev:null
    nsdiag:blog.users
    size:2304 firstRecord:0:26b0 lastRecord:0:2ec8
  116 objects found, nobj:116
  6496 bytes data w/headers
  4640 bytes data wout/headers
  deletedList: 0000000010000000000
  deleted: n: 1 size: 4672
  nIndexes:1
    blog.users.$_id_ keys:116
",
        "ok" : true,
        "valid" : true,
        "lastExtentSize" : 9216
}</pre>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5291/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5291/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5291/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5291/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5291/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5291/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>选择哪个MongoDB插件？</title>
		<link>http://www.iwanna.cn/archives/2010/09/17/5285/</link>
		<comments>http://www.iwanna.cn/archives/2010/09/17/5285/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 16:42:58 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[NoSQL]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=5285</guid>
		<description><![CDATA[相信MongoDB的名头不少人 已经听说过了，虽然已有针对它的Java驱动，但我敢打保票，只要你用过一次，很快就会怀念使用GORM的好时光。拜Grails的插件架构所赐，对于Grails项目来说，这不再是幻想。目前在Grails社区至少已经有两个针对MongoDB的插件：MongoDB Tools和Grails MongoDB plugin，我们该选择哪一个呢？ 在查看了两个插件的信息后，我坚定地选择了后者：Grails MongoDB plugin。 原因无它：完全保留了GORM的使用习惯，而且特性几乎一样： 实例方法：save、delete 类方法：get、find、findAll、delete、deleteAll、exists、count、list 诸如findAllByNameAndDegreeBetween(&#8230;)这样的动态查找器 支持约束和验证 支持自动时辍：dateCreated和lastUpdated Gorm事件：beforeSave、afterSave、beforeDelete、afterDelete（还有Morphia支持的 afterLoad） 支持MongoDB 1.4和1.5 提供了注入所有必需字段和Morphia注解的AST Transformations 支持延迟引用 支持generate-all 注意最后一个点，这个插件甚至支持了脚手架代码的产生！反观MongoDB Tools，看完它的使用示例之 后，我的第一感受就是：可能能起作用，但用起来实在是太难看了。限于篇幅，我就不在这里列出它的例子，有兴趣了解其实现究竟的，可前往观之。对于Grails MongoDB plugin的基本使用，其实跟使用Domain Class一样： 安装插件：grails install-plugin gorm-mongodb 在Config.groovy里配置数据源： mongodb { host = '192.168.1.36' port = 27017 database = 'test' } 创建MongoDB领域类：grails create-mongodb-class 领域类名 象平常一样编辑领域类 产生脚手架代码：grails generate-all 领域类名 是不是非常简单？你还可以利用grails的template机制来定制你自己的MongoDB领域类：在工程的src\templates \artifacts目录下创建一个MongoDBClass.groovy文件，如： @artifact.package@import grails.plugins.mongodb.MongoEntity [...]]]></description>
			<content:encoded><![CDATA[<p>相信<a href="http://www.mongodb.org/" target="_blank">MongoDB</a>的名头不少人 已经听说过了，虽然已有针对它的<a href="http://github.com/mongodb/mongo-java-driver" target="_blank">Java驱动</a>，但我敢打保票，只要你用过一次，很快就会怀念使用GORM的好时光。拜Grails的插件架构所赐，对于Grails项目来说，这不再是幻想。目前在Grails社区至少已经有两个针对MongoDB的插件：<a href="http://grails.org/plugin/mongodb-tools" target="_blank">MongoDB Tools</a>和<a href="http://www.grails.org/plugin/gorm-mongodb" target="_blank">Grails  MongoDB plugin</a>，我们该选择哪一个呢？</p>
<p>在查看了两个插件的信息后，我坚定地选择了后者：<a href="http://www.grails.org/plugin/gorm-mongodb" target="_blank">Grails MongoDB plugin</a>。 原因无它：完全保留了GORM的使用习惯，而且特性几乎一样：</p>
<blockquote>
<ul>
<li>实例方法：save、delete</li>
<li>类方法：get、find、findAll、delete、deleteAll、exists、count、list</li>
<li>诸如findAllByNameAndDegreeBetween(&#8230;)这样的动态查找器</li>
<li>支持约束和验证</li>
<li>支持自动时辍：dateCreated和lastUpdated</li>
<li>Gorm事件：beforeSave、afterSave、beforeDelete、afterDelete（还有Morphia支持的 afterLoad）</li>
<li>支持MongoDB 1.4和1.5</li>
<li>提供了注入所有必需字段和Morphia注解的AST Transformations</li>
<li>支持延迟引用</li>
<li>支持generate-all</li>
</ul>
</blockquote>
<p><span id="more-5285"></span><br />
注意最后一个点，这个插件甚至支持了脚手架代码的产生！反观<a href="http://grails.org/plugin/mongodb-tools" target="_blank">MongoDB Tools</a>，看完它的<a href="http://github.com/mpriatel/mongodb-grails" target="_blank">使用示例</a>之 后，我的第一感受就是：可能能起作用，但用起来实在是太难看了。限于篇幅，我就不在这里列出它的例子，有兴趣了解其实现究竟的，可前往观之。对于<a href="http://www.grails.org/plugin/gorm-mongodb" target="_blank">Grails  MongoDB plugin</a>的基本使用，其实跟使用Domain Class一样：</p>
<ul>
<li>安装插件：grails install-<a href="http://www.iwanna.cn/tags/plugin/" class="st_tag internal_tag" rel="tag" title="标签 Plugin 下的日志">plugin</a> gorm-<a href="http://www.iwanna.cn/tags/mongodb/" class="st_tag internal_tag" rel="tag" title="标签 MongoDB 下的日志">mongodb</a></li>
<li>在Config.groovy里配置数据源：</li>
<blockquote>
<pre title="code">            mongodb {
                host = '192.168.1.36'
                port = 27017
                <a href="http://www.iwanna.cn/tags/db/" class="st_tag internal_tag" rel="tag" title="标签 Database 下的日志">database</a> = 'test'
            }
</pre>
</blockquote>
<li>创建MongoDB领域类：grails create-mongodb-class 领域类名</li>
<li>象平常一样编辑领域类</li>
<li>产生脚手架代码：grails generate-all 领域类名</li>
</ul>
<p>是不是非常简单？你还可以利用grails的template机制来定制你自己的MongoDB领域类：在工程的src\templates \artifacts目录下创建一个MongoDBClass.groovy文件，如：</p>
<pre title="code">@artifact.package@import grails.plugins.mongodb.MongoEntity

@MongoEntity
class @artifact.name@ {

    Date dateCreated
    Date lastUpdated

    int version

    static constraints = {
    }
}
</pre>
<p>如此，你每次创建出的MongoDB领域类就都会包含上述3个字段，即具有时辍和版本。</p>
<p>除了这种高级别的抽象，要是你想访问底层支持类，如Morphia或MongoDB的Java驱动类，这个插件同样提供了支持，相关细节请查看<a href="http://jkuehn.github.com/gorm-mongodb/guide/5.%20Low%20Level%20Access.html" target="_blank">这 篇文档</a>。其余信息则可以通过它的<a href="http://jkuehn.github.com/gorm-mongodb/" target="_blank">用户参考</a>了解。</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/09/17/5285/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/09/17/5285/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/09/17/5285/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/09/17/5285/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/09/17/5285/">抓虾</a>
<hr />
<script type="text/javascript"><!--
google_ad_client = "pub-2057344547305288";
/* 336x280,iwanna feed,created 10/3/10 */
google_ad_slot = "9738886183";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<hr />
</p>
<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/09/17/5285/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

