<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_剑飘红</title><subtitle type="text">一身   是胆一起轰轰烈烈的干翻千翻 拐千湾你和我是不见不散故事在线</subtitle><id>http://feed.cnblogs.com/blog/u/20514/rss</id><updated>2012-05-28T08:42:27Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/20514/rss"/><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/28/2521542.html</id><title type="text">如何避免死锁</title><summary type="text">什么是死锁，如何避免死锁？ 线程A需要资源X，而线程B需要资源Y，而双方都掌握有对方所要的资源，这种情况称为死锁（deadlock），或死亡拥抱（the deadly embrace）。在并发程序设计中，死锁 (deadlock) 是一种十分常见的逻辑错误。通过采用正确的编程方式，死锁的发生不难避免。 死锁的四个必要条件－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－在计算机专业的本科教材中，通常都会介绍死锁的四个必要条件。这四个条件缺一不可，或者说只要破坏了其中任何一个条件，死锁就不可能发生。我们来复习一下，这四个条件是： 互斥（Mutual exclusion）：存在这样一种资</summary><published>2012-05-28T08:42:00Z</published><updated>2012-05-28T08:42:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/28/2521542.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/28/2521542.html"/><content type="html">&lt;p&gt;&lt;font size="4"&gt;&lt;strong&gt;什么是死锁，如何避免死锁？&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 线程A需要资源X，而线程B需要资源Y，而双方都掌握有对方所要的资源，这种情况称为死锁（deadlock），或死亡拥抱（the deadly embrace）。&lt;br /&gt;&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font size="4"&gt;&lt;strong&gt;在并发程序设计中，死锁 (deadlock) 是一种十分常见的逻辑错误。通过采用正确的编程方式，死锁的发生不难避免。 &lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;a name="死锁的四个必要条件"&gt;&lt;font color="#ff0000" size="4"&gt;&lt;strong&gt;死锁的四个必要条件&lt;/strong&gt;&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;font size="4"&gt;&lt;strong&gt;－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;div class="level2"&gt;&lt;p&gt;&lt;font size="4"&gt;&lt;strong&gt;在计算机专业的本科教材中，通常都会介绍死锁的四个必要条件。这四个条件缺一不可，或者说只要破坏了其中任何一个条件，死锁就不可能发生。我们来复习一下，这四个条件是： &lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;ol&gt;&lt;li class="level1"&gt;&lt;div class="li"&gt;&lt;font size="4"&gt;&lt;strong&gt;互斥（Mutual exclusion）：存在这样一种资源，它在某个时刻只能被分配给一个执行绪（也称为线程）使用；&lt;/strong&gt;&lt;/font&gt;&lt;/div&gt;&lt;/li&gt;&lt;li class="level1"&gt;&lt;div class="li"&gt;&lt;font size="4"&gt;&lt;strong&gt;持有（Hold and wait）：当请求的资源已被占用从而导致执行绪阻塞时，资源占用者不但无需释放该资源，而且还可以继续请求更多资源；&lt;/strong&gt;&lt;/font&gt;&lt;/div&gt;&lt;/li&gt;&lt;li class="level1"&gt;&lt;div class="li"&gt;&lt;font size="4"&gt;&lt;strong&gt;不可剥夺（No preemption）：执行绪获得到的互斥资源不可被强行剥夺，换句话说，只有资源占用者自己才能释放资源；&lt;/strong&gt;&lt;/font&gt;&lt;/div&gt;&lt;/li&gt;&lt;li class="level1"&gt;&lt;div class="li"&gt;&lt;font size="4"&gt;&lt;strong&gt;环形等待（Circular wait）：若干执行绪以不同的次序获取互斥资源，从而形成环形等待的局面，想象在由多个执行绪组成的环形链中，每个执行绪都在等待下一个执行绪释放它持有的资源。&lt;/strong&gt;&lt;/font&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;a name="解除死锁的必要条件"&gt;&lt;font color="#0000ff" size="4"&gt;解除死锁的必要条件&lt;/font&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;font size="4"&gt;&lt;strong&gt;不难看出，在死锁的四个必要条件中，第二、三和四项条件比较容易消除。通过引入事务机制，往往可以消除第二、三两项条件，方法是将所有上锁操作均作为事务对待，一旦开始上锁，即确保全部操作均可回退，同时通过锁管理器检测死锁，并剥夺资源（回退事务）。这种做法有时会造成较大开销，而且也需要对上锁模式进行较多改动。 &lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font size="4"&gt;&lt;strong&gt;消除第四项条件是比较容易且代价较低的办法。具体来说这种方法约定：上锁的顺序必须一致。具体来说，我们人为地给锁指定一种类似&amp;#8220;水位&amp;#8221;的方向性属性。无论已持有任何锁，该执行绪所有的上锁操作，必须按照一致的先后顺序从低到高（或从高到低）进行，且在一个系统中，只允许使用一种先后次序。 &lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font size="4"&gt;&lt;strong&gt;请注意，放锁的顺序并不会导致死锁。也就是说，尽管按照 锁A, 锁B, 放A, 放B 这样的顺序来进行锁操作看上去有些怪异，但是只要大家都按先A后B的顺序上锁，便不会导致死锁。&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000" size="4"&gt;&lt;strong&gt;解决方法：&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font size="4"&gt;&lt;strong&gt;1 使用事务时，尽量缩短事务的逻辑处理过程，及早提交或回滚事务； (细化处理逻辑，执行一段逻辑后便回滚或者提交，然后再执行其它逻辑，直到事物执行完毕提交)&lt;br /&gt;2 设置死锁超时参数为合理范围，如：3分钟-10分种；超过时间，自动放弃本次操作，避免进程悬挂； &lt;br /&gt;3 优化程序，检查并避免死锁现象出现； &lt;br /&gt;4 .对所有的脚本和SP都要仔细测试，在正是版本之前。 &lt;br /&gt;5 所有的SP都要有错误处理（通过@error） &lt;br /&gt;6 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁 &lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#0000ff" size="4"&gt;&lt;strong&gt;另外参考的解决方法：&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font size="4"&gt;&lt;strong&gt;按同一顺序访问对象&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果所有并发事务按同一顺序访问对象，则发生死锁的可能性会降低。例如，如果两个并发事务获得 Supplier 表上的锁，然后获得 Part 表上的锁，则在其中一个事务完成之前，另一个事务被阻塞在 Supplier 表上。第一个事务提交或回滚后，第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序。&lt;/strong&gt;&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font size="4"&gt;&lt;strong&gt;避免事务中的用户交互&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 避免编写包含用户交互的事务，因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度，例如答复应用程序请求参数的提示。例如，如果事务正在等待用户输入，而用户去吃午餐了或者甚至回家过周末了，则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量，因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况，访问同一资源的其它事务也会被阻塞，等待该事务完成。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 保持事务简短并在一个批处理中&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长，其持有排它锁或更新锁的时间也就越长，从而堵塞了其它活动并可能导致死锁。保持事务在一个批处理中，可以最小化事务的网络通信往返量，减少完成事务可能的延迟并释放锁。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 使用低隔离级别&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取（未修改）的数据，而不必等待第一个事务完成。使用较低的隔离级别（例如提交读）而不使用较高的隔离级别（例如可串行读）可以缩短持有共享锁的时间，从而降低了锁定争夺。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 使用绑定连接&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有，反之亦然，因此不会相互阻塞。&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;div style="display: none"&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2521542.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/28/2521542.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507779.html</id><title type="text">mongodb mapreduce学习</title><summary type="text">for( var i = 1; i &lt; 10001; i++){db.students.insert({classid:i%400, age:i%100, name:'Tom'+i})}db.students.insert({classid:1,age:14,name:'Tom'})db.students.insert({classid:1,age:12,name:'Jacky'})db.students.insert({classid:2,age:16,name:'Lily'})db.students.insert({cl</summary><published>2012-05-18T06:58:00Z</published><updated>2012-05-18T06:58:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507779.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507779.html"/><content type="html">&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;for( var i = 1; i &amp;lt; 10001; i++){&lt;br /&gt;&amp;nbsp;db.students.insert({classid:i%400, age:i%100, name:'Tom'+i})&lt;br /&gt;}&lt;/div&gt;&lt;div&gt;db.students.insert({classid:1,&amp;nbsp;age:14,&amp;nbsp;name:'Tom'})&lt;br /&gt;db.students.insert({classid:1,&amp;nbsp;age:12,&amp;nbsp;name:'Jacky'})&lt;br /&gt;db.students.insert({classid:2,&amp;nbsp;age:16,&amp;nbsp;name:'Lily'})&lt;br /&gt;db.students.insert({classid:2,&amp;nbsp;age:9,&amp;nbsp;name:'Tony'})&lt;br /&gt;db.students.insert({classid:2,&amp;nbsp;age:19,&amp;nbsp;name:'Harry'})&lt;br /&gt;db.students.insert({classid:2,&amp;nbsp;age:13,&amp;nbsp;name:'Vincent'})&lt;br /&gt;db.students.insert({classid:1,&amp;nbsp;age:14,&amp;nbsp;name:'Bill'})&lt;br /&gt;db.students.insert({classid:2,&amp;nbsp;age:17,&amp;nbsp;name:'Bruce'})&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;m&amp;nbsp;=&amp;nbsp;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt;()&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;emit(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.classid,&amp;nbsp;1)&amp;nbsp;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;r&amp;nbsp;=&amp;nbsp;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(key,&amp;nbsp;values)&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt;&amp;nbsp;x&amp;nbsp;=&amp;nbsp;0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;values.forEach(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(v)&amp;nbsp;{&amp;nbsp;x&amp;nbsp;+=&amp;nbsp;v&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000ff"&gt;return&lt;/span&gt;&amp;nbsp;x;&lt;br /&gt;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;res&amp;nbsp;=&amp;nbsp;db.runCommand({&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mapreduce:"students",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;map:m,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reduce:r,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out:"students_res"&lt;br /&gt;&amp;nbsp;});&lt;br /&gt;&lt;br /&gt;db.students_res.find()&lt;br /&gt;&lt;br /&gt;m&amp;nbsp;=&amp;nbsp;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt;()&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;emit(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.classid,&amp;nbsp;{cnt:1,name:&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.name})&amp;nbsp;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;r&amp;nbsp;=&amp;nbsp;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(key,&amp;nbsp;values)&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt;&amp;nbsp;x&amp;nbsp;=&amp;nbsp;0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt;&amp;nbsp;names="";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;values.forEach(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(v)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;x&amp;nbsp;+=&amp;nbsp;v.cnt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;names+=v.name;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000ff"&gt;return&lt;/span&gt;&amp;nbsp;{classid:key,cnt:x,names:names};&lt;br /&gt;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;res&amp;nbsp;=&amp;nbsp;db.runCommand({&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mapreduce:"students",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;map:m,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reduce:r,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out:"students_res"&lt;br /&gt;&amp;nbsp;});&lt;br /&gt;&lt;br /&gt;db.students_res.find()&lt;br /&gt;&lt;br /&gt;f&amp;nbsp;=&amp;nbsp;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(key,&amp;nbsp;value)&amp;nbsp;{&amp;nbsp;&lt;span style="color: #0000ff"&gt;return&lt;/span&gt;&amp;nbsp;{classid:key,&amp;nbsp;count:value};&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;res&amp;nbsp;=&amp;nbsp;db.runCommand({&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mapreduce:"students",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;map:m,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reduce:r,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out:"students_res",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;finalize:f&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;db.students_res.find()&lt;br /&gt;&lt;br /&gt;res&amp;nbsp;=&amp;nbsp;db.runCommand({&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mapreduce:"students",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;map:m,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reduce:r,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out:"students_res",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;finalize:f,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;query:{age:{$lt:10}}&lt;br /&gt;&amp;nbsp;});&lt;br /&gt;&lt;br /&gt;db.students_res.find()&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2507779.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507779.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507777.html</id><title type="text">mongoDB配制及学习</title><summary type="text">mongoDB配制及学习分类： 数据库 2011-12-26 14:20 387人阅读 评论(0) 收藏 举报第一部分 基础篇第一章 走进MongoDBMongoDB 是一个高性能，开源，无模式的文档型数据库，是当前NoSQL 数据库产品中最热门的一种。它在许多场景下可用于替代传统的关系型数据库或键/值存储方式，MongoDB 使用C++开发。MongoDB 的官方网站地址是：http://www.mongodb.org/，读者朋友们可以在此获得更详细的信息。6 / 911.1 为什么要用NoSQL1.1.1 NoSQL简介NoSQL，全称是”Not Only Sql”,指的是非关系型的数据库</summary><published>2012-05-18T06:57:00Z</published><updated>2012-05-18T06:57:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507777.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507777.html"/><content type="html">mongoDB配制及学习&lt;br /&gt;分类： 数据库 2011-12-26 14:20 387人阅读 评论(0) 收藏 举报&lt;br /&gt;第一部分 基础篇&lt;br /&gt;第一章 走进MongoDB&lt;br /&gt;MongoDB 是一个高性能，开源，无模式的文档型数据库，是当前NoSQL 数据库产品中最热&lt;br /&gt;门的一种。它在许多场景下可用于替代传统的关系型数据库或键/值存储方式，MongoDB 使&lt;br /&gt;用C++开发。MongoDB 的官方网站地址是：http://www.mongodb.org/，读者朋友们可以在&lt;br /&gt;此获得更详细的信息。&lt;br /&gt;&lt;br /&gt;6 / 91&lt;br /&gt;1.1 为什么要用NoSQL&lt;br /&gt;1.1.1 NoSQL简介&lt;br /&gt;NoSQL，全称是&amp;#8221;Not Only Sql&amp;#8221;,指的是非关系型的数据库。这类数据库主要有这些特点：非关&lt;br /&gt;系型的、分布式的、开源的、水平可扩展的。原始的目的是为了大规模web 应用，这场全&lt;br /&gt;新的数据库革命运动早期就有人提出，发展至2009 年趋势越发高涨。NoSQL 的拥护者们提&lt;br /&gt;倡运用非关系型的数据存储，通常的应用如：模式自由、支持简易复制、简单的API、最终&lt;br /&gt;的一致性（非ACID）、大容量数据等。NoSQL 被我们用得最多的当数key-value 存储，当然还&lt;br /&gt;有其他的文档型的、列存储、图型数据库、xml 数据库等。相对于目前铺天盖地的关系型数&lt;br /&gt;据库运用，这一概念无疑是一种全新思维的注入。&lt;br /&gt;1.1.2 发展现状&lt;br /&gt;现今的计算机体系结构在数据存储方面要求应用架构具备庞大的水平扩展性，而NoSQL 正&lt;br /&gt;在致力于改变这一现状。目前新浪微博的Redis 和Google 的Bigtable 以及Amazon 的SimpleDB&lt;br /&gt;使用的就是NoSQL 型数据库。&lt;br /&gt;NoSQL 项目的名字上看不出什么相同之处，但是，它们通常在某些方面相同：它们可以处理&lt;br /&gt;超大量的数据。&lt;br /&gt;这场革命目前仍然需要等待。NoSQL 对大型企业来说还不是主流，但是，一两年之后很可能&lt;br /&gt;就会变个样子。在NoSQL 运动的最新一次聚会中，来自世界各地的150 人挤满了CBS&lt;br /&gt;Interactive 的一间会议室。分享他们如何推翻缓慢而昂贵的关系数据库的暴政，怎样使用更&lt;br /&gt;有效和更便宜的方法来管理数据。&lt;br /&gt;关系型数据库给你强加了太多东西。它们要你强行修改对象数据，以满足数据库系统的需要。&lt;br /&gt;在NoSQL 拥护者们来看，基于NoSQL 的数据库替代方案&amp;#8220;只是给你所需要的&amp;#8221;。&lt;br /&gt;1.1.3 为什么是NoSQL&lt;br /&gt;随着互联网web2.0 网站的兴起，非关系型的数据库现在成了一个极其热门的新领域，非关&lt;br /&gt;&lt;br /&gt;7 / 91&lt;br /&gt;系数据库产品的发展非常迅速，而传统的关系型数据库在应付web2.0 网站，特别是超大规&lt;br /&gt;模和高并发的SNS 类型的web2.0 纯动态网站已经显得力不从心，暴露了很多难以克服的问&lt;br /&gt;题，例如：&lt;br /&gt;1、 High performance - 对数据库高并发读写的需求&lt;br /&gt;web2.0 网站要根据用户个性化信息来实时生成动态页面和提供动态信息，所以基本上无法&lt;br /&gt;使用动态页面静态化技术，因此数据库并发负载非常高，往往要达到每秒上万次读写请求。&lt;br /&gt;关系型数据库应付上万次SQL 查询还勉强顶得住，但是应付上万次SQL 写数据请求，硬盘&lt;br /&gt;IO 就已经无法承受了，其实对于普通的BBS 网站，往往也存在对高并发写请求的需求。&lt;br /&gt;2、Huge Storage - 对海量数据的高效率存储和访问的需求&lt;br /&gt;对于大型的SNS 网站，每天用户产生海量的用户动态信息，以国外的Friend feed 为例，一&lt;br /&gt;个月就达到了2.5 亿条用户动态，对于关系数据库来说，在一张2.5 亿条记录的表里面进行&lt;br /&gt;SQL 查询，效率是极其低下乃至不可忍受的。再例如大型web 网站的用户登录系统，例如腾&lt;br /&gt;讯，盛大，动辄数以亿计的帐号，关系数据库也很难应付。&lt;br /&gt;3、High Scalability &amp;amp;&amp;amp; High Availability - 对数据库的高可扩展性和高可用性的需求&lt;br /&gt;在基于web 的架构当中，数据库是最难进行横向扩展的，当一个应用系统的用户量和访问&lt;br /&gt;量与日俱增的时候，你的数据库却没有办法像web server 和app server 那样简单的通过添加&lt;br /&gt;更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供24 小时不间断服务的网&lt;br /&gt;站来说，对数据库系统进行升级和扩展是非常痛苦的事情，往往需要停机维护和数据迁移，&lt;br /&gt;可是停机维护随之带来的就是公司收入的减少。&lt;br /&gt;在上面提到的&amp;#8220;三高&amp;#8221;需求面前，关系数据库遇到了难以克服的障碍，而对于web2.0 网站&lt;br /&gt;来说，关系数据库的很多主要特性却往往无用武之地，例如：&lt;br /&gt;1、数据库事务一致性需求&lt;br /&gt;很多web 实时系统并不要求严格的数据库事务，对读一致性的要求很低，有些场合对写一&lt;br /&gt;致性要求也不高。因此数据库事务管理成了数据库高负载下一个沉重的负担。&lt;br /&gt;2、数据库的写实时性和读实时性需求&lt;br /&gt;对关系数据库来说，插入一条数据之后立刻查询，是肯定可以读出来这条数据的，但是对于&lt;br /&gt;很多web 应用来说，并不要求这么高的实时性。&lt;br /&gt;3、对复杂的SQL查询，特别是多表关联查询的需求&lt;br /&gt;任何大数据量的web 系统，都非常忌讳多个大表的关联查询，以及复杂的数据分析类型的&lt;br /&gt;复杂SQL 报表查询，特别是SNS 类型的网站，从需求以及产品设计角度，就避免了这种情&lt;br /&gt;况的产生。往往更多的只是单表的主键查询，以及单表的简单条件分页查询，SQL 的功能被&lt;br /&gt;极大的弱化了。&lt;br /&gt;因此，关系数据库在这些越来越多的应用场景下显得不那么合适了，为了解决这类问题的&lt;br /&gt;NoSQL 数据库应运而生。&lt;br /&gt;NoSQL 是非关系型数据存储的广义定义。它打破了长久以来关系型数据库与ACID 理论大一&lt;br /&gt;统的局面。NoSQL 数据存储不需要固定的表结构，通常也不存在连接操作。在大数据存取&lt;br /&gt;上具备关系型数据库无法比拟的性能优势，该概念在 2009 年初得到了广泛认同。&lt;br /&gt;&lt;br /&gt;8 / 91&lt;br /&gt;当今的应用体系结构需要数据存储在横向伸缩性上能够满足需求。而 NoSQL 存储就是为了&lt;br /&gt;实现这个需求。Google 的BigTable 与Amazon 的Dynamo 是非常成功的商业 NoSQL 实现。&lt;br /&gt;一些开源的 NoSQL 体系，如Facebook 的Cassandra， Apache 的HBase，也得到了广泛认&lt;br /&gt;同。从这些NoSQL 项目的名字上看不出什么相同之处：Hadoop、Voldemort、Dynomite，还&lt;br /&gt;有其它很多，但它们都有一个共同的特点，就是要改变大家对数据库在传统意义上的理解。&lt;br /&gt;1.1.4 NoSQL特点&lt;br /&gt;1、 它可以处理超大量的数据&lt;br /&gt;2、 它运行在便宜的PC服务器集群上&lt;br /&gt;PC 集群扩充起来非常方便并且成本很低，避免了传统商业数据库&amp;#8220;sharding&amp;#8221;操作的复杂性&lt;br /&gt;和成本。&lt;br /&gt;3、 它击碎了性能瓶颈&lt;br /&gt;NoSQL 的支持者称，通过NoSQL 架构可以省去将Web 或Java 应用和数据转换成SQL 格式的&lt;br /&gt;时间，执行速度变得更快。&lt;br /&gt;&amp;#8220;SQL 并非适用于所有的程序代码&amp;#8221;，对于那些繁重的重复操作的数据，SQL 值得花钱。但&lt;br /&gt;是当数据库结构非常简单时，SQL 可能没有太大用处。&lt;br /&gt;4、 它没有过多的操作&lt;br /&gt;虽然NoSQL 的支持者也承认关系型数据库提供了无可比拟的功能集合，而且在数据完整性&lt;br /&gt;上也发挥绝对稳定，他们同时也表示，企业的具体需求可能没有那么复杂。&lt;br /&gt;5、 它的支持者源于社区&lt;br /&gt;因为NoSQL 项目都是开源的，因此它们缺乏供应商提供的正式支持。这一点它们与大多数&lt;br /&gt;开源项目一样，不得不从社区中寻求支持。&lt;br /&gt;NoSQL 发展至今，出现了好几种非关系性数据库，本书就以NoSQL 中目前表现最好的&lt;br /&gt;MongoDB 为例来进行说明。&lt;br /&gt;1.2 初识MongoDB&lt;br /&gt;MongoDB 是一个介于关系数据库和非关系数据库之间的产品，是非关系数据库当中功能最&lt;br /&gt;丰富，最像关系数据库的。他支持的数据结构非常松散，是类似json 的bjson 格式，因此可&lt;br /&gt;以存储比较复杂的数据类型。MongoDB 最大的特点是他支持的查询语言非常强大，其语法&lt;br /&gt;有点类似于面向对象的查询语言，几乎可以实现类似关系数据库单表查询的绝大部分功能，&lt;br /&gt;而且还支持对数据建立索引。它是一个面向集合的,模式自由的文档型数据库。&lt;br /&gt;1、 面向集合（Collenction-Orented）&lt;br /&gt;意思是数据被分组存储在数据集中， 被称为一个集合（Collenction)。每个集合在数据库中&lt;br /&gt;都有一个唯一的标识名，并且可以包含无限数目的文档。集合的概念类似关系型数据库&lt;br /&gt;&lt;br /&gt;9 / 91&lt;br /&gt;（RDBMS）里的表（table），不同的是它不需要定义任何模式（schema)。&lt;br /&gt;2、 模式自由（schema-free)&lt;br /&gt;意味着对于存储在MongoDB 数据库中的文件，我们不需要知道它的任何结构定义。提了这&lt;br /&gt;么多次"无模式"或"模式自由"，它到是个什么概念呢？例如，下面两个记录可以存在于同一&lt;br /&gt;个集合里面：&lt;br /&gt;{"welcome" : "Beijing"}&lt;br /&gt;{"age" : 25}&lt;br /&gt;3、 文档型&lt;br /&gt;意思是我们存储的数据是键-值对的集合,键是字符串,值可以是数据类型集合里的任意类型,&lt;br /&gt;包括数组和文档. 我们把这个数据格式称作 &amp;#8220;BSON&amp;#8221; 即 &amp;#8220;Binary Serialized dOcument&lt;br /&gt;Notation.&amp;#8221;&lt;br /&gt;下面将分别介绍MongoDB 的特点、功能和适用场合。&lt;br /&gt;1.2.1 特点&lt;br /&gt;&#x1; 面向集合存储，易于存储对象类型的数据&lt;br /&gt;&#x1; 模式自由&lt;br /&gt;&#x1; 支持动态查询&lt;br /&gt;&#x1; 支持完全索引，包含内部对象&lt;br /&gt;&#x1; 支持查询&lt;br /&gt;&#x1; 支持复制和故障恢复&lt;br /&gt;&#x1; 使用高效的二进制数据存储，包括大型对象（如视频等）&lt;br /&gt;&#x1; 自动处理碎片，以支持云计算层次的扩展性&lt;br /&gt;&#x1; 支持Python，PHP，Ruby，Java，C，C#，Javascript，Perl 及C++语言的驱动程序，社区&lt;br /&gt;中也提供了对Erlang 及.NET 等平台的驱动程序&lt;br /&gt;&#x1; 文件存储格式为BSON（一种JSON 的扩展）&lt;br /&gt;&#x1; 可通过网络访问&lt;br /&gt;1.2.2 功能&lt;br /&gt;&#x1; 面向集合的存储：适合存储对象及JSON 形式的数据&lt;br /&gt;&#x1; 动态查询：MongoDB 支持丰富的查询表达式。查询指令使用JSON 形式的标记，可轻易&lt;br /&gt;查询文档中内嵌的对象及数组&lt;br /&gt;&#x1; 完整的索引支持：包括文档内嵌对象及数组。MongoDB 的查询优化器会分析查询表达&lt;br /&gt;式，并生成一个高效的查询计划&lt;br /&gt;&#x1; 查询监视：MongoDB 包含一系列监视工具用于分析数据库操作的性能&lt;br /&gt;&#x1; 复制及自动故障转移：MongoDB 数据库支持服务器之间的数据复制，支持主-从模式及&lt;br /&gt;服务器之间的相互复制。复制的主要目标是提供冗余及自动故障转移&lt;br /&gt;&#x1; 高效的传统存储方式：支持二进制数据及大型对象（如照片或图片）&lt;br /&gt;&#x1; 自动分片以支持云级别的伸缩性：自动分片功能支持水平的数据库集群，可动态添加额&lt;br /&gt;&lt;br /&gt;10 / 91&lt;br /&gt;外的机器&lt;br /&gt;1.2.3 适用场合&lt;br /&gt;&#x1; 网站数据：MongoDB 非常适合实时的插入，更新与查询，并具备网站实时数据存储所&lt;br /&gt;需的复制及高度伸缩性&lt;br /&gt;&#x1; 缓存：由于性能很高，MongoDB 也适合作为信息基础设施的缓存层。在系统重启之后，&lt;br /&gt;由MongoDB 搭建的持久化缓存层可以避免下层的数据源过载&lt;br /&gt;&#x1; 大尺寸，低价值的数据：使用传统的关系型数据库存储一些数据时可能会比较昂贵，在&lt;br /&gt;此之前，很多时候程序员往往会选择传统的文件进行存储&lt;br /&gt;&#x1; 高伸缩性的场景：MongoDB 非常适合由数十或数百台服务器组成的数据库。MongoDB&lt;br /&gt;的路线图中已经包含对MapReduce 引擎的内置支持&lt;br /&gt;&#x1; 用于对象及JSON 数据的存储：MongoDB 的BSON 数据格式非常适合文档化格式的存储&lt;br /&gt;及查询&lt;br /&gt;第二章 安装和配置&lt;br /&gt;MongoDB 的官方下载站是http://www.mongodb.org/downloads，可以去上面下载最新的安装&lt;br /&gt;程序下来。在下载页面可以看到，它对操作系统支持很全面，如OS X、Linux、Windows、Solaris&lt;br /&gt;都支持，而且都有各自的32 位和64 位版本。目前的稳定版本是1.8.1 版本。&lt;br /&gt;注意:&lt;br /&gt;1. MongoDB 1.8.1 Linux 版要求glibc 必须是2.5 以上，所以需要先确认操作系统的glibc 的版&lt;br /&gt;本，笔者最初用Linux AS 4 安装不上，最后用的是RHEL5 来安装才成功的。&lt;br /&gt;2. 在32 位平台MongoDB 不允许数据库文件（累计总和）超过2G，而64 位平台没有这个&lt;br /&gt;限制。&lt;br /&gt;怎么安装 MongoDB 数据库呢？下面将分别介绍Windows 和Linux 版本的安装方法&lt;br /&gt;&lt;br /&gt;11 / 91&lt;br /&gt;2.1 Windows平台的安装&lt;br /&gt;步骤一: 下载MongoDB&lt;br /&gt;url 下载地址: http://downloads.mongodb.org/win32/mongodb-win32-i386-1.8.1.zip&lt;br /&gt;步骤二: 设置MongoDB程序存放目录&lt;br /&gt;将其解压到 c:\，再重命名为mongo，路径为c:\mongo&lt;br /&gt;步骤三: 设置数据文件存放目录&lt;br /&gt;在c:盘建一个db 文件夹，路径c:\db&lt;br /&gt;步骤四: 启动MongoDB服务&lt;br /&gt;进入 cmd 提示符控制台，c:\mongo\bin\mongod.exe --dbpath=c:\db&lt;br /&gt;C:\mongo\bin&amp;gt;C:\mongo\bin\mongod --dbpath=c:\db&lt;br /&gt;Sun Apr 10 22:34:09 [initandlisten] MongoDB starting : pid=5192 port=27017 dbpat&lt;br /&gt;h=d:\data\db 32-bit&lt;br /&gt;** NOTE: when using MongoDB 32 bit, you are limited to about 2 gigabytes of data&lt;br /&gt;** see http://blog.mongodb.org/post/137788967/32-bit-limitations&lt;br /&gt;** with --dur, the limit is lower&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;Sun Apr 10 22:34:09 [initandlisten] waiting for connections on port 27017&lt;br /&gt;Sun Apr 10 22:34:09 [websvr] web admin interface listening on port 28017&lt;br /&gt;MongoDB 服务端的默认监听端口是 27017&lt;br /&gt;步骤五: 将MongoDB作为 Windows 服务随机启动&lt;br /&gt;先创建C:\mongo\logs\mongodb.log 文件，用于存储MongoDB 的日志文件, 再安装系统&lt;br /&gt;服务。&lt;br /&gt;C:\mongo\bin&amp;gt;C:\mongo\bin\mongod --dbpath=c:\ db --logpath=c:\mongo\lo&lt;br /&gt;gs\mongodb.log --install&lt;br /&gt;all output going to: c:\mongo\logs\mongodb.log&lt;br /&gt;Creating service MongoDB.&lt;br /&gt;Service creation successful.&lt;br /&gt;Service can be started from the command line via 'net start "MongoDB"'.&lt;br /&gt;C:\mongo\bin&amp;gt;net start mongodb&lt;br /&gt;Mongo DB 服务已经启动成功。&lt;br /&gt;C:\mongo\bin&amp;gt;&lt;br /&gt;步骤六: 客户端连接验证&lt;br /&gt;新打开一个CMD 输入：c:\mongo\bin\mongo，如果出现下面提示，那么您就可以开始&lt;br /&gt;MongoDB 之旅了&lt;br /&gt;C:\mongo\bin&amp;gt;c:\mongo\bin\mongo&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt;&lt;br /&gt;步骤七: 查看MongoDB日志&lt;br /&gt;查看C:\mongo\logs\mongodb.log 文件，即可对MongoDB 的运行情况进行查看或排错了，&lt;br /&gt;这样就完成了Windows 平台的MongoDB 安装。&lt;br /&gt;&lt;br /&gt;12 / 91&lt;br /&gt;2.2 Linux平台的安装&lt;br /&gt;步骤一: 下载MongoDB&lt;br /&gt;下载安装包：curl -O http://fastdl.mongodb.org/linux/mongodb-linux-i686-1.8.1.tgz&lt;br /&gt;步骤二: 设置MongoDB程序存放目录&lt;br /&gt;将其解压到/Apps，再重命名为mongo，路径为/Apps/mongo&lt;br /&gt;步骤三: 设置数据文件存放目录&lt;br /&gt;建立/data/db 的目录, mkdir &amp;#8211;p /data/db&lt;br /&gt;步骤四: 启动MongoDB服务&lt;br /&gt;/Apps/mongo/bin/mongod --dbpath=/data/db&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongod --dbpath=/data/db&lt;br /&gt;Sun Apr 8 22:41:06 [initandlisten] MongoDB starting : pid=13701 port=27017 dbpath=/data/db&lt;br /&gt;32-bit&lt;br /&gt;** NOTE: when using MongoDB 32 bit, you are limited to about 2 gigabytes of data&lt;br /&gt;** see http://blog.mongodb.org/post/137788967/32-bit-limitations&lt;br /&gt;** with --dur, the limit is lower&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;Sun Apr 8 22:41:06 [initandlisten] waiting for connections on port 27017&lt;br /&gt;Sun Apr 8 22:41:06 [websvr] web admin interface listening on port 28017&lt;br /&gt;MongoDB 服务端的默认连接端口是 27017&lt;br /&gt;步骤五: 将MongoDB作为 Linux 服务随机启动&lt;br /&gt;先创建/Apps/mongo/logs/mongodb.log 文件，用于存储MongoDB 的日志文件&lt;br /&gt;vi /etc/rc.local, 使用vi 编辑器打开配置文件，并在其中加入下面一行代码&lt;br /&gt;/Apps/mongo/bin/mongod --dbpath=/data/db --logpath=/Apps/mongo/logs/mongodb.log&lt;br /&gt;步骤六: 客户端连接验证&lt;br /&gt;新打开一个Session 输入：/Apps/mongo/bin/mongo，如果出现下面提示，那么您就可以&lt;br /&gt;开始MongoDB 之旅了&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongo&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt;&lt;br /&gt;步骤七: 查看MongoDB日志&lt;br /&gt;查看/Apps/mongo/logs/mongodb.log 文件，即可对MongoDB 的运行状况进行查看或分&lt;br /&gt;析了&lt;br /&gt;[root@localhost logs]# ll&lt;br /&gt;总计 0&lt;br /&gt;-rw-r--r-- 1 root root 0 04-08 20:15 mongodb.log&lt;br /&gt;[root@localhost logs]#&lt;br /&gt;以上的几个步骤就OK 了！！这样一个简单的MongoDB 数据库就可以畅通无阻地运行起&lt;br /&gt;来了。&lt;br /&gt;&lt;br /&gt;13 / 91&lt;br /&gt;第三章 体系结构&lt;br /&gt;MongoDB 是一个可移植的数据库，它在流行的每一个平台上都可以使用，即所谓的跨平台&lt;br /&gt;特性。在不同的操作系统上虽然略有差别，但是从整体构架上来看，MongoDB 在不同的平&lt;br /&gt;台上是一样的，如数据逻辑结构和数据的存储等等。&lt;br /&gt;一个运行着的MongoDB 数据库就可以看成是一个MongoDB Server，该Server 由实例和数据&lt;br /&gt;库组成，在一般的情况下一个MongoDB Server 机器上包含一个实例和多个与之对应的数据&lt;br /&gt;库，但是在特殊情况下，如硬件投入成本有限或特殊的应用需求，也允许一个Server 机器&lt;br /&gt;上可以有多个实例和多个数据库。&lt;br /&gt;MongoDB 中一系列物理文件（数据文件，日志文件等）的集合或与之对应的逻辑结构（集&lt;br /&gt;合，文档等）被称为数据库，简单的说，就是数据库是由一系列与磁盘有关系的物理文件的&lt;br /&gt;组成。&lt;br /&gt;3.1 数据逻辑结构&lt;br /&gt;很多人在学习MongoDB 体系结构的时候会遇到各种各样的问题，我在这里给大家简单的介&lt;br /&gt;绍一下MongoDB 体系结构之－的逻辑结构。MongoDB 的逻辑结构是一种层次结构。主要由：&lt;br /&gt;文档(document)、集合(collection)、数据库(database)这三部分组成的。逻辑结构是面向用户&lt;br /&gt;的，用户使用MongoDB 开发应用程序使用的就是逻辑结构。&lt;br /&gt;&#x1; MongoDB 的文档（document），相当于关系数据库中的一行记录。&lt;br /&gt;&#x1; 多个文档组成一个集合（collection），相当于关系数据库的表。&lt;br /&gt;&#x1; 多个集合（collection），逻辑上组织在一起，就是数据库（database）。&lt;br /&gt;&#x1; 一个MongoDB 实例支持多个数据库（database）。&lt;br /&gt;文档(document)、集合(collection)、数据库(database)的层次结构如下图:&lt;br /&gt;&lt;br /&gt;14 / 91&lt;br /&gt;对于习惯了关系型数据库的朋友们，我将MongoDB 与关系型数据库的逻辑结构进行了对比，&lt;br /&gt;以便让大家更深刻的理解MongoDB 的逻辑结构&lt;br /&gt;逻辑结构对比&lt;br /&gt;MongoDB 关系型数据库&lt;br /&gt;文档(document) 行(row)&lt;br /&gt;集合(collection) 表(table)&lt;br /&gt;数据库(database) 数据库(database)&lt;br /&gt;3.2 数据存储结构&lt;br /&gt;MongoDB 对国内用户来说比较新, 它就像是一个黑盒子，但是如果对于它内部的数据存储&lt;br /&gt;了解多一些的话，那么将会很快的理解和驾驭MongoDB，让它发挥它更大的作用。&lt;br /&gt;MongoDB 的默认数据目录是/data/db，它负责存储所有的MongoDB 的数据文件。在MongoDB&lt;br /&gt;内部，每个数据库都包含一个.ns 文件和一些数据文件，而且这些数据文件会随着数据量的&lt;br /&gt;增加而变得越来越多。所以如果系统中有一个叫做foo 的数据库，那么构成foo 这个数据库&lt;br /&gt;的文件就会由foo.ns，foo.0，foo.1，foo.2 等等组成，具体如下:&lt;br /&gt;&lt;br /&gt;15 / 91&lt;br /&gt;[root@localhost db]# ll /data/db/&lt;br /&gt;总计 196844&lt;br /&gt;-rw------- 1 root root 16777216 04-15 16:33 admin.0&lt;br /&gt;-rw------- 1 root root 33554432 04-15 16:33 admin.1&lt;br /&gt;-rw------- 1 root root 16777216 04-15 16:33 admin.ns&lt;br /&gt;-rw------- 1 root root 16777216 04-21 17:30 foo.0&lt;br /&gt;-rw------- 1 root root 33554432 04-21 17:30 foo.1&lt;br /&gt;-rw------- 1 root root 67108864 04-21 17:30 foo.2&lt;br /&gt;-rw------- 1 root root 16777216 04-21 17:30 foo.ns&lt;br /&gt;-rwxr-xr-x 1 root root 6 04-21 17:16 mongod.lock&lt;br /&gt;-rw------- 1 root root 16777216 04-15 16:30 test.0&lt;br /&gt;-rw------- 1 root root 33554432 04-15 16:30 test.1&lt;br /&gt;-rw------- 1 root root 16777216 04-15 16:30 test.ns&lt;br /&gt;drwxr-xr-x 2 root root 4096 04-21 17:30 _tmp&lt;br /&gt;MongoDB 内部有预分配空间的机制，每个预分配的文件都用0 进行填充，由于有了这个机&lt;br /&gt;制, MongoDB 始终保持额外的空间和空余的数据文件，从而有效避免了由于数据暴增而带来&lt;br /&gt;的磁盘压力过大的问题。&lt;br /&gt;由于表中数据量的增加，数据文件每新分配一次，它的大小都会是上一个数据文件大小的2&lt;br /&gt;倍，每个数据文件最大2G。这样的机制有利于防止较小的数据库浪费过多的磁盘空间，同&lt;br /&gt;时又能保证较大的数据库有相应的预留空间使用。&lt;br /&gt;数据库的每张表都对应一个命名空间，每个索引也有对应的命名空间。这些命名空间的元数&lt;br /&gt;据都集中在*.ns 文件中。&lt;br /&gt;在下图中，foo 这个数据库包含3 个文件用于存储表和索引数据，foo.2 文件属于预分配的空&lt;br /&gt;文件。foo.0 和foo.1 这两个数据文件被分为了相应的盘区对应不同的名字空间。&lt;br /&gt;&lt;br /&gt;16 / 91&lt;br /&gt;上图显示了命名空间和盘区的关系。每个命名空间可以包含多个不同的盘区，这些盘区并不&lt;br /&gt;是连续的。与数据文件的增长相同，每一个命名空间对应的盘区大小的也是随着分配的次数&lt;br /&gt;不断增长的。这样做的目的是为了平衡命名空间浪费的空间与保持某一个命名空间中数据的&lt;br /&gt;连续性。上图中还有一个需要注意的命名空间：$freelist，这个命名空间用于记录不再使用&lt;br /&gt;的盘区（被删除的Collection 或索引）。每当命名空间需要分配新的盘区的时候，都会先查&lt;br /&gt;看$freelist 是否有大小合适的盘区可以使用，这样就回收空闲的磁盘空间。&lt;br /&gt;第四章 快速入门&lt;br /&gt;MongoDB Shell 是MongoDB 自带的交互式Javascript shell，用来对MongoDB 进行操作和管理&lt;br /&gt;的交互式环境。&lt;br /&gt;使用 "./mongo --help" 可查看相关连接参数，下面将从常见的操作，如插入，查询，修改，&lt;br /&gt;删除等几个方面阐述MongoDB shell 的用法。&lt;br /&gt;4.1 启动数据库&lt;br /&gt;MongoDB 安装、配置完后，必须先启动它，然后才能使用它。怎么启动它呢？下面分别展&lt;br /&gt;示了3 种方式来启动实例。&lt;br /&gt;4.1.1 命令行方式启动&lt;br /&gt;MongoDB 默认存储数据目录为/data/db/ (或者 c:\data\db), 默认端口27017，默认HTTP 端&lt;br /&gt;&lt;br /&gt;17 / 91&lt;br /&gt;口28017。当然你也可以修改成不同目录,只需要指定dbpath 参数: /Apps/mongo/bin/mongod&lt;br /&gt;--dbpath=/data/db&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongod --dbpath=/data/db&lt;br /&gt;Sun Apr 8 22:41:06 [initandlisten] MongoDB starting : pid=13701 port=27017 dbpath=/data/db&lt;br /&gt;32-bit&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;Sun Apr 8 22:41:06 [initandlisten] waiting for connections on port 27017&lt;br /&gt;Sun Apr 8 22:41:06 [websvr] web admin interface listening on port 28017&lt;br /&gt;4.1.2 配置文件方式启动&lt;br /&gt;如果是一个专业的DBA，那么实例启动时会加很多的参数以便使系统运行的非常稳定，这样&lt;br /&gt;就可能会在启动时在mongod 后面加一长串的参数，看起来非常混乱而且不好管理和维护，&lt;br /&gt;那么有什么办法让这些参数有条理呢？MongoDB 也支持同mysql 一样的读取启动配置文件&lt;br /&gt;的方式来启动数据库，配置文件的内容如下:&lt;br /&gt;[root@localhost bin]# cat /etc/mongodb.cnf&lt;br /&gt;dbpath=/data/db/&lt;br /&gt;启动时加上&amp;#8221;-f&amp;#8221;参数，并指向配置文件即可&lt;br /&gt;[root@localhost bin]# ./mongod -f /etc/mongodb.cnf&lt;br /&gt;Mon May 28 18:27:18 [initandlisten] MongoDB starting : pid=18481 port=27017&lt;br /&gt;dbpath=/data/db/ 32-bit&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;Mon May 28 18:27:18 [initandlisten] waiting for connections on port 27017&lt;br /&gt;Mon May 28 18:27:18 [websvr] web admin interface listening on port 28017&lt;br /&gt;4.1.3 Daemon方式启动&lt;br /&gt;大家可以注意到上面的两种方式都慢在前台启动MongoDB 进程，但当启动MongoDB 进程&lt;br /&gt;的session 窗口不小心关闭时，MongoDB 进程也将随之停止，这无疑是非常不安全的，幸好&lt;br /&gt;MongoDB 提供了一种后台Daemon 方式启动的选择，只需加上一个&amp;#8221;--fork&amp;#8221;参数即可，这&lt;br /&gt;就使我们可以更方便的操作数据库的启动，但如果用到了&amp;#8221;--fork&amp;#8221;参数就必须也启用&amp;#8221;&lt;br /&gt;--logpath&amp;#8221;参数，这是强制的&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongod --dbpath=/data/db --fork&lt;br /&gt;--fork has to be used with --logpath&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongod --dbpath=/data/db --logpath=/data/log/r3.log&lt;br /&gt;--fork&lt;br /&gt;all output going to: /data/log/r3.log&lt;br /&gt;forked process: 19528&lt;br /&gt;[root@localhost ~]#&lt;br /&gt;&lt;br /&gt;18 / 91&lt;br /&gt;4.1.4 mongod参数说明&lt;br /&gt;最简单的，通过执行mongod 即可以启动MongoDB 数据库服务，mongod 支持很多的参数，&lt;br /&gt;但都有默认值，其中最重要的是需要指定数据文件路径，或者确保默认的/data/db 存在并且&lt;br /&gt;有访问权限，否则启动后会自动关闭服务。Ok，那也就是说，只要确保dbpath 就可以启动&lt;br /&gt;MongoDB 服务了&lt;br /&gt;mongod 的主要参数有：&lt;br /&gt;&#x1; dbpath:&lt;br /&gt;数据文件存放路径，每个数据库会在其中创建一个子目录，用于防止同一个实例多次运&lt;br /&gt;行的mongod.lock 也保存在此目录中。&lt;br /&gt;&#x1; logpath&lt;br /&gt;错误日志文件&lt;br /&gt;&#x1; logappend&lt;br /&gt;错误日志采用追加模式（默认是覆写模式）&lt;br /&gt;&#x1; bind_ip&lt;br /&gt;对外服务的绑定ip，一般设置为空，及绑定在本机所有可用ip 上，如有需要可以单独&lt;br /&gt;指定&lt;br /&gt;&#x1; port&lt;br /&gt;对外服务端口。Web 管理端口在这个port 的基础上+1000&lt;br /&gt;&#x1; fork&lt;br /&gt;以后台Daemon 形式运行服务&lt;br /&gt;&#x1; journal&lt;br /&gt;开启日志功能，通过保存操作日志来降低单机故障的恢复时间，在1.8 版本后正式加入，&lt;br /&gt;取代在1.7.5 版本中的dur 参数。&lt;br /&gt;&#x1; syncdelay&lt;br /&gt;系统同步刷新磁盘的时间，单位为秒，默认是60 秒。&lt;br /&gt;&#x1; directoryperdb&lt;br /&gt;每个db 存放在单独的目录中，建议设置该参数。与MySQL 的独立表空间类似&lt;br /&gt;&#x1; maxConns&lt;br /&gt;最大连接数&lt;br /&gt;&#x1; repairpath&lt;br /&gt;执行repair 时的临时目录。在如果没有开启journal，异常down 机后重启，必须执行repair&lt;br /&gt;操作。&lt;br /&gt;在源代码中，mongod 的参数分为一般参数，windows 参数，replication 参数，replica set 参&lt;br /&gt;数，以及隐含参数。上面列举的都是一般参数。如果要配置replication，replica set 等，还需&lt;br /&gt;要设置对应的参数，这里先不展开，后续会有专门的章节来讲述。执行mongod --help 可以&lt;br /&gt;看到对大多数参数的解释，但有一些隐含参数，则只能通过看代码来获得(见db.cpp&lt;br /&gt;po::options_description hidden_options(&amp;#8220;Hidden options&amp;#8221;);)，隐含参数一般要么是还在开发&lt;br /&gt;中，要么是准备废弃，因此在生产环境中不建议使用。&lt;br /&gt;可能你已经注意到，mongod 的参数中，没有设置内存大小相关的参数，是的，MongoDB 使&lt;br /&gt;&lt;br /&gt;19 / 91&lt;br /&gt;用os mmap 机制来缓存数据文件数据，自身目前不提供缓存机制。这样好处是代码简单，&lt;br /&gt;mmap 在数据量不超过内存时效率很高。但是数据量超过系统可用内存后，则写入的性能可&lt;br /&gt;能不太稳定，容易出现大起大落，不过在最新的1.8 版本中，这个情况相对以前的版本已经&lt;br /&gt;有了一定程度的改善。&lt;br /&gt;这么多参数，全面写在命令行中则容易杂乱而不好管理。因此，mongod 支持将参数写入到&lt;br /&gt;一个配置文本文件中，然后通过config 参数来引用此配置文件：&lt;br /&gt;./mongod --config /etc/mongo.cnf&lt;br /&gt;4.2 停止数据库&lt;br /&gt;MongoDB 提供的停止数据库命令也非常丰富，如果Control-C、发送shutdownServer()指令及&lt;br /&gt;发送Unix 系统中断信号等&lt;br /&gt;4.2.1 Control-C&lt;br /&gt;如果处理连接状态，那么直接可以通过Control-C 的方式去停止MongoDB 实例，具体如下:&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongo --port 28013&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:28013/test&lt;br /&gt;&amp;gt; use test&lt;br /&gt;switched to db test&lt;br /&gt;&amp;gt; ^C[root@localhost ~]#&lt;br /&gt;4.2.2 shutdownServer()指令&lt;br /&gt;如果处理连接状态，那么直接可以通过在admin 库中发送db.shutdownServer()指令去停止&lt;br /&gt;MongoDB 实例，具体如下:&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongo --port 28013&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:28013/test&lt;br /&gt;&amp;gt; use admin&lt;br /&gt;switched to db admin&lt;br /&gt;&amp;gt; db.shutdownServer()&lt;br /&gt;Thu May 31 23:22:00 DBClientCursor::init call() failed&lt;br /&gt;Thu May 31 23:22:00 query failed : admin.$cmd { shutdown: 1.0 } to: 127.0.0.1:28013&lt;br /&gt;server should be down...&lt;br /&gt;Thu May 31 23:22:00 trying reconnect to 127.0.0.1:28013&lt;br /&gt;Thu May 31 23:22:00 reconnect 127.0.0.1:28013 failed couldn't connect to server&lt;br /&gt;127.0.0.1:28013&lt;br /&gt;Thu May 31 23:22:00 Error: error doing query: unknown shell/collection.js:150&lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;20 / 91&lt;br /&gt;4.2.3 Unix系统指令&lt;br /&gt;在找到实例的进程后，可能通过发送kill -2 PID 或kill -15 PID 来停止进程&lt;br /&gt;[root@localhost ~]# ps aux|grep mongod&lt;br /&gt;root 19269 0.3 1.3 76008 3108 Sl 23:24 0:00 /Apps/mongo/bin/mongod --fork&lt;br /&gt;--port 28013&lt;br /&gt;[root@localhost ~]# kill -2 19269&lt;br /&gt;注意:&lt;br /&gt;不要用kill -9 PID 来杀死MongoDB 进程，这样可以会导致MongoDB 的数据损坏&lt;br /&gt;4.3 连接数据库&lt;br /&gt;现在我们就可以使用自带的MongoDB shell 工具来操作数据库了. (我们也可以使用各种编程&lt;br /&gt;语言的驱动来使用MongoDB, 但自带的MongoDB shell 工具可以方便我们管理数据库)。&lt;br /&gt;新打开一个Session 输入：/Apps/mongo/bin/mongo，如果出现下面提示，那么您就说明连接&lt;br /&gt;上数据库了，可以进行操作了&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongo&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt;&lt;br /&gt;默认 shell 连接的是本机localhost 上面的 test 库，"connecting to:" 这个会显示你正在使用&lt;br /&gt;的数据库的名称. 想换数据库的话可以用&amp;#8221;use mydb&amp;#8221;来实现。&lt;br /&gt;4.4 插入记录&lt;br /&gt;下面我们来建立一个test 的集合并写入一些数据. 建立两个对象j 和t , 并保存到集合中去.&lt;br /&gt;在例子里 &amp;#8220;&amp;gt;&amp;#8221; 来表示是 shell 输入提示符&lt;br /&gt;&amp;gt; j = { name : "mongo" };&lt;br /&gt;{"name" : "mongo"}&lt;br /&gt;&amp;gt; t = { x : 3 };&lt;br /&gt;{ "x" : 3 }&lt;br /&gt;&amp;gt; db.things.save(j);&lt;br /&gt;&amp;gt; db.things.save(t);&lt;br /&gt;&amp;gt; db.things.find();&lt;br /&gt;{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }&lt;br /&gt;{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }&lt;br /&gt;&amp;gt;&lt;br /&gt;有几点需要注意一下:&lt;br /&gt;&#x1; 不需要预先创建一个集合. 在第一次插入数据时候会自动创建.&lt;br /&gt;&lt;br /&gt;21 / 91&lt;br /&gt;&#x1; 在文档中其实可以存储任何结构的数据, 当然在实际应用我们存储的还是相同类型文&lt;br /&gt;档的集合. 这个特性其实可以在应用里很灵活, 你不需要类似alter table 语句来修改你&lt;br /&gt;的数据结构&lt;br /&gt;&#x1; 每次插入数据时候集合中都会有一个ID, 名字叫 _id.&lt;br /&gt;下面再加点数据:&lt;br /&gt;&amp;gt; for( var i = 1; i &amp;lt; 10; i++ ) db.things.save( { x:4, j:i } ); &amp;gt; db.things.find();&lt;br /&gt;{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}&lt;br /&gt;{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}&lt;br /&gt;{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}&lt;br /&gt;{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}&lt;br /&gt;{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}&lt;br /&gt;{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}&lt;br /&gt;{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}&lt;br /&gt;{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}&lt;br /&gt;{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}&lt;br /&gt;{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}&lt;br /&gt;请注意一下, 这里循环次数是10, 但是只显示到第8 条, 还有2 条数据没有显示. 如果想继&lt;br /&gt;续查询下面的数据只需要使用&amp;#8221;it&amp;#8221;命令, 就会继续显示下面的数据:&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd866"), "x" : 4, "j" : 17 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd867"), "x" : 4, "j" : 18 }&lt;br /&gt;has more&lt;br /&gt;&amp;gt; it&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd868"), "x" : 4, "j" : 19 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd869"), "x" : 4, "j" : 20 }&lt;br /&gt;从技术上讲 find() 返回一个游标对象. 但在上面的例子里, 并没有拿到一个游标的变量. 所&lt;br /&gt;以 shell 自动遍历游标, 返回一个初始化的set, 并允许我们继续用 it 迭代输出.&lt;br /&gt;当然我们也可以直接用游标来输出, 不过这个是&amp;#8221;游标&amp;#8221;部分的内容了.&lt;br /&gt;4.5 _id key&lt;br /&gt;MongoDB 支持的数据类型中，_id 是其自有产物，下面对其做些简单的介绍。&lt;br /&gt;存储在MongoDB 集合中的每个文档（document）都有一个默认的主键_id，这个主键名称是&lt;br /&gt;固定的，它可以是MongoDB 支持的任何数据类型，默认是ObjectId。在关系数据库schema&lt;br /&gt;设计中，主键大多是数值型的，比如常用的int 和long，并且更通常的是主键的取值由数据&lt;br /&gt;库自增获得，这种主键数值的有序性有时也表明了某种逻辑。反观MongoDB，它在设计之&lt;br /&gt;初就定位于分布式存储系统，所以它原生的不支持自增主键。&lt;br /&gt;_id key 举例说明 :&lt;br /&gt;当我们在往一个集合中写入一条文档时，系统会自动生成一个名为_id 的key.如：&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;&lt;br /&gt;22 / 91&lt;br /&gt;这里多出了一个类型为ObjectId 的key ,在插入时并没有指定，这有点类似Oracle 的rowid&lt;br /&gt;的信息，属于自动生成的。&lt;br /&gt;在MongoDB 中，每一个集合都必须有一个叫做_id 的字段,字段类型默认是ObjectId ,换句话&lt;br /&gt;说，字段类型可以不是ObjectId,例如:&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;{ "_id" : 3, "name" : "Bill", "age" : 55 }&lt;br /&gt;虽然_id 的类型可以自由指定,但是在同一个集合中必须唯一，如果插入重复的值的话，系统&lt;br /&gt;将会抛出异常，具体如下:&lt;br /&gt;&amp;gt; db.c1.insert({_id:3, name:"Bill_new", age:55})&lt;br /&gt;E11000 duplicate key error index: test.c1.$_id_ dup key: { : 3.0 }&lt;br /&gt;&amp;gt;&lt;br /&gt;因为前面已经插入了一条_id=3 的记录，所以再插入相同的文档就不允许了。&lt;br /&gt;4.6 查询记录&lt;br /&gt;4.6.1 普通查询&lt;br /&gt;在没有深入查询之前, 我们先看看怎么从一个查询中返回一个游标对象. 可以简单的通过&lt;br /&gt;find() 来查询, 他返回一个任意结构的集合. 如果实现特定的查询稍后讲解.&lt;br /&gt;实现上面同样的查询, 然后通过 while 来输出:&lt;br /&gt;&amp;gt; var cursor = db.things.find();&lt;br /&gt;&amp;gt; while (cursor.hasNext()) printjson(cursor.next());&lt;br /&gt;{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }&lt;br /&gt;{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "x" : 4, "j" : 2 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "x" : 4, "j" : 5 }&lt;br /&gt;上面的例子显示了游标风格的迭代输出. hasNext() 函数告诉我们是否还有数据, 如果有则&lt;br /&gt;可以调用 next() 函数.&lt;br /&gt;当我们使用的是 JavaScript shell, 可以用到JS 的特性, forEach 就可以输出游标了. 下面的例&lt;br /&gt;子就是使用 forEach() 来循环输出: forEach() 必须定义一个函数供每个游标元素调用.&lt;br /&gt;&amp;gt; db.things.find().forEach(printjson);&lt;br /&gt;{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }&lt;br /&gt;{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }&lt;br /&gt;&lt;br /&gt;23 / 91&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "x" : 4, "j" : 2 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "x" : 4, "j" : 5 }&lt;br /&gt;在 MongoDB shell 里, 我们也可以把游标当作数组来用:&lt;br /&gt;&amp;gt; var cursor = db.things.find();&lt;br /&gt;&amp;gt; printjson(cursor[4]);&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }&lt;br /&gt;使用游标时候请注意占用内存的问题, 特别是很大的游标对象, 有可能会内存溢出. 所以应&lt;br /&gt;该用迭代的方式来输出. 下面的示例则是把游标转换成真实的数组类型:&lt;br /&gt;&amp;gt; var arr = db.things.find().toArray();&lt;br /&gt;&amp;gt; arr[5];&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }&lt;br /&gt;请注意这些特性只是在MongoDB shell 里使用, 而不是所有的其他应用程序驱动都支持.&lt;br /&gt;MongoDB 游标对象不是没有快照，如果有其他用户在集合里第一次或者最后一次调用&lt;br /&gt;next(), 你可能得不到游标里的数据. 所以要明确的锁定你要查询的游标.&lt;br /&gt;4.6.2 条件查询&lt;br /&gt;到这里我们已经知道怎么从游标里实现一个查询并返回数据对象, 下面就来看看怎么根据&lt;br /&gt;指定的条件来查询.&lt;br /&gt;下面的示例就是说明如何执行一个类似SQL 的查询, 并演示了怎么在 MongoDB 里实现. 这&lt;br /&gt;是在MongoDB shell 里查询, 当然你也可以用其他的应用程序驱动或者语言来实现:&lt;br /&gt;SELECT * FROM things WHERE name="mongo"&lt;br /&gt;&amp;gt; db.things.find({name:"mongo"}).forEach(printjson);&lt;br /&gt;{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }&lt;br /&gt;SELECT * FROM things WHERE x=4&lt;br /&gt;&amp;gt; db.things.find({x:4}).forEach(printjson);&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "x" : 4, "j" : 2 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "x" : 4, "j" : 5 }&lt;br /&gt;查询条件是 { a:A, b:B, &amp;#8230; } 类似 &amp;#8220;where a==A and b==B and &amp;#8230;&amp;#8221;.&lt;br /&gt;上面显示的是所有的元素, 当然我们也可以返回特定的元素, 类似于返回表里某字段的值,&lt;br /&gt;只需要在 find({x:4}) 里指定元素的名字&lt;br /&gt;SELECT j FROM things WHERE x=4&lt;br /&gt;&amp;gt; db.things.find({x:4}, {j:true}).forEach(printjson);&lt;br /&gt;&lt;br /&gt;24 / 91&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "j" : 1 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "j" : 2 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "j" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "j" : 4 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "j" : 5 }&lt;br /&gt;4.6.3 findOne()语法&lt;br /&gt;为了方便考虑, MongoDB shell 避免游标可能带来的开销, 提供一个findOne() 函数. 这个函&lt;br /&gt;数和 find() 函数一样, 不过它返回的是游标里第一条数据, 或者返回null，即空数据.&lt;br /&gt;作为一个例子, name=&amp;#8221;mongo&amp;#8221; 可以用很多方法来实现, 可以用 next() 来循环游标或者当&lt;br /&gt;做数组返回第一个元素.&lt;br /&gt;但是用 findOne() 方法则更简单和高效:&lt;br /&gt;&amp;gt; printjson(db.things.findOne({name:"mongo"}));&lt;br /&gt;{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }&lt;br /&gt;4.6.4 通过limit限制结果集数量&lt;br /&gt;如果需要限制结果集的长度, 那么可以调用 limit 方法.&lt;br /&gt;这是强烈推荐解决性能问题的方法, 就是通过限制条数来减少网络传输, 例如:&lt;br /&gt;&amp;gt; db.things.find().limit(3);&lt;br /&gt;{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }&lt;br /&gt;{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }&lt;br /&gt;4.7 修改记录&lt;br /&gt;将name 是mongo 的记录的name 修改为mongo_new&lt;br /&gt;&amp;gt; db.things.update({name:"mongo"},{$set:{name:"mongo_new"}});&lt;br /&gt;我们来查询一下是否改过来了&lt;br /&gt;&amp;gt; db.things.find();&lt;br /&gt;{ "_id" : ObjectId("4faa9e7dedd27e6d86d86371"), "x" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4faa9e7bedd27e6d86d86370"), "name" : "mongo_new" }&lt;br /&gt;4.8 删除记录&lt;br /&gt;将用户name 是mongo_new 的记录从集合things 中删除&lt;br /&gt;&lt;br /&gt;25 / 91&lt;br /&gt;&amp;gt; db.things.remove({name:"mongo_new"});&lt;br /&gt;&amp;gt; db.things.find();&lt;br /&gt;{ "_id" : ObjectId("4faa9e7dedd27e6d86d86371"), "x" : 3 }&lt;br /&gt;经验证，该记录确实被删除了&lt;br /&gt;4.9常用工具集&lt;br /&gt;MongoDB 在bin 目录下提供了一系列有用的工具，这些工具提供了MongoDB 在运维管理上&lt;br /&gt;的方便。&lt;br /&gt;&#x1; bsondump: 将bson 格式的文件转储为json 格式的数据&lt;br /&gt;&#x1; mongo: 客户端命令行工具，其实也是一个js 解释器，支持js 语法&lt;br /&gt;&#x1; mongod: 数据库服务端，每个实例启动一个进程，可以fork 为后台运行&lt;br /&gt;&#x1; mongodump/ mongorestore: 数据库备份和恢复工具&lt;br /&gt;&#x1; mongoexport/ mongoimport: 数据导出和导入工具&lt;br /&gt;&#x1; mongofiles: GridFS 管理工具，可实现二制文件的存取&lt;br /&gt;&#x1; mongos: 分片路由，如果使用了sharding 功能，则应用程序连接的是mongos 而不是&lt;br /&gt;mongod&lt;br /&gt;&#x1; mongosniff: 这一工具的作用类似于tcpdump，不同的是他只监控MongoDB 相关的包请&lt;br /&gt;求，并且是以指定的可读性的形式输出&lt;br /&gt;&#x1; mongostat: 实时性能监控工具&lt;br /&gt;4.10 客户端GUI 工具&lt;br /&gt;看一个产品是否得到认可,可以从一个侧面看其第三方工具的数量和成熟程度,下面我们就来&lt;br /&gt;细数一下MongoDB 常用的GUI 管理工具。&lt;br /&gt;4.10.1 MongoVUE&lt;br /&gt;主页：http://www.mongovue.com/&lt;br /&gt;一个桌面程序，提供了对MongoDB 数据库的基本操作，如查看、查询、更新、删除等，简&lt;br /&gt;单易用，但是功能还比较弱，以后发展应该不错。&lt;br /&gt;&lt;br /&gt;26 / 91&lt;br /&gt;4.10.2 RockMongo&lt;br /&gt;主页：http://code.google.com/p/rock-php/&lt;br /&gt;RockMongo 是一个PHP5 写的MongoDB 管理工具。&lt;br /&gt;主要特征:&lt;br /&gt;使用宽松的New BSD License 协议&lt;br /&gt;速度快，安装简单&lt;br /&gt;支持多语言（目前提供中文、英文、日文、巴西葡萄牙语、法语、德语）&lt;br /&gt;系统可以配置多个主机，每个主机可以有多个管理员，需要管理员密码才能登入操作，确保&lt;br /&gt;数据库的安全性&lt;br /&gt;&lt;br /&gt;27 / 91&lt;br /&gt;服务器信息 (WEB 服务器, PHP, PHP.ini 相关指令 ...)状态&lt;br /&gt;数据查询，创建和删除&lt;br /&gt;执行命令和Javascript 代码&lt;br /&gt;4.10.3 MongoHub&lt;br /&gt;主页：https://github.com/bububa/MongoHub&lt;br /&gt;MongoHub 是一个针对Mac 平台的MongoDB 图形管理客户端，可用来管理MongoDB 数据&lt;br /&gt;的应用程序。&lt;br /&gt;第二部分 应用篇&lt;br /&gt;本章将结合实际应用，重点阐述一些实际工作中最常用的方法。&lt;br /&gt;第五章 高级查询&lt;br /&gt;面向文档的NoSQL 数据库主要解决的问题不是高性能的并发读写，而是保证海量数据存储&lt;br /&gt;的同时，具有良好的查询性能。&lt;br /&gt;MongoDB 最大的特点是他支持的查询语言非常强大，其语法有点类似于面向对象的查询语&lt;br /&gt;言，几乎可以实现类似关系数据库单表查询的绝大部分功能，而且还支持对数据建立索引。&lt;br /&gt;&lt;br /&gt;28 / 91&lt;br /&gt;最后由于MongoDB 可以支持复杂的数据结构，而且带有强大的数据查询功能，因此非常受&lt;br /&gt;到欢迎，很多项目都考虑用MongoDB 来替代MySQL 等传统数据库来实现不是特别复杂的&lt;br /&gt;Web 应用。由于数据量实在太大，所以迁移到了MongoDB 上面，数据查询的速度得到了非&lt;br /&gt;常显著的提升。&lt;br /&gt;下面将介绍一些高级查询语法:&lt;br /&gt;5.1 条件操作符&lt;br /&gt;5.1 条件操作符&lt;br /&gt;&amp;lt;, &amp;lt;=, &amp;gt;, &amp;gt;= 这个操作符就不用多解释了，最常用也是最简单的&lt;br /&gt;db.collection.find({ "field" : { $gt: value } } ); // 大于: field &amp;gt; value&lt;br /&gt;db.collection.find({ "field" : { $lt: value } } ); // 小于: field &amp;lt; value&lt;br /&gt;db.collection.find({ "field" : { $gte: value } } ); // 大于等于: field &amp;gt;= value&lt;br /&gt;db.collection.find({ "field" : { $lte: value } } ); // 小于等于: field &amp;lt;= value&lt;br /&gt;如果要同时满足多个条件，可以这样做&lt;br /&gt;db.collection.find({ "field" : { $gt: value1, $lt: value2 } } ); // value1 &amp;lt; field &amp;lt; value&lt;br /&gt;5.2 $all匹配所有&lt;br /&gt;这个操作符跟SQL 语法的in 类似，但不同的是, in 只需满足( )内的某一个值即可, 而$all 必&lt;br /&gt;须满足[ ]内的所有值，例如:&lt;br /&gt;db.users.find({age : {$all : [6, 8]}});&lt;br /&gt;可以查询出 {name: 'David', age: 26, age: [ 6, 8, 9 ] }&lt;br /&gt;但查询不出 {name: 'David', age: 26, age: [ 6, 7, 9 ] }&lt;br /&gt;5.3 $exists判断字段是否存在&lt;br /&gt;查询所有存在age 字段的记录&lt;br /&gt;db.users.find({age: {$exists: true}});&lt;br /&gt;查询所有不存在name 字段的记录&lt;br /&gt;db.users.find({name: {$exists: false}});&lt;br /&gt;举例如下:&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find();&lt;br /&gt;{ "_id" : ObjectId("4fb4a773afa87dc1bed9432d"), "age" : 20, "length" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4a7e1afa87dc1bed9432e"), "age_1" : 20, "length_1" : 30 }&lt;br /&gt;&lt;br /&gt;29 / 91&lt;br /&gt;查询存在字段age 的数据&lt;br /&gt;&amp;gt; db.c1.find({age:{$exists:true}});&lt;br /&gt;{ "_id" : ObjectId("4fb4a773afa87dc1bed9432d"), "age" : 20, "length" : 30 }&lt;br /&gt;可以看出只显示出了有age 字段的数据，age_1 的数据并没有显示出来&lt;br /&gt;5.4 Null值处理&lt;br /&gt;Null 值的处理稍微有一点奇怪，具体看下面的样例数据：&lt;br /&gt;&amp;gt; db.c2.find()&lt;br /&gt;{ "_id" : ObjectId("4fc34bb81d8a39f01cc17ef4"), "name" : "Lily", "age" : null }&lt;br /&gt;{ "_id" : ObjectId("4fc34be01d8a39f01cc17ef5"), "name" : "Jacky", "age" : 23 }&lt;br /&gt;{ "_id" : ObjectId("4fc34c1e1d8a39f01cc17ef6"), "name" : "Tom", "addr" : 23 }&lt;br /&gt;其中&amp;#8221;Lily&amp;#8221;的age 字段为空，Tom 没有age 字段，我们想找到age 为空的行，具体如下：&lt;br /&gt;&amp;gt; db.c2.find({age:null})&lt;br /&gt;{ "_id" : ObjectId("4fc34bb81d8a39f01cc17ef4"), "name" : "Lily", "age" : null }&lt;br /&gt;{ "_id" : ObjectId("4fc34c1e1d8a39f01cc17ef6"), "name" : "Tom", "addr" : 23 }&lt;br /&gt;奇怪的是我们以为只能找到&amp;#8221;Lily&amp;#8221;，但&amp;#8221;Tom&amp;#8221;也被找出来了，所以&amp;#8221;null&amp;#8221;不仅能找到它自身，&lt;br /&gt;连不存在age 字段的记录也找出来了。那么怎么样才能只找到&amp;#8221;Lily&amp;#8221;呢?我们用exists 来限制&lt;br /&gt;一下即可:&lt;br /&gt;&amp;gt; db.c2.find({age:{"$in":[null], "$exists":true}})&lt;br /&gt;{ "_id" : ObjectId("4fc34bb81d8a39f01cc17ef4"), "name" : "Lily", "age" : null }&lt;br /&gt;这样如我们期望一样，只有&amp;#8221;Lily&amp;#8221;被找出来了。&lt;br /&gt;5.5 $mod取模运算&lt;br /&gt;查询age 取模10 等于0 的数据&lt;br /&gt;db.student.find( { age: { $mod : [ 10 , 1 ] } } )&lt;br /&gt;举例如下:&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb4af85afa87dc1bed94330"), "age" : 7, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af89afa87dc1bed94331"), "age" : 8, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af8cafa87dc1bed94332"), "age" : 6, "length_1" : 30 }&lt;br /&gt;查询age 取模6 等于1 的数据&lt;br /&gt;&amp;gt; db.c1.find({age: {$mod : [ 6 , 1 ] } })&lt;br /&gt;{ "_id" : ObjectId("4fb4af85afa87dc1bed94330"), "age" : 7, "length_1" : 30 }&lt;br /&gt;可以看出只显示出了age 取模6 等于1 的数据，其它不符合规则的数据并没有显示出来&lt;br /&gt;&lt;br /&gt;30 / 91&lt;br /&gt;5.6 $ne不等于&lt;br /&gt;查询x 的值不等于3 的数据&lt;br /&gt;db.things.find( { x : { $ne : 3 } } );&lt;br /&gt;举例如下:&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb4af85afa87dc1bed94330"), "age" : 7, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af89afa87dc1bed94331"), "age" : 8, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af8cafa87dc1bed94332"), "age" : 6, "length_1" : 30 }&lt;br /&gt;查询age 的值不等于7 的数据&lt;br /&gt;&amp;gt; db.c1.find( { age : { $ne : 7 } } );&lt;br /&gt;{ "_id" : ObjectId("4fb4af89afa87dc1bed94331"), "age" : 8, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af8cafa87dc1bed94332"), "age" : 6, "length_1" : 30 }&lt;br /&gt;可以看出只显示出了age 等于7 的数据，其它不符合规则的数据并没有显示出来&lt;br /&gt;5.7 $in包含&lt;br /&gt;与sql 标准语法的用途是一样的，即要查询的是一系列枚举值的范围内&lt;br /&gt;查询x 的值在2,4,6 范围内的数据&lt;br /&gt;db.things.find({x:{$in: [2,4,6]}});&lt;br /&gt;举例如下:&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb4af85afa87dc1bed94330"), "age" : 7, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af89afa87dc1bed94331"), "age" : 8, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af8cafa87dc1bed94332"), "age" : 6, "length_1" : 30 }&lt;br /&gt;查询age 的值在7,8 范围内的数据&lt;br /&gt;&amp;gt; db.c1.find({age:{$in: [7,8]}});&lt;br /&gt;{ "_id" : ObjectId("4fb4af85afa87dc1bed94330"), "age" : 7, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af89afa87dc1bed94331"), "age" : 8, "length_1" : 30 }&lt;br /&gt;可以看出只显示出了age 等于7 或8 的数据，其它不符合规则的数据并没有显示出来&lt;br /&gt;5.8 $nin不包含&lt;br /&gt;与sql 标准语法的用途是一样的，即要查询的数据在一系列枚举值的范围外&lt;br /&gt;查询x 的值在2,4,6 范围外的数据&lt;br /&gt;db.things.find({x:{$nin: [2,4,6]}});&lt;br /&gt;举例如下:&lt;br /&gt;&lt;br /&gt;31 / 91&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb4af85afa87dc1bed94330"), "age" : 7, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af89afa87dc1bed94331"), "age" : 8, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af8cafa87dc1bed94332"), "age" : 6, "length_1" : 30 }&lt;br /&gt;查询age 的值在7,8 范围外的数据&lt;br /&gt;&amp;gt; db.c1.find({age:{$nin: [7,8]}});&lt;br /&gt;{ "_id" : ObjectId("4fb4af8cafa87dc1bed94332"), "age" : 6, "length_1" : 30 }&lt;br /&gt;可以看出只显示出了age 不等于7 或8 的数据，其它不符合规则的数据并没有显示出来&lt;br /&gt;5.9 $size数组元素个数&lt;br /&gt;对于{name: 'David', age: 26, favorite_number: [ 6, 7, 9 ] }记录&lt;br /&gt;匹配db.users.find({favorite_number: {$size: 3}});&lt;br /&gt;不匹配db.users.find({favorite_number: {$size: 2}});&lt;br /&gt;举例如下:&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb4af85afa87dc1bed94330"), "age" : 7, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af89afa87dc1bed94331"), "age" : 8, "length_1" : 30 }&lt;br /&gt;{ "_id" : ObjectId("4fb4af8cafa87dc1bed94332"), "age" : 6, "length_1" : 30 }&lt;br /&gt;查询age 的值在7,8 范围外的数据&lt;br /&gt;&amp;gt; db.c1.find({age:{$nin: [7,8]}});&lt;br /&gt;{ "_id" : ObjectId("4fb4af8cafa87dc1bed94332"), "age" : 6, "length_1" : 30 }&lt;br /&gt;可以看出只显示出了age 不等于7 或8 的数据，其它不符合规则的数据并没有显示出来&lt;br /&gt;5.10 正则表达式匹配&lt;br /&gt;查询不匹配name=B*带头的记录&lt;br /&gt;db.users.find({name: {$not: /^B.*/}});&lt;br /&gt;举例如下:&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find();&lt;br /&gt;{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;查询name 不以T 开头的数据&lt;br /&gt;&amp;gt; db.c1.find({name: {$not: /^T.*/}});&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;可以看出只显示出了name=Tony 的数据，其它不符合规则的数据并没有显示出来&lt;br /&gt;&lt;br /&gt;32 / 91&lt;br /&gt;5.11 Javascript查询和$where查询&lt;br /&gt;查询a 大于3 的数据，下面的查询方法殊途同归&lt;br /&gt;&#x1; db.c1.find( { a : { $gt: 3 } } );&lt;br /&gt;&#x1; db.c1.find( { $where: "this.a &amp;gt; 3" } );&lt;br /&gt;&#x1; db.c1.find("this.a &amp;gt; 3");&lt;br /&gt;&#x1; f = function() { return this.a &amp;gt; 3; } db.c1.find(f);&lt;br /&gt;5.12 count查询记录条数&lt;br /&gt;count 查询记录条数&lt;br /&gt;db.users.find().count();&lt;br /&gt;以下返回的不是5，而是user 表中所有的记录数量&lt;br /&gt;db.users.find().skip(10).limit(5).count();&lt;br /&gt;如果要返回限制之后的记录数量，要使用count(true)或者count(非0)&lt;br /&gt;db.users.find().skip(10).limit(5).count(true);&lt;br /&gt;举例如下:&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;查询c1 表的数据量&lt;br /&gt;&amp;gt; db.c1.count()&lt;br /&gt;2&lt;br /&gt;可以看出表中共有2 条数据&lt;br /&gt;5.13 skip限制返回记录的起点&lt;br /&gt;从第3 条记录开始，返回5 条记录(limit 3, 5)&lt;br /&gt;db.users.find().skip(3).limit(5);&lt;br /&gt;举例如下:&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;查询c1 表的第2 条数据&lt;br /&gt;&amp;gt; db.c1.find().skip(1).limit(1)&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;可以看出表中第2 条数据被显示了出来&lt;br /&gt;&lt;br /&gt;33 / 91&lt;br /&gt;5.14 sort排序&lt;br /&gt;以年龄升序asc&lt;br /&gt;db.users.find().sort({age: 1});&lt;br /&gt;以年龄降序desc&lt;br /&gt;db.users.find().sort({age: -1});&lt;br /&gt;C1 表的数据如下:&lt;br /&gt;&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;查询c1 表按age 升序排列&lt;br /&gt;&amp;gt; db.c1.find().sort({age: 1});&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }&lt;br /&gt;第1 条是age=10 的，而后升序排列结果集&lt;br /&gt;查询c1 表按age 降序排列&lt;br /&gt;&amp;gt; db.c1.find().sort({age: -1});&lt;br /&gt;{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }&lt;br /&gt;{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }&lt;br /&gt;第1 条是age=20 的，而后降序排列结果集&lt;br /&gt;5.2 游标&lt;br /&gt;象大多数数据库产品一样，MongoDB 也是用游标来循环处理每一条结果数据，具体语法如&lt;br /&gt;下：&lt;br /&gt;&amp;gt; for( var c = db.t3.find(); c.hasNext(); ) {&lt;br /&gt;... printjson( c.next());&lt;br /&gt;... }&lt;br /&gt;{ "_id" : ObjectId("4fb8e4838b2cb86417c9423a"), "age" : 1 }&lt;br /&gt;{ "_id" : ObjectId("4fb8e4878b2cb86417c9423b"), "age" : 2 }&lt;br /&gt;{ "_id" : ObjectId("4fb8e4898b2cb86417c9423c"), "age" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4fb8e48c8b2cb86417c9423d"), "age" : 4 }&lt;br /&gt;{ "_id" : ObjectId("4fb8e48e8b2cb86417c9423e"), "age" : 5 }&lt;br /&gt;MongoDB 还有另一种方式来处理游标&lt;br /&gt;&amp;gt; db.t3.find().forEach( function(u) { printjson(u); } );&lt;br /&gt;{ "_id" : ObjectId("4fb8e4838b2cb86417c9423a"), "age" : 1 }&lt;br /&gt;{ "_id" : ObjectId("4fb8e4878b2cb86417c9423b"), "age" : 2 }&lt;br /&gt;{ "_id" : ObjectId("4fb8e4898b2cb86417c9423c"), "age" : 3 }&lt;br /&gt;{ "_id" : ObjectId("4fb8e48c8b2cb86417c9423d"), "age" : 4 }&lt;br /&gt;&lt;br /&gt;34 / 91&lt;br /&gt;{ "_id" : ObjectId("4fb8e48e8b2cb86417c9423e"), "age" : 5 }&lt;br /&gt;&amp;gt;&lt;br /&gt;5.3 存储过程&lt;br /&gt;MongoDB 为很多问题提供了一系列的解决方案，针对于其它数据库的特性，它仍然毫不示&lt;br /&gt;弱，表现的非比寻常。&lt;br /&gt;MongoDB 同样支持存储过程。关于存储过程你需要知道的第一件事就是它是用javascript 来&lt;br /&gt;写的。也许这会让你很奇怪，为什么它用javascript 来写，但实际上它会让你非常满意，&lt;br /&gt;MongoDB 存储过程是存储在db.system.js 表中的，我们想象一个简单的sql 自定义函数如下：&lt;br /&gt;function addNumbers( x , y ) {&lt;br /&gt;return x + y;&lt;br /&gt;}&lt;br /&gt;下面我们将这个sql 自定义函数转换为MongoDB 的存储过程:&lt;br /&gt;&amp;gt; db.system.js.save({_id:"addNumbers", value:function(x, y){ return x + y; }});&lt;br /&gt;存储过程可以被查看，修改和删除，所以我们用find 来查看一下是否这个存储过程已经被&lt;br /&gt;创建上了。&lt;br /&gt;&amp;gt; db.system.js.find()&lt;br /&gt;{ "_id" : "addNumbers", "value" : function cf__1__f_(x, y) {&lt;br /&gt;return x + y;&lt;br /&gt;} }&lt;br /&gt;&amp;gt;&lt;br /&gt;这样看起来还不错，下面我看来实际调用一下这个存储过程:&lt;br /&gt;&amp;gt; db.eval('addNumbers(3, 4.2)');&lt;br /&gt;7.2&lt;br /&gt;&amp;gt;&lt;br /&gt;这样的操作方法简直太简单了，也许这就是MongoDB 的魅力所在。&lt;br /&gt;db.eval()是一个比较奇怪的东西，我们可以将存储过程的逻辑直接在里面并同时调用，而无&lt;br /&gt;需事先声明存储过程的逻辑。&lt;br /&gt;&amp;gt; db.eval( function() { return 3+3; } );&lt;br /&gt;6&lt;br /&gt;&amp;gt;&lt;br /&gt;从上面可以看出，MongoDB 的存储过程可以方便的完成算术运算，但其它数据库产品在存&lt;br /&gt;储过程中可以处理数据库内部的一些事情，例如取出某张表的数据量等等操作，这些&lt;br /&gt;MongoDB 能做到吗？答案是肯定的，MongoDB 可以轻而易举的做到，看下面的实例吧:&lt;br /&gt;&amp;gt; db.system.js.save({_id:"get_count", value:function(){ return db.c1.count(); }});&lt;br /&gt;&amp;gt; db.eval('get_count()')&lt;br /&gt;2&lt;br /&gt;&lt;br /&gt;35 / 91&lt;br /&gt;可以看到存储过程可以很轻松的在存储过程中操作表&lt;br /&gt;第六章 Capped Collection&lt;br /&gt;6.1 简单介绍&lt;br /&gt;capped collections 是性能出色的有着固定大小的集合，以LRU(Least Recently Used 最近最少&lt;br /&gt;使用)规则和插入顺序进行age-out(老化移出)处理，自动维护集合中对象的插入顺序，在创&lt;br /&gt;建时要预先指定大小。如果空间用完，新添加的对象将会取代集合中最旧的对象。&lt;br /&gt;6.2 功能特点&lt;br /&gt;可以插入及更新，但更新不能超出collection 的大小，否则更新失败。不允许删除，但是可&lt;br /&gt;以调用drop() 删除集合中的所有行，但是drop 后需要显式地重建集合。在32 位机上，一&lt;br /&gt;个capped collection 的最大值约为482.5M，64 位上只受系统文件大小的限制。&lt;br /&gt;6.3 常见用处&lt;br /&gt;1、 logging&lt;br /&gt;MongoDB 中日志机制的首选，MongoDB 没有使用日志文件，而是把日志事件存储在数&lt;br /&gt;据库中。在一个没有索引的capped collection 中插入对象的速度与在文件系统中记录日&lt;br /&gt;志的速度相当。&lt;br /&gt;2、 cache&lt;br /&gt;缓存一些对象在数据库中，比如计算出来的统计信息。这样的需要在collection 上建立&lt;br /&gt;一个索引，因为使用缓存往往是读比写多。&lt;br /&gt;3、 auto archiving&lt;br /&gt;可以利用capped collection 的age-out 特性，省去了写cron 脚本进行人工归档的工作。&lt;br /&gt;6.4 推荐用法&lt;br /&gt;1、 为了发挥capped collection 的最大性能，如果写比读多，最好不要在上面建索引，否则&lt;br /&gt;插入速度从"log speed"降为"database speed"。&lt;br /&gt;2、使用"nature ordering"可以有效地检索最近插入的元素，因为capped collection 能够保证&lt;br /&gt;自然排序就是插入时的顺序，类似于log 文件上的tail 操作。&lt;br /&gt;6.5 注意事项&lt;br /&gt;1、可以在创建capped collection 时指定collection 中能够存放的最大文档数。但这时也要指&lt;br /&gt;定size，因为总是先检查size 后检查maxRowNumber。可以使用validate()查看一个collection&lt;br /&gt;&lt;br /&gt;36 / 91&lt;br /&gt;已经使用了多少空间，从而决定size 设为多大。如：&lt;br /&gt;db.createCollection("mycoll", {capped:true, size:100000, max:100});&lt;br /&gt;db.mycoll.validate();&lt;br /&gt;max=1 时会往collection 中存放尽量多的documents。&lt;br /&gt;2 、上述的createCollection 函数也可以用来创建一般的collection ， 还有一个参数&lt;br /&gt;"autoIndexID"，值可以为"true"和"false"来决定是否需要在"_id"字段上自动创建索引，如：&lt;br /&gt;db.createCollection("mycoll", {size:10000000, autoIndexId:false})。&lt;br /&gt;默认情况下对一般的collection 是创建索引的，但不会对capped collection 创建。&lt;br /&gt;第七章 GridFS&lt;br /&gt;GridFS 是一种将大型文件存储在MongoDB 数据库中的文件规范。所有官方支持的驱动均实&lt;br /&gt;现了GridFS 规范。&lt;br /&gt;7.1 为什么要用GridFS&lt;br /&gt;由于MongoDB 中BSON 对象大小是有限制的，所以GridFS 规范提供了一种透明的机制，可&lt;br /&gt;以将一个大文件分割成为多个较小的文档，这样的机制允许我们有效的保存大文件对象，特&lt;br /&gt;别对于那些巨大的文件，比如视频、高清图片等。&lt;br /&gt;7.2 如何实现海量存储&lt;br /&gt;为实现这点，该规范指定了一个将文件分块的标准。每个文件都将在文件集合对象中保存一&lt;br /&gt;个元数据对象，一个或多个chunk 块对象可被组合保存在一个chunk 块集合中。大多数情况&lt;br /&gt;下，你无需了解此规范中细节，而可将注意力放在各个语言版本的驱动中有关GridFS API 的&lt;br /&gt;部分或是如何使用mongofiles 工具上。&lt;br /&gt;7.3 语言支持&lt;br /&gt;GridFS 对Java, Perl, PHP, Python, Ruby 等程序语言均支持，且提供了良好的API 接口。&lt;br /&gt;7.4 简单介绍&lt;br /&gt;GridFS 使用两个表来存储数据：&lt;br /&gt;&#x1; files 包含元数据对象&lt;br /&gt;&#x1; chunks 包含其他一些相关信息的二进制块&lt;br /&gt;为了使多个GridFS 命名为一个单一的数据库，文件和块都有一个前缀，默认情况下，前缀&lt;br /&gt;是fs，所以任何默认的GridFS 存储将包括命名空间fs.files 和fs.chunks。各种第三方语言的&lt;br /&gt;驱动有权限改变这个前缀，所以你可以尝试设置另一个GridFS 命名空间用于存储照片，它&lt;br /&gt;的具体位置为:photos.files 和photos.chunks。下面我们看一下实际的例子吧。&lt;br /&gt;&lt;br /&gt;37 / 91&lt;br /&gt;7.5 命令行工具&lt;br /&gt;mongofiles 是从命令行操作GridFS 的一种工具，例如我们将&amp;#8221;mongosniff&amp;#8221;这个文件存到库里&lt;br /&gt;面，具体用法如下：&lt;br /&gt;[root@localhost bin]# ./mongofiles put testfile&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;added file: { _id: ObjectId('4fc60175c714c5d960fff76a'), filename: "testfile", chunkSize: 262144,&lt;br /&gt;uploadDate: new Date(1338376565745), md5: "8addbeb77789ae6b2cb75deee30faf1a", length:&lt;br /&gt;16 }&lt;br /&gt;done!&lt;br /&gt;下面我们查一下看库里有哪些GridFS 文件，在&amp;#8221;mongofiles&amp;#8221;后加一个参数&amp;#8221;list&amp;#8221;即可&lt;br /&gt;[root@localhost bin]# ./mongofiles list&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;testfile 16&lt;br /&gt;接下来我们进库里看一下是否有新的东西&lt;br /&gt;[root@localhost bin]# ./mongo&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt; show collections&lt;br /&gt;fs.chunks --上文提到的fs.chunks&lt;br /&gt;fs.files --上文提到的fs.files&lt;br /&gt;system.indexes&lt;br /&gt;system.js&lt;br /&gt;&amp;gt;&lt;br /&gt;我们继续查看fs.files 中的内容&lt;br /&gt;&amp;gt; db.fs.files.find()&lt;br /&gt;{ "_id" : ObjectId("4fc60175c714c5d960fff76a"), "filename" : "testfile", "chunkSize" : 262144,&lt;br /&gt;"uploadDate" : ISODate("2012-05-30T11:16:05.745Z"), "md5" :&lt;br /&gt;"8addbeb77789ae6b2cb75deee30faf1a", "length" : 16 }&lt;br /&gt;字段说明:&lt;br /&gt;&#x1; Filename: 存储的文件名&lt;br /&gt;&#x1; chunkSize: chunks 分块的大小&lt;br /&gt;&#x1; uploadDate: 入库时间&lt;br /&gt;&#x1; md5: 此文件的md5 码&lt;br /&gt;&#x1; length: 文件大小, 单位&amp;#8221;字节&amp;#8221;&lt;br /&gt;看来fs.files 中存储的是一些基础的元数据信息&lt;br /&gt;我们继续查看fs.chunks 中的内容&lt;br /&gt;&amp;gt; db.fs.chunks.find()&lt;br /&gt;{ "_id" : ObjectId("4fc60175cf1154905d949336"), "files_id" :&lt;br /&gt;&lt;br /&gt;38 / 91&lt;br /&gt;ObjectId("4fc60175c714c5d960fff76a"), "n" : 0, "data" :&lt;br /&gt;BinData(0,"SGVyZSBpcyBCZWlqaW5nCg==") }&lt;br /&gt;其中比较重要的字段是&amp;#8221;n&amp;#8221;，它代表的是chunks 的序号，此序号从0 开始，看来fs.chunks&lt;br /&gt;中存储的是一些实际的内容数据信息&lt;br /&gt;我们即然能将此文件存进去，我们就应该有办法将其取出来，下面看一下实例:&lt;br /&gt;[root@localhost bin]# rm testfile&lt;br /&gt;rm：是否删除 一般文件 &amp;#8220;testfile&amp;#8221;? y --先删文件&lt;br /&gt;[root@localhost bin]# ./mongofiles get testfile --将其从库里取出来&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;done write to: testfile&lt;br /&gt;[root@localhost bin]# md5sum testfile --校验md5，结果跟库里相同&lt;br /&gt;8addbeb77789ae6b2cb75deee30faf1a testfile&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;7.6 索引&lt;br /&gt;db.fs.chunks.ensureIndex({files_id:1, n:1}, {unique: true});&lt;br /&gt;这样，一个块就可以利用它的files_id 和 n 的值进行检索。注意，GridFS 仍然可以用findOne&lt;br /&gt;得到第一个块，如下：&lt;br /&gt;db.fs.chunks.findOne({files_id: myFileID, n: 0});&lt;br /&gt;第八章 MapReduce&lt;br /&gt;MongoDB 的MapReduce 相当于Mysql 中的"group by"，所以在MongoDB 上使用 Map/Reduce&lt;br /&gt;进行并行"统计"很容易。&lt;br /&gt;使用MapReduce 要实现两个函数 Map 函数和Reduce 函数，Map 函数调用emit(key, value)，&lt;br /&gt;遍历collection中所有的记录，将key与value传递给Reduce 函数进行处理。Map函数和Reduce&lt;br /&gt;函数可以使用JavaScript 来实现，可以通过db.runCommand 或mapReduce 命令来执行一个&lt;br /&gt;MapReduce 的操作：&lt;br /&gt;db.runCommand(&lt;br /&gt;{ mapreduce : &amp;lt;collection&amp;gt;,&lt;br /&gt;map : &amp;lt;mapfunction&amp;gt;,&lt;br /&gt;reduce : &amp;lt;reducefunction&amp;gt;&lt;br /&gt;[, query : &amp;lt;query filter object&amp;gt;]&lt;br /&gt;[, sort : &amp;lt;sorts the input objects using this key. Useful for optimization, like sorting by the&lt;br /&gt;emit key for fewer reduces&amp;gt;]&lt;br /&gt;[, limit : &amp;lt;number of objects to return from collection&amp;gt;]&lt;br /&gt;[, out : &amp;lt;see output options below&amp;gt;]&lt;br /&gt;&lt;br /&gt;39 / 91&lt;br /&gt;[, keeptemp: &amp;lt;true|false&amp;gt;]&lt;br /&gt;[, finalize : &amp;lt;finalizefunction&amp;gt;]&lt;br /&gt;[, scope : &amp;lt;object where fields go into javascript global scope &amp;gt;]&lt;br /&gt;[, verbose : true]&lt;br /&gt;}&lt;br /&gt;);&lt;br /&gt;参数说明:&lt;br /&gt;&#x1; mapreduce: 要操作的目标集合。&lt;br /&gt;&#x1; map: 映射函数 (生成键值对序列，作为 reduce 函数参数)。&lt;br /&gt;&#x1; reduce: 统计函数。&lt;br /&gt;&#x1; query: 目标记录过滤。&lt;br /&gt;&#x1; sort: 目标记录排序。&lt;br /&gt;&#x1; limit: 限制目标记录数量。&lt;br /&gt;&#x1; out: 统计结果存放集合 (不指定则使用临时集合，在客户端断开后自动删除)。&lt;br /&gt;&#x1; keeptemp: 是否保留临时集合。&lt;br /&gt;&#x1; finalize: 最终处理函数 (对 reduce 返回结果进行最终整理后存入结果集合)。&lt;br /&gt;&#x1; scope: 向 map、reduce、finalize 导入外部变量。&lt;br /&gt;&#x1; verbose: 显示详细的时间统计信息。&lt;br /&gt;下面我们先准备一些数据:&lt;br /&gt;&amp;gt; db.students.insert({classid:1, age:14, name:'Tom'})&lt;br /&gt;&amp;gt; db.students.insert({classid:1, age:12, name:'Jacky'})&lt;br /&gt;&amp;gt; db.students.insert({classid:2, age:16, name:'Lily'})&lt;br /&gt;&amp;gt; db.students.insert({classid:2, age:9, name:'Tony'})&lt;br /&gt;&amp;gt; db.students.insert({classid:2, age:19, name:'Harry'})&lt;br /&gt;&amp;gt; db.students.insert({classid:2, age:13, name:'Vincent'})&lt;br /&gt;&amp;gt; db.students.insert({classid:1, age:14, name:'Bill'})&lt;br /&gt;&amp;gt; db.students.insert({classid:2, age:17, name:'Bruce'})&lt;br /&gt;&amp;gt;&lt;br /&gt;接下来，我们将演示如何统计1 班和2 班的学生数量&lt;br /&gt;8.1 Map&lt;br /&gt;Map 函数必须调用 emit(key, value) 返回键值对，使用 this 访问当前待处理的 Document。&lt;br /&gt;&amp;gt; m = function() { emit(this.classid, 1) }&lt;br /&gt;function () {&lt;br /&gt;emit(this.classid, 1);&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;value 可以使用 JSON Object 传递 (支持多个属性值)。例如:&lt;br /&gt;emit(this.classid, {count:1})&lt;br /&gt;&lt;br /&gt;40 / 91&lt;br /&gt;8.2 Reduce&lt;br /&gt;Reduce 函数接收的参数类似 Group 效果，将 Map 返回的键值序列组合成 { key, [value1,&lt;br /&gt;value2, value3, value...] } 传递给 reduce。&lt;br /&gt;&amp;gt; r = function(key, values) {&lt;br /&gt;... var x = 0;&lt;br /&gt;... values.forEach(function(v) { x += v });&lt;br /&gt;... return x;&lt;br /&gt;... }&lt;br /&gt;function (key, values) {&lt;br /&gt;var x = 0;&lt;br /&gt;values.forEach(function (v) {x += v;});&lt;br /&gt;return x;&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;Reduce 函数对这些 values 进行 "统计" 操作，返回结果可以使用 JSON Object。&lt;br /&gt;8.3 Result&lt;br /&gt;&amp;gt; res = db.runCommand({&lt;br /&gt;... mapreduce:"students",&lt;br /&gt;... map:m,&lt;br /&gt;... reduce:r,&lt;br /&gt;... out:"students_res"&lt;br /&gt;... });&lt;br /&gt;{&lt;br /&gt;"result" : "students_res",&lt;br /&gt;"timeMillis" : 1587,&lt;br /&gt;"counts" : {&lt;br /&gt;"input" : 8,&lt;br /&gt;"emit" : 8,&lt;br /&gt;"output" : 2&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt; db.students_res.find()&lt;br /&gt;{ "_id" : 1, "value" : 3 }&lt;br /&gt;{ "_id" : 2, "value" : 5 }&lt;br /&gt;&amp;gt;&lt;br /&gt;mapReduce() 将结果存储在 "students_res" 表中。&lt;br /&gt;&lt;br /&gt;41 / 91&lt;br /&gt;8.4 Finalize&lt;br /&gt;利用 finalize() 我们可以对 reduce() 的结果做进一步处理。&lt;br /&gt;&amp;gt; f = function(key, value) { return {classid:key, count:value}; }&lt;br /&gt;function (key, value) {&lt;br /&gt;return {classid:key, count:value};&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;我们再重新计算一次，看看返回的结果:&lt;br /&gt;&amp;gt; res = db.runCommand({&lt;br /&gt;... mapreduce:"students",&lt;br /&gt;... map:m,&lt;br /&gt;... reduce:r,&lt;br /&gt;... out:"students_res",&lt;br /&gt;... finalize:f&lt;br /&gt;... });&lt;br /&gt;{&lt;br /&gt;"result" : "students_res",&lt;br /&gt;"timeMillis" : 804,&lt;br /&gt;"counts" : {&lt;br /&gt;"input" : 8,&lt;br /&gt;"emit" : 8,&lt;br /&gt;"output" : 2&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt; db.students_res.find()&lt;br /&gt;{ "_id" : 1, "value" : { "classid" : 1, "count" : 3 } }&lt;br /&gt;{ "_id" : 2, "value" : { "classid" : 2, "count" : 5 } }&lt;br /&gt;&amp;gt;&lt;br /&gt;列名变与 &amp;#8220;classid&amp;#8221;和&amp;#8221;count&amp;#8221;了，这样的列表更容易理解。&lt;br /&gt;8.5 Options&lt;br /&gt;我们还可以添加更多的控制细节。&lt;br /&gt;&amp;gt; res = db.runCommand({&lt;br /&gt;... mapreduce:"students",&lt;br /&gt;... map:m,&lt;br /&gt;... reduce:r,&lt;br /&gt;... out:"students_res",&lt;br /&gt;... finalize:f,&lt;br /&gt;... query:{age:{$lt:10}}&lt;br /&gt;... });&lt;br /&gt;&lt;br /&gt;42 / 91&lt;br /&gt;{&lt;br /&gt;"result" : "students_res",&lt;br /&gt;"timeMillis" : 358,&lt;br /&gt;"counts" : {&lt;br /&gt;"input" : 1,&lt;br /&gt;"emit" : 1,&lt;br /&gt;"output" : 1&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt; db.students_res.find();&lt;br /&gt;{ "_id" : 2, "value" : { "classid" : 2, "count" : 1 } }&lt;br /&gt;&amp;gt;&lt;br /&gt;可以看到先进行了过滤，只取age&amp;lt;10 的数据，然后再进行统计，所以就没有1 班的统计数&lt;br /&gt;据了。&lt;br /&gt;第三部分 管理篇&lt;br /&gt;第九章 数据导出 mongoexport&lt;br /&gt;作为DBA，经常会碰到导入导出数据的需求，下面就介绍实用工具mongoexport 和&lt;br /&gt;mongoimport 的使用方法，望你会有所收获。&lt;br /&gt;假设库里有一张user 表，里面有2 条记录，我们要将它导出&lt;br /&gt;&amp;gt; use my_mongodb&lt;br /&gt;switched to db my_mongodb&lt;br /&gt;&amp;gt; db.user.find();&lt;br /&gt;{ "_id" : ObjectId("4f81a4a1779282ca68fd8a5a"), "uid" : 2, "username" : "Jerry", "age" : 100 }&lt;br /&gt;{ "_id" : ObjectId("4f844d1847d25a9ce5f120c4"), "uid" : 1, "username" : "Tom", "age" : 25 }&lt;br /&gt;&amp;gt;&lt;br /&gt;9.1 常用导出方法&lt;br /&gt;[root@localhost bin]# ./mongoexport -d my_mongodb -c user -o user.dat&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;exported 2 records&lt;br /&gt;[root@localhost bin]# cat user.dat&lt;br /&gt;{ "_id" : { "$oid" : "4f81a4a1779282ca68fd8a5a" }, "uid" : 2, "username" : "Jerry", "age" : 100 }&lt;br /&gt;{ "_id" : { "$oid" : "4f844d1847d25a9ce5f120c4" }, "uid" : 1, "username" : "Tom", "age" : 25 }&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;参数说明:&lt;br /&gt;&#x1; -d 指明使用的库, 本例中为&amp;#8221; my_mongodb&amp;#8221;&lt;br /&gt;&lt;br /&gt;43 / 91&lt;br /&gt;&#x1; -c 指明要导出的表, 本例中为&amp;#8221;user&amp;#8221;&lt;br /&gt;&#x1; -o 指明要导出的文件名, 本例中为&amp;#8221;user.dat&amp;#8221;&lt;br /&gt;从上面可以看到导出的方式使用的是JSON 的样式&lt;br /&gt;9.2 导出CSV格式的文件&lt;br /&gt;[root@localhost bin]# ./mongoexport -d my_mongodb -c user --csv -f uid,username,age -o&lt;br /&gt;user_csv.dat&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;exported 2 records&lt;br /&gt;[root@localhost bin]# cat user_csv.dat&lt;br /&gt;uid,username,age&lt;br /&gt;2,"Jerry",100&lt;br /&gt;1,"Tom",25&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;参数说明:&lt;br /&gt;&#x1; -csv 指要要导出为csv 格式&lt;br /&gt;&#x1; -f 指明需要导出哪些例&lt;br /&gt;更详细的用法可以 mongoexport &amp;#8211;help 来查看&lt;br /&gt;第十章 数据导入mongoimport&lt;br /&gt;在上例中我们讨论的是导出工具的使用，那么本节将讨论如何向表中导入数据&lt;br /&gt;10.1 导入JSON 数据&lt;br /&gt;我们先将表user 删除掉，以便演示效果&lt;br /&gt;&amp;gt; db.user.drop();&lt;br /&gt;true&lt;br /&gt;&amp;gt; show collections;&lt;br /&gt;system.indexes&lt;br /&gt;&amp;gt;&lt;br /&gt;然后导入数据&lt;br /&gt;[root@localhost bin]# ./mongoimport -d my_mongodb -c user user.dat&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;imported 2 objects&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;可以看到导入数据的时候会隐式创建表结构&lt;br /&gt;&lt;br /&gt;44 / 91&lt;br /&gt;10.2 导入CSV数据&lt;br /&gt;我们先将表user 删除掉，以便演示效果&lt;br /&gt;&amp;gt; db.user.drop();&lt;br /&gt;true&lt;br /&gt;&amp;gt; show collections;&lt;br /&gt;system.indexes&lt;br /&gt;&amp;gt;&lt;br /&gt;然后导入数据&lt;br /&gt;[root@localhost bin]# ./mongoimport -d my_mongodb -c user --type csv --headerline --file&lt;br /&gt;user_csv.dat&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;imported 3 objects&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;参数说明:&lt;br /&gt;&#x1; -type 指明要导入的文件格式&lt;br /&gt;&#x1; -headerline 批明不导入第一行，因为第一行是列名&lt;br /&gt;&#x1; -file 指明要导入的文件路径&lt;br /&gt;注意:&lt;br /&gt;CSV 格式良好，主流数据库都支持导出为CSV 的格式，所以这种格式非常利于异构数据迁移&lt;br /&gt;第十一章 数据备份mongodump&lt;br /&gt;可以用mongodump 来做MongoDB 的库或表级别的备份，下面举例说明:&lt;br /&gt;备份my_mongodb 数据库&lt;br /&gt;[root@localhost bin]# ./mongodump -d my_mongodb&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;DATABASE: my_mongodb to dump/my_mongodb&lt;br /&gt;my_mongodb.system.indexes to dump/my_mongodb/system.indexes.bson&lt;br /&gt;1 objects&lt;br /&gt;my_mongodb.user to dump/my_mongodb/user.bson&lt;br /&gt;2 objects&lt;br /&gt;[root@localhost bin]# ll&lt;br /&gt;总计 67648&lt;br /&gt;-rwxr-xr-x 1 root root 7508756 2011-04-06 bsondump&lt;br /&gt;drwxr-xr-x 3 root root 4096 04-10 23:54 dump&lt;br /&gt;-rwxr-xr-x 1 root root 2978016 2011-04-06 mongo&lt;br /&gt;此时会在当前目录下创建一个dump 目录，用于存放备份出来的文件&lt;br /&gt;也可以指定备份存放的目录，&lt;br /&gt;&lt;br /&gt;45 / 91&lt;br /&gt;[root@localhost bin]# ./mongodump -d my_mongodb -o my_mongodb_dump&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;DATABASE: my_mongodb to my_mongodb_dump/my_mongodb&lt;br /&gt;my_mongodb.system.indexes to&lt;br /&gt;my_mongodb_dump/my_mongodb/system.indexes.bson&lt;br /&gt;1 objects&lt;br /&gt;my_mongodb.user to my_mongodb_dump/my_mongodb/user.bson&lt;br /&gt;2 objects&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;这个例子中将备份的文件存在了当前目录下的my_mongodb_dump 目录下&lt;br /&gt;第十二章 数据恢复mongorestore&lt;br /&gt;由于刚刚已经做了备份，所以我们先将库my_mongodb 删除掉&lt;br /&gt;&amp;gt; use my_mongodb&lt;br /&gt;switched to db my_mongodb&lt;br /&gt;&amp;gt; db.dropDatabase()&lt;br /&gt;{ "dropped" : "my_mongodb", "ok" : 1 }&lt;br /&gt;&amp;gt; show dbs&lt;br /&gt;admin (empty)&lt;br /&gt;local (empty)&lt;br /&gt;test (empty)&lt;br /&gt;&amp;gt;&lt;br /&gt;接下来我们进行数据库恢复&lt;br /&gt;[root@localhost bin]# ./mongorestore -d my_mongodb my_mongodb_dump/*&lt;br /&gt;connected to: 127.0.0.1&lt;br /&gt;Wed Apr 11 00:03:03 my_mongodb_dump/my_mongodb/user.bson&lt;br /&gt;Wed Apr 11 00:03:03 going into namespace [my_mongodb.user]&lt;br /&gt;Wed Apr 11 00:03:03 2 objects found&lt;br /&gt;Wed Apr 11 00:03:03 my_mongodb_dump/my_mongodb/system.indexes.bson&lt;br /&gt;Wed Apr 11 00:03:03 going into namespace [my_mongodb.system.indexes]&lt;br /&gt;Wed Apr 11 00:03:03 { name: "_id_", ns: "my_mongodb.user", key: { _id: 1 }, v: 0 }&lt;br /&gt;Wed Apr 11 00:03:03 1 objects found&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;经验证数据库又回来了，其实要是想恢复库，也大可不必先删除my_mongodb 库，只要指&lt;br /&gt;明 &amp;#8211;drop 参数，就可以在恢复的时候先删除表然后再向表中插入数据的&lt;br /&gt;第十三章 访问控制&lt;br /&gt;官方手册中启动 MongoDB 服务时没有任何参数，一旦客户端连接后可以对数据库任意操&lt;br /&gt;&lt;br /&gt;46 / 91&lt;br /&gt;作，而且可以远程访问数据库，所以推荐开发阶段可以不设置任何参数，但对于生产环境还&lt;br /&gt;是要仔细考虑一下安全方面的因素，而提高 MongoDB 数据库安全有几个方面：&lt;br /&gt;&#x1; 绑定IP 内网地址访问MongoDB 服务&lt;br /&gt;&#x1; 设置监听端口&lt;br /&gt;&#x1; 使用用户名和口令登录&lt;br /&gt;13.1 绑定IP内网地址访问MongoDB服务&lt;br /&gt;MongoDB 可以限制只允许某一特定IP 来访问，只要在启动时加一个参数bind_ip 即可，如&lt;br /&gt;下:&lt;br /&gt;服务端限制只有192.168.1.103 这个IP 可以访问MongoDB 服务&lt;br /&gt;[root@localhost bin]# ./mongod --bind_ip 192.168.1.103&lt;br /&gt;客户端访问时需要明确指定服务端的IP，否则会报错:&lt;br /&gt;[root@localhost bin]# ./mongo 192.168.1.102&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 192.168.1.103/test&lt;br /&gt;&amp;gt;&lt;br /&gt;13.2 设置监听端口&lt;br /&gt;官方默认的监听端口是27017，为了安全起见，一般都会修改这个监听端口，避免恶意的连&lt;br /&gt;接尝试，具体如下：&lt;br /&gt;将服务端监听端口修改为28018&lt;br /&gt;[root@localhost bin]# ./mongod --bind_ip 192.168.1.103 --port 28018&lt;br /&gt;端户访问时不指定端口，会连接到默认端口27017，对于本例会报错&lt;br /&gt;[root@localhost bin]# ./mongo 192.168.1.102&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 192.168.1.102/test&lt;br /&gt;Sun Apr 15 15:55:51 Error: couldn't connect to server 192.168.1.102 shell/mongo.js:81&lt;br /&gt;exception: connect failed&lt;br /&gt;所以当服务端指定了端口后，客户端必须要明确指定端口才可以正常访问&lt;br /&gt;[root@localhost bin]# ./mongo 192.168.1.102:28018&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 192.168.1.102:28018/test&lt;br /&gt;&amp;gt;&lt;br /&gt;13.3 使用用户名和口令登录&lt;br /&gt;MongoDB 默认的启动是不验证用户名和密码的,启动MongoDB 后,可以直接用MongoDB 连接&lt;br /&gt;&lt;br /&gt;47 / 91&lt;br /&gt;上来,对所有的库具有root 权限。所以启动的时候指定参数,可以阻止客户端的访问和连接。&lt;br /&gt;先启用系统的登录验证模块, 只需在启动时指定 auth 参数即可，如:&lt;br /&gt;[root@localhost bin]# ./mongod --auth&lt;br /&gt;本地客户端连接一下看看效果;&lt;br /&gt;[root@localhost bin]# ./mongo&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt; show collections;&lt;br /&gt;&amp;gt;&lt;br /&gt;很奇怪，为什么我们启用了登录验证模块，但我们登录时没有指定用户，为什么还可以登录&lt;br /&gt;呢？在最初始的时候 MongoDB 都默认有一个 admin 数据库（默认是空的），&lt;br /&gt;而 admin.system.users 中将会保存比在其它数据库中设置的用户权限更大的用户信息。&lt;br /&gt;注意：当 admin.system.users 中没有添加任何用户时，即使 MongoDB 启动时添加了 --auth&lt;br /&gt;参数，如果在除 admin 数据库中添加了用户，此时不进行任何认证依然可以使用任何操作，&lt;br /&gt;直到知道你在 admin.system.users 中添加了一个用户。&lt;br /&gt;1、建立系统root用户&lt;br /&gt;在admin 库中新添一个用户root:&lt;br /&gt;[root@localhost bin]# ./mongo&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt; db.addUser("root","111")&lt;br /&gt;{&lt;br /&gt;"user" : "root",&lt;br /&gt;"readOnly" : false,&lt;br /&gt;"pwd" : "e54950178e2fa777b1d174e9b106b6ab"&lt;br /&gt;}&lt;br /&gt;&amp;gt; db.auth("root","111")&lt;br /&gt;1&lt;br /&gt;&amp;gt;&lt;br /&gt;本地客户端连接，但不指定用户，结果如下:&lt;br /&gt;[root@localhost bin]# ./mongo&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt; show collections;&lt;br /&gt;Sun Apr 15 16:36:52 uncaught exception: error: {&lt;br /&gt;"$err" : "unauthorized db:test lock type:-1 client:127.0.0.1",&lt;br /&gt;"code" : 10057&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;连上test 库了，但进一步操作时有异常，看来MongoDB 允许未授权连接，不能进行任何&lt;br /&gt;&lt;br /&gt;48 / 91&lt;br /&gt;本地客户端连接，指定用户，结果如下:&lt;br /&gt;[root@localhost bin]# ./mongo -u root -p&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;Enter password:&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt; show collections;&lt;br /&gt;system.indexes&lt;br /&gt;system.users&lt;br /&gt;&amp;gt;&lt;br /&gt;看来指定了用户名之后，访问数据库才是正常&lt;br /&gt;2、建立指定权限用户&lt;br /&gt;MongoDB 也支持为某个特定的数据库来设置用户，如我们为test 库设一个只读的用户&lt;br /&gt;user_reader:&lt;br /&gt;[root@localhost bin]# ./mongo -u root -p&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;Enter password:&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt; show collections;&lt;br /&gt;system.indexes&lt;br /&gt;system.users&lt;br /&gt;&amp;gt; use test&lt;br /&gt;switched to db test&lt;br /&gt;&amp;gt; db.addUser("user_reader", "user_pwd", true)&lt;br /&gt;{&lt;br /&gt;"user" : "user_reader",&lt;br /&gt;"readOnly" : true,&lt;br /&gt;"pwd" : "0809760bb61ee027199e513c5ecdedc6"&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;客户端用此用户来访问:&lt;br /&gt;[root@localhost bin]# ./mongo -u user_reader -p&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;Enter password:&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt; show collections;&lt;br /&gt;system.indexes&lt;br /&gt;system.users&lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;49 / 91&lt;br /&gt;第十四章 命令行操作&lt;br /&gt;MongoDB shell 不仅仅是一个交互式的shell，它也支持执行指定javascript 文件，也支持执行&lt;br /&gt;指定的命令片断。&lt;br /&gt;有了这个特性，就可以将MongoDB 与linux shell 完美结合，完成大部分的日常管理和维护&lt;br /&gt;工作。&lt;br /&gt;14.1 通过eval参数执行指定语句&lt;br /&gt;例如，需要查询test 库的t1 表中的记录数有多少，常用方法如下:&lt;br /&gt;[root@localhost bin]# ./mongo test&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt; db.t1.count()&lt;br /&gt;7&lt;br /&gt;&amp;gt;&lt;br /&gt;通过命令行eval 参数直接执行语句:&lt;br /&gt;[root@localhost bin]# ./mongo test --eval "printjson(db.t1.count())"&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;7&lt;br /&gt;14.2 执行指定文件中的内容&lt;br /&gt;如果涉及到很多的操作后，才能得到结果，那么用eval 的方式来做的话是不可能完成的，&lt;br /&gt;那么更灵活的执行指定文件的方式就派上用场了。例如我们仍然要查看test 库t1 表中的记&lt;br /&gt;录数：&lt;br /&gt;t1_count.js 就是我们要执行的文件，里面的内容如下&lt;br /&gt;[root@localhost bin]# cat t1_count.js&lt;br /&gt;var totalcount = db.t1.count();&lt;br /&gt;printjson('Total count of t1 is : ' + totalcount);&lt;br /&gt;printjson('-----------------------');&lt;br /&gt;下面我们将执行这个文件&lt;br /&gt;[root@localhost bin]# ./mongo t1_count.js&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;"Total count of t1 is : 7"&lt;br /&gt;"-----------------------"&lt;br /&gt;&lt;br /&gt;50 / 91&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;大家可以看到最终得到t1 表的记录数 7，那么一些不必要的说明性文字我们要是不希望出&lt;br /&gt;现该怎么办呢?&lt;br /&gt;[root@localhost bin]# ./mongo --quiet t1_count.js&lt;br /&gt;"Total count of t1 is : 7"&lt;br /&gt;"-----------------------"&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;通过指定quiet 参数，即可以将一些登录信息屏蔽掉，这样可以让结果更清晰。&lt;br /&gt;第十五章 进程控制&lt;br /&gt;DBA 经常要解决系统的一些查询性能问题，此时一般的操作习惯是先查看有哪些进程，然后&lt;br /&gt;将异常的进程杀掉，那么MongoDB 是怎么样处理的呢?&lt;br /&gt;15.1 查看活动进程&lt;br /&gt;查看活动进程，便于了解系统正在做什么，以便做下一步判断&lt;br /&gt;&amp;gt; db.currentOp();&lt;br /&gt;&amp;gt; // 等同于: db.$cmd.sys.inprog.findOne()&lt;br /&gt;{ inprog: [ { "opid" : 18 , "op" : "query" , "ns" : "mydb.votes" ,&lt;br /&gt;"query" : "{ score : 1.0 }" , "inLock" : 1 }&lt;br /&gt;]&lt;br /&gt;}&lt;br /&gt;字段说明:&lt;br /&gt;&#x1; Opid: 操作进程号&lt;br /&gt;&#x1; Op: 操作类型(查询，更新等)&lt;br /&gt;&#x1; Ns: 命名空间, 指操作的是哪个对象&lt;br /&gt;&#x1; Query: 如果操作类型是查询的话，这里将显示具体的查询内容&lt;br /&gt;&#x1; lockType: 锁的类型，指明是读锁还是写锁&lt;br /&gt;15.2 结束进程&lt;br /&gt;如果某个异常是由于某个进程产生的，那么一般DBA 都会毫不留情的杀掉这个罪魁祸首的&lt;br /&gt;进程，下面将是这操作&lt;br /&gt;&amp;gt; db.killOp(1234/*opid*/)&lt;br /&gt;&amp;gt; // 等同于: db.$cmd.sys.killop.findOne({op:1234})&lt;br /&gt;注意:&lt;br /&gt;不要kill 内部发起的操作，比如说replica set 发起的sync 操作等&lt;br /&gt;&lt;br /&gt;51 / 91&lt;br /&gt;第四部分 性能篇&lt;br /&gt;第十六章 索引&lt;br /&gt;MongoDB 提供了多样性的索引支持，索引信息被保存在system.indexes 中，且默认总是为_id&lt;br /&gt;创建索引，它的索引使用基本和MySQL 等关系型数据库一样。其实可以这样说说，索引是&lt;br /&gt;凌驾于数据存储系统之上的另一层系统，所以各种结构迥异的存储都有相同或相似的索引实&lt;br /&gt;现及使用接口并不足为奇。&lt;br /&gt;16.1 基础索引&lt;br /&gt;在字段age 上创建索引，1(升序);-1(降序)&lt;br /&gt;&amp;gt; db.t3.ensureIndex({age:1})&lt;br /&gt;&amp;gt; db.t3.getIndexes();&lt;br /&gt;[&lt;br /&gt;{&lt;br /&gt;"name" : "_id_",&lt;br /&gt;"ns" : "test.t3",&lt;br /&gt;"key" : {&lt;br /&gt;"_id" : 1&lt;br /&gt;},&lt;br /&gt;"v" : 0&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : ObjectId("4fb906da0be632163d0839fe"),&lt;br /&gt;"ns" : "test.t3",&lt;br /&gt;"key" : {&lt;br /&gt;"age" : 1&lt;br /&gt;},&lt;br /&gt;"name" : "age_1",&lt;br /&gt;"v" : 0&lt;br /&gt;}&lt;br /&gt;]&lt;br /&gt;&amp;gt;&lt;br /&gt;上例显示出来的一共有2 个索引，其中_id 是创建表的时候自动创建的索引，此索引是不能&lt;br /&gt;够删除的。&lt;br /&gt;当系统已有大量数据时，创建索引就是个非常耗时的活，我们可以在后台执行，只需指定&lt;br /&gt;&amp;#8220;backgroud:true&amp;#8221;即可。&lt;br /&gt;&amp;gt; db.t3.ensureIndex({age:1} , {backgroud:true})&lt;br /&gt;&lt;br /&gt;52 / 91&lt;br /&gt;16.2 文档索引&lt;br /&gt;索引可以任何类型的字段，甚至文档&lt;br /&gt;db.factories.insert( { name: "wwl", addr: { city: "Beijing", state: "BJ" } } );&lt;br /&gt;//在addr 列上创建索引&lt;br /&gt;db.factories.ensureIndex( { addr : 1 } );&lt;br /&gt;//下面这个查询将会用到我们刚刚建立的索引&lt;br /&gt;db.factories.find( { addr: { city: "Beijing", state: "BJ" } } );&lt;br /&gt;//但是下面这个查询将不会用到索引，因为查询的顺序跟索引建立的顺序不一样&lt;br /&gt;db.factories.find( { addr: { state: "BJ" , city: "Beijing"} } );&lt;br /&gt;16.3 组合索引&lt;br /&gt;跟其它数据库产品一样，MongoDB 也是有组合索引的，下面我们将在addr.city 和addr.state&lt;br /&gt;上建立组合索引。当创建组合索引时，字段后面的1 表示升序，-1 表示降序，是用1 还是&lt;br /&gt;用-1 主要是跟排序的时候或指定范围内查询 的时候有关的。&lt;br /&gt;db.factories.ensureIndex( { "addr.city" : 1, "addr.state" : 1 } );&lt;br /&gt;// 下面的查询都用到了这个索引&lt;br /&gt;db.factories.find( { "addr.city" : "Beijing", "addr.state" : "BJ" } );&lt;br /&gt;db.factories.find( { "addr.city" : "Beijing" } );&lt;br /&gt;db.factories.find().sort( { "addr.city" : 1, "addr.state" : 1 } );&lt;br /&gt;db.factories.find().sort( { "addr.city" : 1 } )&lt;br /&gt;16.4 唯一索引&lt;br /&gt;只需在ensureIndex 命令中指定&amp;#8221;unique:true&amp;#8221;即可创建唯一索引。例如，往表t4 中插入2 条&lt;br /&gt;记录&lt;br /&gt;db.t4.insert({firstname: "wang", lastname: "wenlong"});&lt;br /&gt;db.t4.insert({firstname: "wang", lastname: "wenlong"});&lt;br /&gt;在t4 表中建立唯一索引&lt;br /&gt;&amp;gt; db.t4.ensureIndex({firstname: 1, lastname: 1}, {unique: true});&lt;br /&gt;E11000 duplicate key error index: test.t4.$firstname_1_lastname_1 dup key: { : "wang", :&lt;br /&gt;"wenlong" }&lt;br /&gt;可以看到，当建唯一索引时，系统报了&amp;#8220;表里有重复值&amp;#8221;的错，具体原因就是因为表中有2&lt;br /&gt;条一模一模的数据，所以建立不了唯一索引。&lt;br /&gt;&lt;br /&gt;53 / 91&lt;br /&gt;16.5 强制使用索引&lt;br /&gt;hint 命令可以强制使用某个索引。&lt;br /&gt;&amp;gt; db.t5.insert({name: "wangwenlong",age: 20})&lt;br /&gt;&amp;gt; db.t5.ensureIndex({name:1, age:1})&lt;br /&gt;&amp;gt; db.t5.find({age:{$lt:30}}).explain()&lt;br /&gt;{&lt;br /&gt;"cursor" : "BasicCursor",&lt;br /&gt;"nscanned" : 1,&lt;br /&gt;"nscannedObjects" : 1,&lt;br /&gt;"n" : 1,&lt;br /&gt;"millis" : 0,&lt;br /&gt;"nYields" : 0,&lt;br /&gt;"nChunkSkips" : 0,&lt;br /&gt;"isMultiKey" : false,&lt;br /&gt;"indexOnly" : false,&lt;br /&gt;"indexBounds" : { --并没有用到索引&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&amp;gt; db.t5.find({age:{$lt:30}}).hint({name:1, age:1}).explain() --强制使用索引&lt;br /&gt;{&lt;br /&gt;"cursor" : "BtreeCursor name_1_age_1",&lt;br /&gt;"nscanned" : 1,&lt;br /&gt;"nscannedObjects" : 1,&lt;br /&gt;"n" : 1,&lt;br /&gt;"millis" : 1,&lt;br /&gt;"nYields" : 0,&lt;br /&gt;"nChunkSkips" : 0,&lt;br /&gt;"isMultiKey" : false,&lt;br /&gt;"indexOnly" : false,&lt;br /&gt;"indexBounds" : { --被强制使用索引了&lt;br /&gt;"name" : [&lt;br /&gt;[&lt;br /&gt;{&lt;br /&gt;"$minElement" : 1&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"$maxElement" : 1&lt;br /&gt;}&lt;br /&gt;]&lt;br /&gt;],&lt;br /&gt;"age" : [&lt;br /&gt;&lt;br /&gt;54 / 91&lt;br /&gt;[&lt;br /&gt;-1.7976931348623157e+308,&lt;br /&gt;30&lt;br /&gt;]&lt;br /&gt;]&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;16.6 删除索引&lt;br /&gt;删除索引分为删除某张表的所有索引和删除某张表的某个索引，具体如下：&lt;br /&gt;//删除t3 表中的所有索引&lt;br /&gt;db.t3.dropIndexes()&lt;br /&gt;//删除t4 表中的firstname 索引&lt;br /&gt;db.t4.dropIndex({firstname: 1})&lt;br /&gt;第十七章 explain执行计划&lt;br /&gt;MongoDB 提供了一个 explain 命令让我们获知系统如何处理查询请求。利用 explain 命令，&lt;br /&gt;我们可以很好地观察系统如何使用索引来加快检索，同时可以针对性优化索引。&lt;br /&gt;&amp;gt; db.t5.ensureIndex({name:1})&lt;br /&gt;&amp;gt; db.t5.ensureIndex({age:1})&lt;br /&gt;&amp;gt; db.t5.find({age:{$gt:45}}, {name:1}).explain()&lt;br /&gt;{&lt;br /&gt;"cursor" : "BtreeCursor age_1",&lt;br /&gt;"nscanned" : 0,&lt;br /&gt;"nscannedObjects" : 0,&lt;br /&gt;"n" : 0,&lt;br /&gt;"millis" : 0,&lt;br /&gt;"nYields" : 0,&lt;br /&gt;"nChunkSkips" : 0,&lt;br /&gt;"isMultiKey" : false,&lt;br /&gt;"indexOnly" : false,&lt;br /&gt;"indexBounds" : {&lt;br /&gt;"age" : [&lt;br /&gt;[&lt;br /&gt;45,&lt;br /&gt;1.7976931348623157e+308&lt;br /&gt;]&lt;br /&gt;]&lt;br /&gt;&lt;br /&gt;55 / 91&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;字段说明:&lt;br /&gt;&#x1; cursor: 返回游标类型(BasicCursor 或 BtreeCursor)&lt;br /&gt;&#x1; nscanned: 被扫描的文档数量&lt;br /&gt;&#x1; n: 返回的文档数量&lt;br /&gt;&#x1; millis: 耗时(毫秒)&lt;br /&gt;&#x1; indexBounds: 所使用的索引&lt;br /&gt;第十八章 优化器profile&lt;br /&gt;在MySQL 中，慢查询日志是经常作为我们优化数据库的依据，那在MongoDB 中是否有类似&lt;br /&gt;的功能呢?答案是肯定的，那就是MongoDB Database Profiler。所以MongoDB 不仅有，而且&lt;br /&gt;还有一些比MySQL 的Slow Query Log 更详细的信息。&lt;br /&gt;18.1 开启 Profiling 功能&lt;br /&gt;有两种方式可以控制 Profiling 的开关和级别，第一种是直接在启动参数里直接进行设置。&lt;br /&gt;启动MongoDB 时加上&amp;#8211;profile=级别 即可。&lt;br /&gt;也可以在客户端调用db.setProfilingLevel(级别) 命令来实时配置，Profiler 信息保存在&lt;br /&gt;system.profile 中。我们可以通过db.getProfilingLevel()命令来获取当前的Profile 级别，类似如&lt;br /&gt;下操作&lt;br /&gt;&amp;gt; db.setProfilingLevel(2);&lt;br /&gt;{ "was" : 0, "slowms" : 100, "ok" : 1 }&lt;br /&gt;上面profile 的级别可以取0，1，2 三个值，他们表示的意义如下：&lt;br /&gt;&#x1; 0 &amp;#8211; 不开启&lt;br /&gt;&#x1; 1 &amp;#8211; 记录慢命令 (默认为&amp;gt;100ms)&lt;br /&gt;&#x1; 2 &amp;#8211; 记录所有命令&lt;br /&gt;Profile 记录在级别1 时会记录慢命令，那么这个慢的定义是什么?上面我们说到其默认为&lt;br /&gt;100ms，当然有默认就有设置，其设置方法和级别一样有两种，一种是通过添加&amp;#8211;slowms 启&lt;br /&gt;动参数配置。第二种是调用db.setProfilingLevel 时加上第二个参数：&lt;br /&gt;db.setProfilingLevel( level , slowms )&lt;br /&gt;db.setProfilingLevel( 1 , 10 );&lt;br /&gt;18.2 查询 Profiling 记录&lt;br /&gt;与MySQL 的慢查询日志不同，MongoDB Profile 记录是直接存在系统db 里的，记录位置&lt;br /&gt;system.profile ，所以，我们只要查询这个Collection 的记录就可以获取到我们的 Profile 记&lt;br /&gt;录了。列出执行时间长于某一限度(5ms)的 Profile 记录：&lt;br /&gt;db.system.profile.find( { millis : { $gt : 5 } } )&lt;br /&gt;&lt;br /&gt;56 / 91&lt;br /&gt;查看最新的 Profile 记录：&lt;br /&gt;db.system.profile.find().sort({$natural:-1}).limit(1)&lt;br /&gt;&amp;gt; db.system.profile.find().sort({$natural:-1}).limit(1)&lt;br /&gt;{ "ts" : ISODate("2012-05-20T16:50:36.321Z"), "info" : "query test.system.profile reslen:1219&lt;br /&gt;nscanned:8 \nquery: { query: {}, orderby: { $natural: -1.0 } } nreturned:8 bytes:1203", "millis" :&lt;br /&gt;0 }&lt;br /&gt;&amp;gt;&lt;br /&gt;字段说明:&lt;br /&gt;&#x1; ts： 该命令在何时执行&lt;br /&gt;&#x1; info: 本命令的详细信息&lt;br /&gt;&#x1; reslen: 返回结果集的大小&lt;br /&gt;&#x1; nscanned: 本次查询扫描的记录数&lt;br /&gt;&#x1; nreturned: 本次查询实际返回的结果集&lt;br /&gt;&#x1; millis: 该命令执行耗时，以毫秒记&lt;br /&gt;MongoDB Shell 还提供了一个比较简洁的命令show profile，可列出最近5 条执行时间超过&lt;br /&gt;1ms 的 Profile 记录。&lt;br /&gt;第十九章 性能优化&lt;br /&gt;如果nscanned(扫描的记录数)远大于nreturned(返回结果的记录数)的话，那么我们就要考虑&lt;br /&gt;通过加索引来优化记录定位了。&lt;br /&gt;reslen 如果过大，那么说明我们返回的结果集太大了，这时请查看find 函数的第二个参数是&lt;br /&gt;否只写上了你需要的属性名。&lt;br /&gt;对于创建索引的建议是：如果很少读，那么尽量不要添加索引，因为索引越多，写操作会越&lt;br /&gt;慢。如果读量很大，那么创建索引还是比较划算的。&lt;br /&gt;假设我们按照时间戳查询最近发表的10 篇博客文章：&lt;br /&gt;articles = db.posts.find().sort({ts:-1});&lt;br /&gt;for (var i=0; i&amp;lt; 10; i++) {&lt;br /&gt;print(articles[i].getSummary());&lt;br /&gt;}&lt;br /&gt;19.1 优化方案1: 创建索引&lt;br /&gt;在查询条件的字段上，或者排序条件的字段上创建索引，可以显著提高执行效率：&lt;br /&gt;db.posts.ensureIndex({ts:1});&lt;br /&gt;19.2 优化方案2: 限定返回结果条数&lt;br /&gt;使用limit()限定返回结果集的大小，可以减少database server 的资源消耗，可以减少网络传&lt;br /&gt;&lt;br /&gt;57 / 91&lt;br /&gt;输数据量。&lt;br /&gt;articles = db.posts.find().sort({ts:-1}).limit(10);&lt;br /&gt;19.3 优化方案3: 只查询使用到的字段，而不查询所有字段&lt;br /&gt;在本例中，博客日志记录内容可能非常大，而且还包括了评论内容（作为embeded 文档）。&lt;br /&gt;所以只查询使用的字段，比查询所有字段效率更高：&lt;br /&gt;articles = db.posts.find({}, {ts:1,title:1,author:1,abstract:1}).sort({ts:-1}).limit(10);&lt;br /&gt;注意：如果只查询部分字段的话，不能用返回的对象直接更新数据库。下面的代码是错误的：&lt;br /&gt;a_post = db.posts.findOne({}, Post.summaryFields);&lt;br /&gt;a_post.x = 3;&lt;br /&gt;db.posts.save(a_post);&lt;br /&gt;19.4 优化方案4: 采用capped collection&lt;br /&gt;capped Collections 比普通Collections 的读写效率高。Capped Collections 是高效率的Collection&lt;br /&gt;类型，它有如下特点：&lt;br /&gt;1、 固定大小；Capped Collections 必须事先创建，并设置大小：&lt;br /&gt;db.createCollection("mycoll", {capped:true, size:100000})&lt;br /&gt;2、 Capped Collections 可以insert 和update 操作；不能delete 操作。只能用drop（）方法&lt;br /&gt;删除整个Collection。&lt;br /&gt;3、 默认基于Insert 的次序排序的。如果查询时没有排序，则总是按照insert 的顺序返回。&lt;br /&gt;4、 FIFO。如果超过了Collection 的限定大小，则用FIFO 算法，新记录将替代最先insert 的&lt;br /&gt;记录。&lt;br /&gt;19.5 优化方案5: 采用Server Side Code Execution&lt;br /&gt;Server-Side Processing 类似于SQL 数据库的存储过程，使用Server-Side Processing 可以减小网&lt;br /&gt;络通讯的开销。&lt;br /&gt;19.6 优化方案6: Hint&lt;br /&gt;一般情况下MongoDB query optimizer 都工作良好，但有些情况下使用hint()可以提高操作效&lt;br /&gt;率。Hint 可以强制要求查询操作使用某个索引。例如，如果要查询多个字段的值，如果在其&lt;br /&gt;中一个字段上有索引，可以使用hint：&lt;br /&gt;db.collection.find({user:u, foo:d}).hint({user:1});&lt;br /&gt;19.7 优化方案7: 采用Profiling&lt;br /&gt;Profiling 功能肯定是会影响效率的，但是不太严重，原因是他使用的是system.profile 来记&lt;br /&gt;录，而system.profile 是一个capped collection 这种collection 在操作上有一些限制和特点，&lt;br /&gt;&lt;br /&gt;58 / 91&lt;br /&gt;但是效率更高。&lt;br /&gt;第二十章 性能监控&lt;br /&gt;20.1 mongosniff&lt;br /&gt;此工具可以从底层监控到底有哪些命令发送给了MongoDB 去执行，从中就可以进行分析：&lt;br /&gt;以root 身份执行：&lt;br /&gt;./mongosniff --source NET lo&lt;br /&gt;然后其会监控位到本地以localhost 监听默认27017 端口的MongoDB 的所有包请求，如执&lt;br /&gt;行&amp;#8221;show dbs&amp;#8221; 操作&lt;br /&gt;[root@localhost bin]# ./mongo&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: test&lt;br /&gt;&amp;gt; show dbs&lt;br /&gt;admin 0.0625GB&lt;br /&gt;foo 0.0625GB&lt;br /&gt;local (empty)&lt;br /&gt;test 0.0625GB&lt;br /&gt;&amp;gt;&lt;br /&gt;那么你可以看到如下输出。&lt;br /&gt;[root@localhost bin]# ./mongosniff --source NET lo&lt;br /&gt;sniffing... 27017&lt;br /&gt;127.0.0.1:38500 --&amp;gt;&amp;gt; 127.0.0.1:27017 admin.$cmd 60 bytes id:537ebe0f 1400815119&lt;br /&gt;query: { whatsmyuri: 1 } ntoreturn: 1 ntoskip: 0&lt;br /&gt;127.0.0.1:27017 &amp;lt;&amp;lt;-- 127.0.0.1:38500 78 bytes id:531c3855 1394358357 - 1400815119&lt;br /&gt;reply n:1 cursorId: 0&lt;br /&gt;{ you: "127.0.0.1:38500", ok: 1.0 }&lt;br /&gt;127.0.0.1:38500 --&amp;gt;&amp;gt; 127.0.0.1:27017 admin.$cmd 80 bytes id:537ebe10 1400815120&lt;br /&gt;query: { replSetGetStatus: 1, forShell: 1 } ntoreturn: 1 ntoskip: 0&lt;br /&gt;127.0.0.1:27017 &amp;lt;&amp;lt;-- 127.0.0.1:38500 92 bytes id:531c3856 1394358358 - 1400815120&lt;br /&gt;reply n:1 cursorId: 0&lt;br /&gt;{ errmsg: "not running with --replSet", ok: 0.0 }&lt;br /&gt;127.0.0.1:38500 --&amp;gt;&amp;gt; 127.0.0.1:27017 admin.$cmd 67 bytes id:537ebe11 1400815121&lt;br /&gt;query: { listDatabases: 1.0 } ntoreturn: -1 ntoskip: 0&lt;br /&gt;127.0.0.1:27017 &amp;lt;&amp;lt;-- 127.0.0.1:38500 293 bytes id:531c3857 1394358359 - 1400815121&lt;br /&gt;reply n:1 cursorId: 0&lt;br /&gt;{ databases: [ { name: "foo", sizeOnDisk: 67108864.0, empty: false }, { name: "test",&lt;br /&gt;sizeOnDisk: 67108864.0, empty: false }, { name: "admin", sizeOnDisk: 67108864.0, empty: false },&lt;br /&gt;{ name: "local", sizeOnDisk: 1.0, empty: true } ], totalSize: 201326592.0, ok: 1.0 }&lt;br /&gt;127.0.0.1:38500 --&amp;gt;&amp;gt; 127.0.0.1:27017 admin.$cmd 80 bytes id:537ebe12 1400815122&lt;br /&gt;&lt;br /&gt;59 / 91&lt;br /&gt;query: { replSetGetStatus: 1, forShell: 1 } ntoreturn: 1 ntoskip: 0&lt;br /&gt;127.0.0.1:27017 &amp;lt;&amp;lt;-- 127.0.0.1:38500 92 bytes id:531c3858 1394358360 - 1400815122&lt;br /&gt;reply n:1 cursorId: 0&lt;br /&gt;{ errmsg: "not running with --replSet", ok: 0.0 }&lt;br /&gt;如果将这些输出到一个日志文件中，那么就可以保留下所有数据库操作的历史记录，对于后&lt;br /&gt;期的性能分析和安全审计等工作将是一个巨大的贡献。&lt;br /&gt;20.2 Mongostat&lt;br /&gt;此工具可以快速的查看某组运行中的MongoDB 实例的统计信息，用法如下：&lt;br /&gt;[root@localhost bin]# ./mongostat&lt;br /&gt;下面是执行结果(部分):&lt;br /&gt;[root@localhost bin]# ./mongostat&lt;br /&gt;insert query update delete ...... locked % idx miss % qr|qw ar|aw conn time&lt;br /&gt;*0 *0 *0 *0 ...... 0 0 0|0 1|0 4 01:19:15&lt;br /&gt;*0 *0 *0 *0 ...... 0 0 0|0 1|0 4 01:19:16&lt;br /&gt;*0 *0 *0 *0 ...... 0 0 0|0 1|0 4 01:19:17&lt;br /&gt;字段说明:&lt;br /&gt;&#x1; insert: 每秒插入量&lt;br /&gt;&#x1; query: 每秒查询量&lt;br /&gt;&#x1; update: 每秒更新量&lt;br /&gt;&#x1; delete: 每秒删除量&lt;br /&gt;&#x1; locked: 锁定量&lt;br /&gt;&#x1; qr | qw: 客户端查询排队长度(读|写)&lt;br /&gt;&#x1; ar | aw: 活跃客户端量(读|写)&lt;br /&gt;&#x1; conn: 连接数&lt;br /&gt;&#x1; time: 当前时间&lt;br /&gt;它每秒钟刷新一次状态值，提供良好的可读性，通过这些参数可以观察到一个整体的性能情&lt;br /&gt;况。&lt;br /&gt;20.3 db.serverStatus&lt;br /&gt;这个命令是最常用也是最基础的查看实例运行状态的命令之一，下面我们看一下它的输出:&lt;br /&gt;&amp;gt; db.serverStatus()&lt;br /&gt;{&lt;br /&gt;"host" : "localhost.localdomain",&lt;br /&gt;"version" : "1.8.1", --服务器版本&lt;br /&gt;"process" : "mongod",&lt;br /&gt;"uptime" : 3184, --启动时间(秒)&lt;br /&gt;"uptimeEstimate" : 3174,&lt;br /&gt;"localTime" : ISODate("2012-05-28T11:20:22.819Z"),&lt;br /&gt;&lt;br /&gt;60 / 91&lt;br /&gt;"globalLock" : {&lt;br /&gt;"totalTime" : 3183918151,&lt;br /&gt;"lockTime" : 10979,&lt;br /&gt;"ratio" : 0.000003448267034299149,&lt;br /&gt;"currentQueue" : {&lt;br /&gt;"total" : 0, --当前全部队列量&lt;br /&gt;"readers" : 0, --读请求队列量&lt;br /&gt;"writers" : 0 --写请求队列量&lt;br /&gt;},&lt;br /&gt;"activeClients" : {&lt;br /&gt;"total" : 0, --当前全部客户端连接量&lt;br /&gt;"readers" : 0, --客户端读请求量&lt;br /&gt;"writers" : 0 --客户端写请求量&lt;br /&gt;}&lt;br /&gt;},&lt;br /&gt;"mem" : {&lt;br /&gt;"bits" : 32, --32 位系统&lt;br /&gt;"resident" : 20, --占用物量内存量&lt;br /&gt;"virtual" : 126, --虚拟内存量&lt;br /&gt;"supported" : true, --是否支持扩展内存&lt;br /&gt;"mapped" : 32&lt;br /&gt;},&lt;br /&gt;"connections" : {&lt;br /&gt;"current" : 1, --当前活动连接量&lt;br /&gt;"available" : 818 --剩余空闲连接量&lt;br /&gt;},&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;"indexCounters" : {&lt;br /&gt;"btree" : {&lt;br /&gt;"accesses" : 0, --索引被访问量&lt;br /&gt;"hits" : 0, --索引命中量&lt;br /&gt;"misses" : 0, --索引偏差量&lt;br /&gt;"resets" : 0,&lt;br /&gt;"missRatio" : 0 --索引偏差率(未命中率)&lt;br /&gt;}&lt;br /&gt;},&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;"network" : {&lt;br /&gt;"bytesIn" : 1953, --发给此服务器的数据量(单位:byte)&lt;br /&gt;"bytesOut" : 25744, --此服务器发出的数据量(单位:byte)&lt;br /&gt;"numRequests" : 30 --发给此服务器的请求量&lt;br /&gt;},&lt;br /&gt;"opcounters" : {&lt;br /&gt;"insert" : 0, --插入操作的量&lt;br /&gt;&lt;br /&gt;61 / 91&lt;br /&gt;"query" : 1, --查询操作的量&lt;br /&gt;"update" : 0, --更新操作的量&lt;br /&gt;"delete" : 0, --删除操作的量&lt;br /&gt;"getmore" : 0,&lt;br /&gt;"command" : 31 --其它操作的量&lt;br /&gt;},&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;此工具提了比较详细的信息，以上已经对主要的一些参数做了说明，请大家参考。、&lt;br /&gt;20.4 db.stats&lt;br /&gt;db.stats 查看数据库状态信息。使用样例如下:&lt;br /&gt;&amp;gt; db.stats()&lt;br /&gt;{&lt;br /&gt;"db" : "test",&lt;br /&gt;"collections" : 7, --collection 数量&lt;br /&gt;"objects" : 28, --对象数量&lt;br /&gt;"avgObjSize" : 50.57142857142857, --对象平均大小&lt;br /&gt;"dataSize" : 1416, --数据大小&lt;br /&gt;"storageSize" : 31744, --数据大小(含预分配空间)&lt;br /&gt;"numExtents" : 7, --事件数量&lt;br /&gt;"indexes" : 7, --索引数量&lt;br /&gt;"indexSize" : 57344, --索引大小&lt;br /&gt;"fileSize" : 50331648, --文件大小&lt;br /&gt;"ok" : 1 --本次取stats 是否正常&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;通过这个工具，可以查看所在数据库的基本信息&lt;br /&gt;20.5 第三方工具&lt;br /&gt;MongoDB 从一面世就得到众多开源爱好者和团队的重视，在常用的监控框架如 cacti、&lt;br /&gt;Nagios、Zabbix 等基础上进行扩展，进行MongoDB 的监控都是非常方便的，有兴趣的朋友&lt;br /&gt;可以自已去多试试。&lt;br /&gt;&lt;br /&gt;62 / 91&lt;br /&gt;第五部分 架构篇&lt;br /&gt;第二十一章 Replica Sets复制集&lt;br /&gt;MongoDB 支持在多个机器中通过异步复制达到故障转移和实现冗余。多机器中同一时刻只&lt;br /&gt;有一台是用于写操作。正是由于这个情况，为MongoDB 提供了数据一致性的保障。担当&lt;br /&gt;Primary 角色的机器能把读操作分发给slave。&lt;br /&gt;MongoDB 高可用可用分两种:&lt;br /&gt;&#x1; Master-Slave 主从复制：&lt;br /&gt;只需要在某一个服务启动时加上&amp;#8211;master 参数，而另一个服务加上&amp;#8211;slave 与&amp;#8211;source 参数，&lt;br /&gt;即可实现同步。MongoDB 的最新版本已不再推荐此方案。&lt;br /&gt;&#x1; Replica Sets复制集：&lt;br /&gt;MongoDB 在 1.6 版本对开发了新功能replica set，这比之前的replication 功能要强大一&lt;br /&gt;些，增加了故障自动切换和自动修复成员节点，各个DB 之间数据完全一致，大大降低了维&lt;br /&gt;护成功。auto shard 已经明确说明不支持replication paris，建议使用replica set，replica set&lt;br /&gt;故障切换完全自动。&lt;br /&gt;如果上图所示，Replica Sets 的结构非常类似一个集群。是的，你完全可以把它当成集群，因&lt;br /&gt;为它确实跟集群实现的作用是一样的，其中一个节点如果出现故障，其它节点马上会将业务&lt;br /&gt;接过来而无须停机操作。&lt;br /&gt;21.1 部署Replica Sets&lt;br /&gt;接下来将一步一步的给大家分享一下实施步骤：&lt;br /&gt;&lt;br /&gt;63 / 91&lt;br /&gt;1、 创建数据文件存储路径&lt;br /&gt;[root@localhost ~]# mkdir -p /data/data/r0&lt;br /&gt;[root@localhost ~]# mkdir -p /data/data/r1&lt;br /&gt;[root@localhost ~]# mkdir -p /data/data/r2&lt;br /&gt;2、 创建日志文件路径&lt;br /&gt;[root@localhost ~]# mkdir -p /data/log&lt;br /&gt;3、创建主从key 文件，用于标识集群的私钥的完整路径，如果各个实例的key file 内容不一&lt;br /&gt;致，程序将不能正常用。&lt;br /&gt;[root@localhost ~]# mkdir -p /data/key&lt;br /&gt;[root@localhost ~]# echo "this is rs1 super secret key" &amp;gt; /data/key/r0&lt;br /&gt;[root@localhost ~]# echo "this is rs1 super secret key" &amp;gt; /data/key/r1&lt;br /&gt;[root@localhost ~]# echo "this is rs1 super secret key" &amp;gt; /data/key/r2&lt;br /&gt;[root@localhost ~]# chmod 600 /data/key/r*&lt;br /&gt;4、启动3 个实例&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r0 --fork --port&lt;br /&gt;28010 --dbpath /data/data/r0 --logpath=/data/log/r0.log --logappend&lt;br /&gt;all output going to: /data/log/r0.log&lt;br /&gt;forked process: 6573&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r1 --fork --port&lt;br /&gt;28011 --dbpath /data/data/r1 --logpath=/data/log/r1.log --logappend&lt;br /&gt;all output going to: /data/log/r1.log&lt;br /&gt;forked process: 6580&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r2 --fork --port&lt;br /&gt;28012 --dbpath /data/data/r2 --logpath=/data/log/r2.log --logappend&lt;br /&gt;all output going to: /data/log/r2.log&lt;br /&gt;forked process: 6585&lt;br /&gt;[root@localhost ~]#&lt;br /&gt;5、配置及初始化Replica Sets&lt;br /&gt;[root@localhost bin]# /Apps/mongo/bin/mongo -port 28010&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:28010/test&lt;br /&gt;&amp;gt; config_rs1 = {_id: 'rs1', members: [&lt;br /&gt;... {_id: 0, host: 'localhost:28010', priority:1}, --成员IP 及端口，priority=1 指PRIMARY&lt;br /&gt;... {_id: 1, host: 'localhost:28011'},&lt;br /&gt;... {_id: 2, host: 'localhost:28012'}]&lt;br /&gt;... }&lt;br /&gt;{&lt;br /&gt;"_id" : "rs1",&lt;br /&gt;"members" : [&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;64 / 91&lt;br /&gt;"_id" : 0,&lt;br /&gt;"host" : "localhost:28010"&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 1,&lt;br /&gt;"host" : "localhost:28011"&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 2,&lt;br /&gt;"host" : "localhost:28012"&lt;br /&gt;}&lt;br /&gt;]&lt;br /&gt;}&lt;br /&gt;&amp;gt; rs.initiate(config_rs1); --初始化配置&lt;br /&gt;{&lt;br /&gt;"info" : "Config now saved locally. Should come online in about a minute.",&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;6、查看复制集状态&lt;br /&gt;&amp;gt; rs.status()&lt;br /&gt;{&lt;br /&gt;"set" : "rs1",&lt;br /&gt;"date" : ISODate("2012-05-31T09:49:57Z"),&lt;br /&gt;"myState" : 1,&lt;br /&gt;"members" : [&lt;br /&gt;{&lt;br /&gt;"_id" : 0,&lt;br /&gt;"name" : "localhost:28010",&lt;br /&gt;"health" : 1, --1 表明正常; 0 表明异常&lt;br /&gt;"state" : 1, -- 1 表明是Primary; 2 表明是Secondary;&lt;br /&gt;"stateStr" : "PRIMARY", --表明此机器是主库&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338457763000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T09:49:23Z"),&lt;br /&gt;"self" : true&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 1,&lt;br /&gt;"name" : "localhost:28011",&lt;br /&gt;"health" : 1,&lt;br /&gt;&lt;br /&gt;65 / 91&lt;br /&gt;"state" : 2,&lt;br /&gt;"stateStr" : "SECONDARY",&lt;br /&gt;"uptime" : 23,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338457763000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T09:49:23Z"),&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T09:49:56Z")&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 2,&lt;br /&gt;"name" : "localhost:28012",&lt;br /&gt;"health" : 1,&lt;br /&gt;"state" : 2,&lt;br /&gt;"stateStr" : "SECONDARY",&lt;br /&gt;"uptime" : 23,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338457763000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T09:49:23Z"),&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T09:49:56Z")&lt;br /&gt;}&lt;br /&gt;],&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;rs1:PRIMARY&amp;gt;&lt;br /&gt;还可以用isMaster 查看Replica Sets 状态。&lt;br /&gt;rs1:PRIMARY&amp;gt; rs.isMaster()&lt;br /&gt;{&lt;br /&gt;"setName" : "rs1",&lt;br /&gt;"ismaster" : true,&lt;br /&gt;"secondary" : false,&lt;br /&gt;"hosts" : [&lt;br /&gt;"localhost:28010",&lt;br /&gt;"localhost:28012",&lt;br /&gt;"localhost:28011"&lt;br /&gt;],&lt;br /&gt;"maxBsonObjectSize" : 16777216,&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;rs1:PRIMARY&amp;gt;&lt;br /&gt;&lt;br /&gt;66 / 91&lt;br /&gt;21.2 主从操作日志oplog&lt;br /&gt;MongoDB 的Replica Set 架构是通过一个日志来存储写操作的，这个日志就叫做&amp;#8221;oplog&amp;#8221;。&lt;br /&gt;oplog.rs 是一个固定长度的 capped collection，它存在于&amp;#8221;local&amp;#8221;数据库中，用于记录 Replica&lt;br /&gt;Sets 操作日志。在默认情况下,对于64 位的MongoDB,oplog 是比较大的，可以达到5%的磁&lt;br /&gt;盘空间。oplog 的大小是可以通过mongod 的参数&amp;#8221;&amp;#8212;oplogSize&amp;#8221;来改变oplog 的日志大小。&lt;br /&gt;Oplog 内容样例:&lt;br /&gt;rs1:PRIMARY&amp;gt; use local&lt;br /&gt;switched to db local&lt;br /&gt;rs1:PRIMARY&amp;gt; show collections&lt;br /&gt;oplog.rs&lt;br /&gt;system.replset&lt;br /&gt;rs1:PRIMARY&amp;gt; db.oplog.rs.find()&lt;br /&gt;{ "ts" : { "t" : 1338457763000, "i" : 1 }, "h" : NumberLong(0), "op" : "n", "ns" : "", "o" : { "msg" :&lt;br /&gt;"initiating set" } }&lt;br /&gt;{ "ts" : { "t" : 1338459114000, "i" : 1 }, "h" : NumberLong("5493127699725549585"), "op" : "i",&lt;br /&gt;"ns" : "test.c1", "o" : { "_id" : ObjectId("4fc743e9aea289af709ac6b5"), "age" : 29, "name" :&lt;br /&gt;"Tony" } }&lt;br /&gt;rs1:PRIMARY&amp;gt;&lt;br /&gt;字段说明:&lt;br /&gt;&#x1; ts: 某个操作的时间戳&lt;br /&gt;&#x1; op: 操作类型，如下：&lt;br /&gt; i: insert&lt;br /&gt; d: delete&lt;br /&gt; u: update&lt;br /&gt;&#x1; ns: 命名空间，也就是操作的collection name&lt;br /&gt;&#x1; o: document 的内容&lt;br /&gt;查看master 的oplog 元数据信息：&lt;br /&gt;rs1:PRIMARY&amp;gt; db.printReplicationInfo()&lt;br /&gt;configured oplog size: 47.6837158203125MB&lt;br /&gt;log length start to end: 1351secs (0.38hrs)&lt;br /&gt;oplog first event time: Thu May 31 2012 17:49:23 GMT+0800 (CST)&lt;br /&gt;oplog last event time: Thu May 31 2012 18:11:54 GMT+0800 (CST)&lt;br /&gt;now: Thu May 31 2012 18:21:58 GMT+0800 (CST)&lt;br /&gt;rs1:PRIMARY&amp;gt;&lt;br /&gt;字段说明:&lt;br /&gt;&#x1; configured oplog size: 配置的oplog 文件大小&lt;br /&gt;&#x1; log length start to end: oplog 日志的启用时间段&lt;br /&gt;&#x1; oplog first event time: 第一个事务日志的产生时间&lt;br /&gt;&#x1; oplog last event time: 最后一个事务日志的产生时间&lt;br /&gt;&lt;br /&gt;67 / 91&lt;br /&gt;&#x1; now: 现在的时间&lt;br /&gt;查看slave 的同步状态：&lt;br /&gt;rs1:PRIMARY&amp;gt; db.printSlaveReplicationInfo()&lt;br /&gt;source: localhost:28011&lt;br /&gt;syncedTo: Thu May 31 2012 18:11:54 GMT+0800 (CST)&lt;br /&gt;= 884secs ago (0.25hrs)&lt;br /&gt;source: localhost:28012&lt;br /&gt;syncedTo: Thu May 31 2012 18:11:54 GMT+0800 (CST)&lt;br /&gt;= 884secs ago (0.25hrs)&lt;br /&gt;rs1:PRIMARY&amp;gt;&lt;br /&gt;字段说明:&lt;br /&gt;&#x1; source: 从库的IP 及端口&lt;br /&gt;&#x1; syncedTo: 目前的同步情况，延迟了多久等信息&lt;br /&gt;21.3 主从配置信息&lt;br /&gt;在local 库中不仅有主从日志oplog 集合，还有一个集合用于记录主从配置信息 &amp;#8211;&lt;br /&gt;system.replset&lt;br /&gt;rs1:PRIMARY&amp;gt; use local&lt;br /&gt;switched to db local&lt;br /&gt;rs1:PRIMARY&amp;gt; show collections&lt;br /&gt;oplog.rs&lt;br /&gt;system.replset&lt;br /&gt;rs1:PRIMARY&amp;gt; db.system.replset.find()&lt;br /&gt;{ "_id" : "rs1", "version" : 1, "members" : [&lt;br /&gt;{&lt;br /&gt;"_id" : 0,&lt;br /&gt;"host" : "localhost:28010"&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 1,&lt;br /&gt;"host" : "localhost:28011"&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 2,&lt;br /&gt;"host" : "localhost:28012"&lt;br /&gt;}&lt;br /&gt;] }&lt;br /&gt;rs1:PRIMARY&amp;gt;&lt;br /&gt;从这个集合中可以看出，Replica Sets 的配置信息，也可以在任何一个成员实例上执行rs.conf()&lt;br /&gt;来查看配置信息&lt;br /&gt;&lt;br /&gt;68 / 91&lt;br /&gt;21.4 管理维护Replica Sets&lt;br /&gt;21.4.1 读写分离&lt;br /&gt;有一些第三方的工具，提供了一些可以让数据库进行读写分离的工具。我们现在是否有一个&lt;br /&gt;疑问，从库要是能进行查询就更好了，这样可以分担主库的大量的查询请求。&lt;br /&gt;1、 先向主库中插入一条测试数据&lt;br /&gt;[root@localhost bin]# ./mongo --port 28010&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:28010/test&lt;br /&gt;rs1:PRIMARY&amp;gt; db.c1.insert({age:30})&lt;br /&gt;db.c2rs1:PRIMARY&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fc77f421137ea4fdb653b4a"), "age" : 30 }&lt;br /&gt;2、 在从库进行查询等操作&lt;br /&gt;[root@localhost bin]# ./mongo --port 28011&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:28011/test&lt;br /&gt;rs1:SECONDARY&amp;gt; show collections&lt;br /&gt;Thu May 31 22:27:17 uncaught exception: error: { "$err" : "not master and slaveok=false",&lt;br /&gt;"code" : 13435 }&lt;br /&gt;rs1:SECONDARY&amp;gt;&lt;br /&gt;当查询时报错了，说明是个从库且不能执行查询的操作&lt;br /&gt;3、 让从库可以读，分担主库的压力&lt;br /&gt;rs1:SECONDARY&amp;gt; db.getMongo().setSlaveOk()&lt;br /&gt;not master and slaveok=false&lt;br /&gt;rs1:SECONDARY&amp;gt; show collections&lt;br /&gt;c1&lt;br /&gt;system.indexes&lt;br /&gt;rs1:SECONDARY&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fc77f421137ea4fdb653b4a"), "age" : 30 }&lt;br /&gt;rs1:SECONDARY&amp;gt;&lt;br /&gt;看来我们要是执行db.getMongo().setSlaveOk()， 我们就可查询从库了。&lt;br /&gt;21.4.2 故障转移&lt;br /&gt;复制集比传统的Master-Slave 有改进的地方就是他可以进行故障的自动转移，如果我们停掉&lt;br /&gt;复制集中的一个成员，那么剩余成员会再自动选举出一个成员，做为主库，例如:&lt;br /&gt;我们将28010 这个主库停掉，然后再看一下复制集的状态&lt;br /&gt;&lt;br /&gt;69 / 91&lt;br /&gt;1、杀掉28010 端口的MongoDB&lt;br /&gt;[root@localhost bin]# ps aux|grep mongod&lt;br /&gt;root 6706 1.6 6.9 463304 6168 Sl 21:49 0:26&lt;br /&gt;/Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r0 --fork --port 28010&lt;br /&gt;root 6733 0.4 6.7 430528 6044 ? Sl 21:50 0:06&lt;br /&gt;/Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r1 --fork --port 28011&lt;br /&gt;root 6747 0.4 4.7 431548 4260 ? Sl 21:50 0:06&lt;br /&gt;/Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r2 --fork --port 28012&lt;br /&gt;root 7019 0.0 0.7 5064 684 pts/2 S+ 22:16 0:00 grep mongod&lt;br /&gt;[root@localhost bin]# kill -9 6706&lt;br /&gt;2、 查看复制集状态&lt;br /&gt;[root@localhost bin]# ./mongo --port 28011&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:28011/test&lt;br /&gt;rs1:SECONDARY&amp;gt; rs.status()&lt;br /&gt;{&lt;br /&gt;"set" : "rs1",&lt;br /&gt;"date" : ISODate("2012-05-31T14:17:03Z"),&lt;br /&gt;"myState" : 2,&lt;br /&gt;"members" : [&lt;br /&gt;{&lt;br /&gt;"_id" : 0,&lt;br /&gt;"name" : "localhost:28010",&lt;br /&gt;"health" : 0,&lt;br /&gt;"state" : 1,&lt;br /&gt;"stateStr" : "(not reachable/healthy)",&lt;br /&gt;"uptime" : 0,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338472279000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T13:51:19Z"),&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T14:16:42Z"),&lt;br /&gt;"errmsg" : "socket exception"&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 1,&lt;br /&gt;"name" : "localhost:28011",&lt;br /&gt;"health" : 1,&lt;br /&gt;"state" : 2,&lt;br /&gt;"stateStr" : "SECONDARY",&lt;br /&gt;"optime" : {&lt;br /&gt;&lt;br /&gt;70 / 91&lt;br /&gt;"t" : 1338472279000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T13:51:19Z"),&lt;br /&gt;"self" : true&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 2,&lt;br /&gt;"name" : "localhost:28012",&lt;br /&gt;"health" : 1,&lt;br /&gt;"state" : 1,&lt;br /&gt;"stateStr" : "PRIMARY",&lt;br /&gt;"uptime" : 1528,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338472279000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T13:51:19Z"),&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T14:17:02Z")&lt;br /&gt;}&lt;br /&gt;],&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;rs1:SECONDARY&amp;gt;&lt;br /&gt;可以看到28010 这个端口的MongoDB 出现了异常，而系统自动选举了28012 这个端口为主，&lt;br /&gt;所以这样的故障处理机制，能将系统的稳定性大大提高。&lt;br /&gt;21.4.3 增减节点&lt;br /&gt;MongoDB Replica Sets 不仅提供高可用性的解决方案，它也同时提供负载均衡的解决方案，&lt;br /&gt;增减Replica Sets 节点在实际应用中非常普遍，例如当应用的读压力暴增时，3 台节点的环&lt;br /&gt;境已不能满足需求，那么就需要增加一些节点将压力平均分配一下；当应用的压力小时，可&lt;br /&gt;以减少一些节点来减少硬件资源的成本；总之这是一个长期且持续的工作。&lt;br /&gt;21.4.3.1 增加节点&lt;br /&gt;官方给我们提了2 个方案用于增加节点，一种是通过oplog 来增加节点，一种是通过数据库&lt;br /&gt;快照(--fastsync)和oplog 来增加节点，下面将分别介绍。&lt;br /&gt;21.4.3.1.1 通过oplog 增加节点&lt;br /&gt;&amp;#9312;、配置并启动新节点，启用28013这个端口给新的节点&lt;br /&gt;[root@localhost ~]# mkdir -p /data/data/r3&lt;br /&gt;&lt;br /&gt;71 / 91&lt;br /&gt;[root@localhost ~]# echo "this is rs1 super secret key" &amp;gt; /data/key/r3&lt;br /&gt;[root@localhost ~]# chmod 600 /data/key/r3&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r3 --fork --port&lt;br /&gt;28013 --dbpath /data/data/r3 --logpath=/data/log/r3.log --logappend&lt;br /&gt;all output going to: /data/log/r3.log&lt;br /&gt;forked process: 10553&lt;br /&gt;[root@localhost ~]#&lt;br /&gt;&amp;#9313;、添加此新节点到现有的Replica Sets&lt;br /&gt;rs1:PRIMARY&amp;gt; rs.add("localhost:28013")&lt;br /&gt;{ "ok" : 1 }&lt;br /&gt;&amp;#9314;、查看Replica Sets我们可以清晰的看到内部是如何添加28013这个新节点的.&lt;br /&gt;步骤一: 进行初始化&lt;br /&gt;rs1: PRIMARY &amp;gt; rs.status()&lt;br /&gt;{&lt;br /&gt;"set" : "rs1",&lt;br /&gt;"date" : ISODate("2012-05-31T12:17:44Z"),&lt;br /&gt;"myState" : 1,&lt;br /&gt;"members" : [&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;{&lt;br /&gt;"_id" : 3,&lt;br /&gt;"name" : "localhost:28013",&lt;br /&gt;"health" : 0,&lt;br /&gt;"state" : 6,&lt;br /&gt;"stateStr" : "(not reachable/healthy)",&lt;br /&gt;"uptime" : 0,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 0,&lt;br /&gt;"i" : 0&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("1970-01-01T00:00:00Z"),&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T12:17:43Z"),&lt;br /&gt;"errmsg" : "still initializing"&lt;br /&gt;}&lt;br /&gt;],&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;步骤二: 进行数据同步&lt;br /&gt;rs1:PRIMARY&amp;gt; rs.status()&lt;br /&gt;{&lt;br /&gt;"set" : "rs1",&lt;br /&gt;&lt;br /&gt;72 / 91&lt;br /&gt;"date" : ISODate("2012-05-31T12:18:07Z"),&lt;br /&gt;"myState" : 1,&lt;br /&gt;"members" : [&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;{&lt;br /&gt;"_id" : 3,&lt;br /&gt;"name" : "localhost:28013",&lt;br /&gt;"health" : 1,&lt;br /&gt;"state" : 3,&lt;br /&gt;"stateStr" : "RECOVERING",&lt;br /&gt;"uptime" : 16,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 0,&lt;br /&gt;"i" : 0&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("1970-01-01T00:00:00Z"),&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T12:18:05Z"),&lt;br /&gt;"errmsg" : "initial sync need a member to be primary or secondary&lt;br /&gt;to do our initial sync"&lt;br /&gt;}&lt;br /&gt;],&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;步骤三: 初始化同步完成&lt;br /&gt;rs1:PRIMARY&amp;gt; rs.status()&lt;br /&gt;{&lt;br /&gt;"set" : "rs1",&lt;br /&gt;"date" : ISODate("2012-05-31T12:18:08Z"),&lt;br /&gt;"myState" : 1,&lt;br /&gt;"members" : [&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;{&lt;br /&gt;"_id" : 3,&lt;br /&gt;"name" : "localhost:28013",&lt;br /&gt;"health" : 1,&lt;br /&gt;"state" : 3,&lt;br /&gt;"stateStr" : "RECOVERING",&lt;br /&gt;"uptime" : 17,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338466661000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T12:17:41Z"),&lt;br /&gt;&lt;br /&gt;73 / 91&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T12:18:07Z"),&lt;br /&gt;"errmsg" : "initial sync done"&lt;br /&gt;}&lt;br /&gt;],&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;步骤四: 节点添加完成，状态正常&lt;br /&gt;rs1:PRIMARY&amp;gt; rs.status()&lt;br /&gt;{&lt;br /&gt;"set" : "rs1",&lt;br /&gt;"date" : ISODate("2012-05-31T12:18:10Z"),&lt;br /&gt;"myState" : 1,&lt;br /&gt;"members" : [&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;{&lt;br /&gt;"_id" : 3,&lt;br /&gt;"name" : "localhost:28013",&lt;br /&gt;"health" : 1,&lt;br /&gt;"state" : 2,&lt;br /&gt;"stateStr" : "SECONDARY",&lt;br /&gt;"uptime" : 19,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338466661000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T12:17:41Z"),&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T12:18:09Z")&lt;br /&gt;}&lt;br /&gt;],&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;#9315;、验证数据已经同步过来了&lt;br /&gt;[root@localhost data]# /Apps/mongo/bin/mongo -port 28013&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:28013/test&lt;br /&gt;rs1:SECONDARY&amp;gt; rs.slaveOk()&lt;br /&gt;rs1:SECONDARY&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fc760d2383ede1dce14ef86"), "age" : 10 }&lt;br /&gt;rs1:SECONDARY&amp;gt;&lt;br /&gt;21.4.3.1.2 通过数据库快照(--fastsync)和oplog 增加节点&lt;br /&gt;通过oplog 直接进行增加节点操作简单且无需人工干预过多，但oplog 是capped collection，&lt;br /&gt;&lt;br /&gt;74 / 91&lt;br /&gt;采用循环的方式进行日志处理，所以采用oplog 的方式进行增加节点，有可能导致数据的不&lt;br /&gt;一致，因为日志中存储的信息有可能已经刷新过了。不过没关系，我们可以通过数据库快照&lt;br /&gt;(--fastsync)和oplog 结合的方式来增加节点，这种方式的操作流程是，先取某一个复制集成&lt;br /&gt;员的物理文件来做为初始化数据，然后剩余的部分用oplog 日志来追，最终达到数据一致性&lt;br /&gt;&amp;#9312;、取某一个复制集成员的物理文件来做为初始化数据&lt;br /&gt;[root@localhost ~]# scp -r /data/data/r3 /data/data/r4&lt;br /&gt;[root@localhost ~]# echo "this is rs1 super secret key" &amp;gt; /data/key/r4&lt;br /&gt;[root@localhost ~]# chmod 600 /data/key/r4&lt;br /&gt;&amp;#9313;、在取完物理文件后，在c1集中插入一条新文档，用于最后验证此更新也同步了&lt;br /&gt;rs1:PRIMARY&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fc760d2383ede1dce14ef86"), "age" : 10 }&lt;br /&gt;rs1:PRIMARY&amp;gt; db.c1.insert({age:20})&lt;br /&gt;rs1:PRIMARY&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fc760d2383ede1dce14ef86"), "age" : 10 }&lt;br /&gt;{ "_id" : ObjectId("4fc7748f479e007bde6644ef"), "age" : 20 }&lt;br /&gt;rs1:PRIMARY&amp;gt;&lt;br /&gt;&amp;#9314;、启用28014这个端口给新的节点&lt;br /&gt;/Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r4 --fork --port 28014 --dbpath&lt;br /&gt;/data/data/r4 --logpath=/data/log/r4.log --logappend --fastsync&lt;br /&gt;&amp;#9315;、添加28014节点&lt;br /&gt;rs1:PRIMARY&amp;gt; rs.add("localhost:28014")&lt;br /&gt;{ "ok" : 1 }&lt;br /&gt;&amp;#9316;、验证数据已经同步过来了&lt;br /&gt;[root@localhost data]# /Apps/mongo/bin/mongo -port 28014&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:28014/test&lt;br /&gt;rs1:SECONDARY&amp;gt; rs.slaveOk()&lt;br /&gt;rs1:SECONDARY&amp;gt; db.c1.find()&lt;br /&gt;{ "_id" : ObjectId("4fc760d2383ede1dce14ef86"), "age" : 10 }&lt;br /&gt;{ "_id" : ObjectId("4fc7748f479e007bde6644ef"), "age" : 20 }&lt;br /&gt;rs1:SECONDARY&amp;gt;&lt;br /&gt;21.4.3.2 减少节点&lt;br /&gt;下面将刚刚添加的两个新节点28013 和28014 从复制集中去除掉，只需执行rs.remove 指令&lt;br /&gt;就可以了，具体如下:&lt;br /&gt;rs1:PRIMARY&amp;gt; rs.remove("localhost:28014")&lt;br /&gt;&lt;br /&gt;75 / 91&lt;br /&gt;{ "ok" : 1 }&lt;br /&gt;rs1:PRIMARY&amp;gt; rs.remove("localhost:28013")&lt;br /&gt;{ "ok" : 1 }&lt;br /&gt;查看复制集状态，可以看到现在只有28010、28011、28012 这三个成员，原来的28013 和&lt;br /&gt;28014 都成功去除了&lt;br /&gt;rs1:PRIMARY&amp;gt; rs.status()&lt;br /&gt;{&lt;br /&gt;"set" : "rs1",&lt;br /&gt;"date" : ISODate("2012-05-31T14:08:29Z"),&lt;br /&gt;"myState" : 1,&lt;br /&gt;"members" : [&lt;br /&gt;{&lt;br /&gt;"_id" : 0,&lt;br /&gt;"name" : "localhost:28010",&lt;br /&gt;"health" : 1,&lt;br /&gt;"state" : 1,&lt;br /&gt;"stateStr" : "PRIMARY",&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338473273000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T14:07:53Z"),&lt;br /&gt;"self" : true&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 1,&lt;br /&gt;"name" : "localhost:28011",&lt;br /&gt;"health" : 1,&lt;br /&gt;"state" : 2,&lt;br /&gt;"stateStr" : "SECONDARY",&lt;br /&gt;"uptime" : 34,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338473273000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T14:07:53Z"),&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T14:08:29Z")&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : 2,&lt;br /&gt;"name" : "localhost:28012",&lt;br /&gt;"health" : 1,&lt;br /&gt;"state" : 2,&lt;br /&gt;&lt;br /&gt;76 / 91&lt;br /&gt;"stateStr" : "SECONDARY",&lt;br /&gt;"uptime" : 34,&lt;br /&gt;"optime" : {&lt;br /&gt;"t" : 1338473273000,&lt;br /&gt;"i" : 1&lt;br /&gt;},&lt;br /&gt;"optimeDate" : ISODate("2012-05-31T14:07:53Z"),&lt;br /&gt;"lastHeartbeat" : ISODate("2012-05-31T14:08:29Z")&lt;br /&gt;}&lt;br /&gt;],&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;rs1:PRIMARY&amp;gt;&lt;br /&gt;第二十二章 Sharding分片&lt;br /&gt;这是一种将海量的数据水平扩展的数据库集群系统，数据分表存储在sharding 的各个节点&lt;br /&gt;上，使用者通过简单的配置就可以很方便地构建一个分布式MongoDB 集群。&lt;br /&gt;MongoDB 的数据分块称为 chunk。每个 chunk 都是 Collection 中一段连续的数据记录，通&lt;br /&gt;常最大尺寸是 200MB，超出则生成新的数据块。&lt;br /&gt;要构建一个 MongoDB Sharding Cluster，需要三种角色：&lt;br /&gt;&#x1; Shard Server&lt;br /&gt;即存储实际数据的分片，每个Shard 可以是一个mongod 实例，也可以是一组mongod 实例&lt;br /&gt;构成的Replica Set。为了实现每个Shard 内部的auto-failover，MongoDB 官方建议每个Shard&lt;br /&gt;为一组Replica Set。&lt;br /&gt;&#x1; Config Server&lt;br /&gt;为了将一个特定的collection 存储在多个shard 中，需要为该collection 指定一个shard key，&lt;br /&gt;例如{age: 1} ，shard key 可以决定该条记录属于哪个chunk。Config Servers 就是用来存储：&lt;br /&gt;所有shard 节点的配置信息、每个chunk 的shard key 范围、chunk 在各shard 的分布情况、&lt;br /&gt;该集群中所有DB 和collection 的sharding 配置信息。&lt;br /&gt;&#x1; Route Process&lt;br /&gt;这是一个前端路由，客户端由此接入，然后询问Config Servers 需要到哪个Shard 上查询或&lt;br /&gt;保存记录，再连接相应的Shard 进行操作，最后将结果返回给客户端。客户端只需要将原本&lt;br /&gt;发给mongod 的查询或更新请求原封不动地发给Routing Process，而不必关心所操作的记录&lt;br /&gt;存储在哪个Shard 上。&lt;br /&gt;下面我们在同一台物理机器上构建一个简单的 Sharding Cluster：&lt;br /&gt;架构图如下：&lt;br /&gt;&lt;br /&gt;77 / 91&lt;br /&gt;&#x1; Shard Server 1：20000&lt;br /&gt;&#x1; Shard Server 2：20001&lt;br /&gt;&#x1; Config Server ：30000&lt;br /&gt;&#x1; Route Process：40000&lt;br /&gt;22.1 启动Shard Server&lt;br /&gt;mkdir -p /data/shard/s0 --创建数据目录&lt;br /&gt;mkdir -p /data/shard/s1&lt;br /&gt;mkdir -p /data/shard/log --创建日志目录&lt;br /&gt;/Apps/mongo/bin/mongod --shardsvr --port 20000 --dbpath /data/shard/s0 --fork --logpath&lt;br /&gt;/data/shard/log/s0.log --directoryperdb --启动Shard Server 实例1&lt;br /&gt;/Apps/mongo/bin/mongod --shardsvr --port 20001 --dbpath /data/shard/s1 --fork --logpath&lt;br /&gt;/data/shard/log/s1.log --directoryperdb --启动Shard Server 实例2&lt;br /&gt;22.2 启动Config Server&lt;br /&gt;mkdir -p /data/shard/config --创建数据目录&lt;br /&gt;/Apps/mongo/bin/mongod --configsvr --port 30000 --dbpath /data/shard/config --fork --logpath&lt;br /&gt;/data/shard/log/config.log --directoryperdb --启动Config Server 实例&lt;br /&gt;22.3 启动Route Process&lt;br /&gt;/Apps/mongo/bin/mongos --port 40000 --configdb localhost:30000 --fork --logpath&lt;br /&gt;/data/shard/log/route.log --chunkSize 1 --启动Route Server 实例&lt;br /&gt;&lt;br /&gt;78 / 91&lt;br /&gt;mongos 启动参数中，chunkSize 这一项是用来指定chunk 的大小的，单位是MB，默认大小&lt;br /&gt;为200MB，为了方便测试Sharding 效果，我们把chunkSize 指定为 1MB。&lt;br /&gt;22.4 配置Sharding&lt;br /&gt;接下来，我们使用MongoDB Shell 登录到mongos，添加Shard 节点&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongo admin --port 40000 --此操作需要连接admin 库&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:40000/admin&lt;br /&gt;&amp;gt; db.runCommand({ addshard:"localhost:20000" }) --添加 Shard Server&lt;br /&gt;{ "shardAdded" : "shard0000", "ok" : 1 }&lt;br /&gt;&amp;gt; db.runCommand({ addshard:"localhost:20001" })&lt;br /&gt;{ "shardAdded" : "shard0001", "ok" : 1 }&lt;br /&gt;&amp;gt; db.runCommand({ enablesharding:"test" }) --设置分片存储的数据库&lt;br /&gt;{ "ok" : 1 }&lt;br /&gt;&amp;gt; db.runCommand({ shardcollection: "test.users", key: { _id:1 }}) --设置分片的集合名称，且必&lt;br /&gt;须指定Shard Key，系统会自动创建索引&lt;br /&gt;{ "collectionsharded" : "test.users", "ok" : 1 }&lt;br /&gt;&amp;gt;&lt;br /&gt;22.5 验证Sharding正常工作&lt;br /&gt;我们已经对test.users 表进行了分片的设置，下面我们们插入一些数据看一下结果&lt;br /&gt;&amp;gt; use test&lt;br /&gt;switched to db test&lt;br /&gt;&amp;gt; for (var i = 1; i &amp;lt;= 500000; i++) db.users.insert({age:i, name:"wangwenlong", addr:"Beijing",&lt;br /&gt;country:"China"})&lt;br /&gt;&amp;gt; db.users.stats()&lt;br /&gt;{&lt;br /&gt;"sharded" : true, --说明此表已被shard&lt;br /&gt;"ns" : "test.users",&lt;br /&gt;"count" : 500000,&lt;br /&gt;"size" : 48000000,&lt;br /&gt;"avgObjSize" : 96,&lt;br /&gt;"storageSize" : 66655232,&lt;br /&gt;"nindexes" : 1,&lt;br /&gt;"nchunks" : 43,&lt;br /&gt;"shards" : {&lt;br /&gt;"shard0000" : { --在此分片实例上约有24.5M 数据&lt;br /&gt;"ns" : "test.users",&lt;br /&gt;"count" : 254889,&lt;br /&gt;"size" : 24469344,&lt;br /&gt;"avgObjSize" : 96,&lt;br /&gt;&lt;br /&gt;79 / 91&lt;br /&gt;"storageSize" : 33327616,&lt;br /&gt;"numExtents" : 8,&lt;br /&gt;"nindexes" : 1,&lt;br /&gt;"lastExtentSize" : 12079360,&lt;br /&gt;"paddingFactor" : 1,&lt;br /&gt;"flags" : 1,&lt;br /&gt;"totalIndexSize" : 11468800,&lt;br /&gt;"indexSizes" : {&lt;br /&gt;"_id_" : 11468800&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;},&lt;br /&gt;"shard0001" : { --在此分片实例上约有23.5M 数据&lt;br /&gt;"ns" : "test.users",&lt;br /&gt;"count" : 245111,&lt;br /&gt;"size" : 23530656,&lt;br /&gt;"avgObjSize" : 96,&lt;br /&gt;"storageSize" : 33327616,&lt;br /&gt;"numExtents" : 8,&lt;br /&gt;"nindexes" : 1,&lt;br /&gt;"lastExtentSize" : 12079360,&lt;br /&gt;"paddingFactor" : 1,&lt;br /&gt;"flags" : 1,&lt;br /&gt;"totalIndexSize" : 10649600,&lt;br /&gt;"indexSizes" : {&lt;br /&gt;"_id_" : 10649600&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;我们看一下磁盘上的物理文件情况&lt;br /&gt;[root@localhost bin]# ll /data/shard/s0/test --此分片实例上有数据产生&lt;br /&gt;总计 262420&lt;br /&gt;-rw------- 1 root root 16777216 06-03 15:21 test.0&lt;br /&gt;-rw------- 1 root root 33554432 06-03 15:21 test.1&lt;br /&gt;-rw------- 1 root root 67108864 06-03 15:22 test.2&lt;br /&gt;-rw------- 1 root root 134217728 06-03 15:24 test.3&lt;br /&gt;-rw------- 1 root root 16777216 06-03 15:21 test.ns&lt;br /&gt;[root@localhost bin]# ll /data/shard/s1/test --此分片实例上有数据产生&lt;br /&gt;总计 262420&lt;br /&gt;&lt;br /&gt;80 / 91&lt;br /&gt;-rw------- 1 root root 16777216 06-03 15:21 test.0&lt;br /&gt;-rw------- 1 root root 33554432 06-03 15:21 test.1&lt;br /&gt;-rw------- 1 root root 67108864 06-03 15:22 test.2&lt;br /&gt;-rw------- 1 root root 134217728 06-03 15:23 test.3&lt;br /&gt;-rw------- 1 root root 16777216 06-03 15:21 test.ns&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;看上述结果，表明test.users 集合已经被分片处理了，但是通过mongos 路由，我们并感觉&lt;br /&gt;不到是数据存放在哪个shard 的chunk 上的，这就是MongoDB 用户体验上的一个优势，即&lt;br /&gt;对用户是透明的。&lt;br /&gt;22.6 管理维护Sharding&lt;br /&gt;22.6.1 列出所有的Shard Server&lt;br /&gt;&amp;gt; db.runCommand({ listshards: 1 }) --列出所有的Shard Server&lt;br /&gt;{&lt;br /&gt;"shards" : [&lt;br /&gt;{&lt;br /&gt;"_id" : "shard0000",&lt;br /&gt;"host" : "localhost:20000"&lt;br /&gt;},&lt;br /&gt;{&lt;br /&gt;"_id" : "shard0001",&lt;br /&gt;"host" : "localhost:20001"&lt;br /&gt;}&lt;br /&gt;],&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;22.6.2 查看Sharding信息&lt;br /&gt;&amp;gt; printShardingStatus() --查看Sharding 信息&lt;br /&gt;--- Sharding Status ---&lt;br /&gt;sharding version: { "_id" : 1, "version" : 3 }&lt;br /&gt;shards:&lt;br /&gt;{ "_id" : "shard0000", "host" : "localhost:20000" }&lt;br /&gt;{ "_id" : "shard0001", "host" : "localhost:20001" }&lt;br /&gt;databases:&lt;br /&gt;{ "_id" : "admin", "partitioned" : false, "primary" : "config" }&lt;br /&gt;{ "_id" : "test", "partitioned" : true, "primary" : "shard0000" }&lt;br /&gt;test.users chunks:&lt;br /&gt;&lt;br /&gt;81 / 91&lt;br /&gt;shard0000 1&lt;br /&gt;{ "_id" : { $minKey : 1 } } --&amp;gt;&amp;gt; { "_id" : { $maxKey : 1 } } on :&lt;br /&gt;shard0000 { "t" : 1000, "i" : 0 }&lt;br /&gt;&amp;gt;&lt;br /&gt;22.6.3 判断是否是Sharding&lt;br /&gt;&amp;gt; db.runCommand({ isdbgrid:1 })&lt;br /&gt;{ "isdbgrid" : 1, "hostname" : "localhost", "ok" : 1 }&lt;br /&gt;&amp;gt;&lt;br /&gt;22.6.4 对现有的表进行Sharding&lt;br /&gt;刚才我们是对表test.users 进行分片了，下面我们将对库中现有的未分片的表test.users_2 进&lt;br /&gt;行分片处理&lt;br /&gt;表最初状态如下，可以看出他没有被分片过：&lt;br /&gt;&amp;gt; db.users_2.stats()&lt;br /&gt;{&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;"sharded" : false,&lt;br /&gt;"primary" : "shard0000",&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;"count" : 500000,&lt;br /&gt;"size" : 48000016,&lt;br /&gt;"avgObjSize" : 96.000032,&lt;br /&gt;"storageSize" : 61875968,&lt;br /&gt;"numExtents" : 11,&lt;br /&gt;"nindexes" : 1,&lt;br /&gt;"lastExtentSize" : 15001856,&lt;br /&gt;"paddingFactor" : 1,&lt;br /&gt;"flags" : 1,&lt;br /&gt;"totalIndexSize" : 20807680,&lt;br /&gt;"indexSizes" : {&lt;br /&gt;"_id_" : 20807680&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;对其进行分片处理:&lt;br /&gt;&amp;gt; use admin&lt;br /&gt;&lt;br /&gt;82 / 91&lt;br /&gt;switched to db admin&lt;br /&gt;&amp;gt; db.runCommand({ shardcollection: "test.users_2", key: { _id:1 }})&lt;br /&gt;{ "collectionsharded" : "test.users_2", "ok" : 1 }&lt;br /&gt;再次查看分片后的表的状态，可以看到它已经被我们分片了&lt;br /&gt;&amp;gt; use test&lt;br /&gt;switched to db test&lt;br /&gt;&amp;gt; db.users_2.stats()&lt;br /&gt;{&lt;br /&gt;"sharded" : true,&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;"count" : 505462,&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;"shards" : {&lt;br /&gt;"shard0000" : {&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;"ok" : 1&lt;br /&gt;},&lt;br /&gt;"shard0001" : {&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;22.6.5 新增Shard Server&lt;br /&gt;刚才我们演示的是新增分片表，接下来我们演示如何新增Shard Server&lt;br /&gt;启动一个新Shard Server 进程&lt;br /&gt;[root@localhost ~]# mkdir /data/shard/s2&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongod --shardsvr --port 20002 --dbpath /data/shard/s2&lt;br /&gt;--fork --logpath /data/shard/log/s2.log --directoryperdb&lt;br /&gt;all output going to: /data/shard/log/s2.log&lt;br /&gt;forked process: 6772&lt;br /&gt;配置新Shard Server&lt;br /&gt;[root@localhost ~]# /Apps/mongo/bin/mongo admin --port 40000&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;&lt;br /&gt;83 / 91&lt;br /&gt;connecting to: 127.0.0.1:40000/admin&lt;br /&gt;&amp;gt; db.runCommand({ addshard:"localhost:20002" })&lt;br /&gt;{ "shardAdded" : "shard0002", "ok" : 1 }&lt;br /&gt;&amp;gt; printShardingStatus()&lt;br /&gt;--- Sharding Status ---&lt;br /&gt;sharding version: { "_id" : 1, "version" : 3 }&lt;br /&gt;shards:&lt;br /&gt;{ "_id" : "shard0000", "host" : "localhost:20000" }&lt;br /&gt;{ "_id" : "shard0001", "host" : "localhost:20001" }&lt;br /&gt;{ "_id" : "shard0002", "host" : "localhost:20002" } --新增Shard Server&lt;br /&gt;databases:&lt;br /&gt;{ "_id" : "admin", "partitioned" : false, "primary" : "config" }&lt;br /&gt;{ "_id" : "test", "partitioned" : true, "primary" : "shard0000" }&lt;br /&gt;test.users chunks:&lt;br /&gt;shard0002 2&lt;br /&gt;shard0000 21&lt;br /&gt;shard0001 21&lt;br /&gt;too many chunksn to print, use verbose if you want to force print&lt;br /&gt;test.users_2 chunks:&lt;br /&gt;shard0001 46&lt;br /&gt;shard0002 1&lt;br /&gt;shard0000 45&lt;br /&gt;too many chunksn to print, use verbose if you want to force print&lt;br /&gt;查看分片表状态，以验证新Shard Server&lt;br /&gt;&amp;gt; use test&lt;br /&gt;switched to db test&lt;br /&gt;&amp;gt; db.users_2.stats()&lt;br /&gt;{&lt;br /&gt;"sharded" : true,&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;"shard0002" : { --新的Shard Server 已有数据&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;"count" : 21848,&lt;br /&gt;"size" : 2097408,&lt;br /&gt;"avgObjSize" : 96,&lt;br /&gt;"storageSize" : 2793472,&lt;br /&gt;"numExtents" : 5,&lt;br /&gt;"nindexes" : 1,&lt;br /&gt;"lastExtentSize" : 2097152,&lt;br /&gt;"paddingFactor" : 1,&lt;br /&gt;"flags" : 1,&lt;br /&gt;"totalIndexSize" : 1277952,&lt;br /&gt;&lt;br /&gt;84 / 91&lt;br /&gt;"indexSizes" : {&lt;br /&gt;"_id_" : 1277952&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt;&lt;br /&gt;我们可以发现，当我们新增Shard Server 后数据自动分布到了新Shard 上，这是由MongoDB&lt;br /&gt;内部自已实现的。&lt;br /&gt;22.6.6 移除Shard Server&lt;br /&gt;有些时候有于硬件资源有限，所以我们不得不进行一些回收工作，下面我们就要将刚刚启用&lt;br /&gt;的Shard Server 回收，系统首先会将在这个即将被移除的Shard Server 上的数据先平均分配&lt;br /&gt;到其它的Shard Server 上，然后最终在将这个Shard Server 踢下线, 我们需要不停的调用&lt;br /&gt;db.runCommand({"removeshard" : "localhost:20002"});来观察这个移除操作进行到哪里了：&lt;br /&gt;&amp;gt; use admin&lt;br /&gt;switched to db admin&lt;br /&gt;&amp;gt; db.runCommand({"removeshard" : "localhost:20002"});&lt;br /&gt;{&lt;br /&gt;"msg" : "draining started successfully",&lt;br /&gt;"state" : "started",&lt;br /&gt;"shard" : "shard0002",&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt; db.runCommand({"removeshard" : "localhost:20002"});&lt;br /&gt;{&lt;br /&gt;"msg" : "draining ongoing",&lt;br /&gt;"state" : "ongoing",&lt;br /&gt;"remaining" : {&lt;br /&gt;"chunks" : NumberLong(44),&lt;br /&gt;"dbs" : NumberLong(0)&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;&amp;gt; db.runCommand({"removeshard" : "localhost:20002"});&lt;br /&gt;{&lt;br /&gt;"msg" : "draining ongoing",&lt;br /&gt;"state" : "ongoing",&lt;br /&gt;"remaining" : {&lt;br /&gt;&lt;br /&gt;85 / 91&lt;br /&gt;"chunks" : NumberLong(1),&lt;br /&gt;"dbs" : NumberLong(0)&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt; db.runCommand({"removeshard" : "localhost:20002"});&lt;br /&gt;{&lt;br /&gt;"msg" : "removeshard completed successfully",&lt;br /&gt;"state" : "completed",&lt;br /&gt;"shard" : "shard0002",&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&amp;gt; db.runCommand({"removeshard" : "localhost:20002"});&lt;br /&gt;{&lt;br /&gt;"assertion" : "can't find shard for: localhost:20002",&lt;br /&gt;"assertionCode" : 13129,&lt;br /&gt;"errmsg" : "db assertion failure",&lt;br /&gt;"ok" : 0&lt;br /&gt;}&lt;br /&gt;最终移除后，当我们再次调用db.runCommand({"removeshard" : "localhost:20002"});的时候系统&lt;br /&gt;会报错，已便通知我们不存在20002 这个端口的Shard Server 了，因为它已经被移除掉了。&lt;br /&gt;接下来我们看一下表中的数据分布：&lt;br /&gt;&amp;gt; use test&lt;br /&gt;switched to db test&lt;br /&gt;&amp;gt; db.users_2.stats()&lt;br /&gt;{&lt;br /&gt;"sharded" : true,&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;"count" : 500000,&lt;br /&gt;"size" : 48000000,&lt;br /&gt;"avgObjSize" : 96,&lt;br /&gt;"storageSize" : 95203584,&lt;br /&gt;"nindexes" : 1,&lt;br /&gt;"nchunks" : 92,&lt;br /&gt;"shards" : {&lt;br /&gt;"shard0000" : {&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;"count" : 248749,&lt;br /&gt;"size" : 23879904,&lt;br /&gt;"avgObjSize" : 96,&lt;br /&gt;"storageSize" : 61875968,&lt;br /&gt;"numExtents" : 11,&lt;br /&gt;"nindexes" : 1,&lt;br /&gt;&lt;br /&gt;86 / 91&lt;br /&gt;"lastExtentSize" : 15001856,&lt;br /&gt;"paddingFactor" : 1,&lt;br /&gt;"flags" : 1,&lt;br /&gt;"totalIndexSize" : 13033472,&lt;br /&gt;"indexSizes" : {&lt;br /&gt;"_id_" : 13033472&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;},&lt;br /&gt;"shard0001" : {&lt;br /&gt;"ns" : "test.users_2",&lt;br /&gt;"count" : 251251,&lt;br /&gt;"size" : 24120096,&lt;br /&gt;"avgObjSize" : 96,&lt;br /&gt;"storageSize" : 33327616,&lt;br /&gt;"numExtents" : 8,&lt;br /&gt;"nindexes" : 1,&lt;br /&gt;"lastExtentSize" : 12079360,&lt;br /&gt;"paddingFactor" : 1,&lt;br /&gt;"flags" : 1,&lt;br /&gt;"totalIndexSize" : 10469376,&lt;br /&gt;"indexSizes" : {&lt;br /&gt;"_id_" : 10469376&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;可以看出数据又被平均分配到了另外2 台Shard Server 上了，对业务没什么特别大的影响。&lt;br /&gt;第二十三章 Replica Sets + Sharding&lt;br /&gt;MongoDB Auto-Sharding 解决了海量存储和动态扩容的问题，但离实际生产环境所需的高可&lt;br /&gt;靠、高可用还有些距离，所以有了&amp;#8221; Replica Sets + Sharding&amp;#8221;的解决方案:&lt;br /&gt;&#x1; Shard:&lt;br /&gt;使用 Replica Sets，确保每个数据节点都具有备份、自动容错转移、自动恢复能力。&lt;br /&gt;&#x1; Config:&lt;br /&gt;使用3 个配置服务器，确保元数据完整性&lt;br /&gt;&#x1; Route:&lt;br /&gt;使用3 个路由进程，实现负载平衡，提高客户端接入性能&lt;br /&gt;以下我们配置一个 Replica Sets + Sharding 的环境，架构图如下:&lt;br /&gt;&lt;br /&gt;87 / 91&lt;br /&gt;开放的端口如下：&lt;br /&gt;主机 IP 服务及端口&lt;br /&gt;Server A 192.168.3.231 mongod shard1_1:27017&lt;br /&gt;mongod shard2_1:27018&lt;br /&gt;mongod config1:20000&lt;br /&gt;mongs1:30000&lt;br /&gt;Server B 192.168.3.232 mongod shard1_2:27017&lt;br /&gt;mongod shard2_2:27018&lt;br /&gt;mongod config2:20000&lt;br /&gt;mongs2:30000&lt;br /&gt;Server C 192.168.3.233 mongod shard1_3:27017&lt;br /&gt;mongod shard2_3:27018&lt;br /&gt;mongod config3:20000&lt;br /&gt;mongs3:30000&lt;br /&gt;23.1 创建数据目录&lt;br /&gt;在Server A 上:&lt;br /&gt;[root@localhost bin]# mkdir -p /data/shard1_1&lt;br /&gt;[root@localhost bin]# mkdir -p /data/shard2_1&lt;br /&gt;&lt;br /&gt;88 / 91&lt;br /&gt;[root@localhost bin]# mkdir -p /data/config&lt;br /&gt;在Server B 上:&lt;br /&gt;[root@localhost bin]# mkdir -p /data/shard1_2&lt;br /&gt;[root@localhost bin]# mkdir -p /data/shard2_2&lt;br /&gt;[root@localhost bin]# mkdir -p /data/config&lt;br /&gt;在Server C 上:&lt;br /&gt;[root@localhost bin]# mkdir -p /data/shard1_3&lt;br /&gt;[root@localhost bin]# mkdir -p /data/shard2_3&lt;br /&gt;[root@localhost bin]# mkdir -p /data/config&lt;br /&gt;23.2 配置Replica Sets&lt;br /&gt;23.2.1 配置shard1所用到的Replica Sets&lt;br /&gt;在Server A 上:&lt;br /&gt;[root@localhost bin]# /Apps/mongo/bin/mongod --shardsvr --replSet shard1 --port 27017&lt;br /&gt;--dbpath /data/shard1_1 --logpath /data/shard1_1/shard1_1.log --logappend --fork&lt;br /&gt;[root@localhost bin]# all output going to: /data/shard1_1/shard1_1.log&lt;br /&gt;forked process: 18923&lt;br /&gt;在Server B 上:&lt;br /&gt;[root@localhost bin]# /Apps/mongo/bin/mongod --shardsvr --replSet shard1 --port 27017&lt;br /&gt;--dbpath /data/shard1_2 --logpath /data/shard1_2/shard1_2.log --logappend --fork&lt;br /&gt;forked process: 18859&lt;br /&gt;[root@localhost bin]# all output going to: /data/shard1_2/shard1_2.log&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;在Server C 上:&lt;br /&gt;[root@localhost bin]# /Apps/mongo/bin/mongod --shardsvr --replSet shard1 --port 27017&lt;br /&gt;--dbpath /data/shard1_3 --logpath /data/shard1_3/shard1_3.log --logappend --fork&lt;br /&gt;all output going to: /data/shard1_3/shard1_3.log&lt;br /&gt;forked process: 18768&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;用mongo 连接其中一台机器的27017 端口的mongod，初始化Replica Sets&amp;#8220;shard1&amp;#8221;，执行:&lt;br /&gt;[root@localhost bin]# ./mongo --port 27017&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:27017/test&lt;br /&gt;&amp;gt; config = {_id: 'shard1', members: [&lt;br /&gt;&lt;br /&gt;89 / 91&lt;br /&gt;... {_id: 0, host: '192.168.3.231:27017'},&lt;br /&gt;... {_id: 1, host: '192.168.3.232:27017'},&lt;br /&gt;... {_id: 2, host: '192.168.3.233:27017'}]&lt;br /&gt;... }&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;&amp;gt; rs.initiate(config)&lt;br /&gt;{&lt;br /&gt;"info" : "Config now saved locally. Should come online in about a minute.",&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;23.2.2 配置shard2所用到的Replica Sets&lt;br /&gt;在Server A 上:&lt;br /&gt;[root@localhost bin]# /Apps/mongo/bin/mongod --shardsvr --replSet shard2 --port 27018&lt;br /&gt;--dbpath /data/shard2_1 --logpath /data/shard2_1/shard2_1.log --logappend --fork&lt;br /&gt;all output going to: /data/shard2_1/shard2_1.log&lt;br /&gt;[root@localhost bin]# forked process: 18993&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;在Server B 上:&lt;br /&gt;[root@localhost bin]# /Apps/mongo/bin/mongod --shardsvr --replSet shard2 --port 27018&lt;br /&gt;--dbpath /data/shard2_2 --logpath /data/shard2_2/shard2_2.log --logappend --fork&lt;br /&gt;all output going to: /data/shard2_2/shard2_2.log&lt;br /&gt;forked process: 18923&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;在Server C 上:&lt;br /&gt;[root@localhost bin]# /Apps/mongo/bin/mongod --shardsvr --replSet shard2 --port 27018&lt;br /&gt;--dbpath /data/shard2_3 --logpath /data/shard2_3/shard2_3.log --logappend --fork&lt;br /&gt;[root@localhost bin]# all output going to: /data/shard2_3/shard2_3.log&lt;br /&gt;forked process: 18824&lt;br /&gt;[root@localhost bin]#&lt;br /&gt;用mongo 连接其中一台机器的27018 端口的mongod，初始化Replica Sets &amp;#8220;shard2&amp;#8221;，执行:&lt;br /&gt;[root@localhost bin]# ./mongo --port 27018&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:27018/test&lt;br /&gt;&amp;gt; config = {_id: 'shard2', members: [&lt;br /&gt;... {_id: 0, host: '192.168.3.231:27018'},&lt;br /&gt;&lt;br /&gt;90 / 91&lt;br /&gt;... {_id: 1, host: '192.168.3.232:27018'},&lt;br /&gt;... {_id: 2, host: '192.168.3.233:27018'}]&lt;br /&gt;... }&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;&amp;gt; rs.initiate(config)&lt;br /&gt;{&lt;br /&gt;"info" : "Config now saved locally. Should come online in about a minute.",&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;23.3 配置3 台Config Server&lt;br /&gt;在Server A、B、C上执行:&lt;br /&gt;/Apps/mongo/bin/mongod --configsvr --dbpath /data/config --port 20000 --logpath&lt;br /&gt;/data/config/config.log --logappend --fork&lt;br /&gt;23.4 配置3 台Route Process&lt;br /&gt;在Server A、B、C上执行:&lt;br /&gt;/Apps/mongo/bin/mongos --configdb&lt;br /&gt;192.168.3.231:20000,192.168.3.232:20000,192.168.3.233:20000 --port 30000 --chunkSize 1&lt;br /&gt;--logpath /data/mongos.log --logappend --fork&lt;br /&gt;23.5 配置Shard Cluster&lt;br /&gt;连接到其中一台机器的端口30000 的mongos 进程，并切换到admin 数据库做以下配置&lt;br /&gt;[root@localhost bin]# ./mongo --port 30000&lt;br /&gt;MongoDB shell version: 1.8.1&lt;br /&gt;connecting to: 127.0.0.1:30000/test&lt;br /&gt;&amp;gt; use admin&lt;br /&gt;switched to db admin&lt;br /&gt;&amp;gt;db.runCommand({addshard:"shard1/192.168.3.231:27017,192.168.3.232:27017,192.168.3.233:&lt;br /&gt;27017"});&lt;br /&gt;{ "shardAdded" : "shard1", "ok" : 1 }&lt;br /&gt;&amp;gt;db.runCommand({addshard:"shard2/192.168.3.231:27018,192.168.3.232:27018,192.168.3.233:&lt;br /&gt;27018"});&lt;br /&gt;{ "shardAdded" : "shard2", "ok" : 1 }&lt;br /&gt;&amp;gt;&lt;br /&gt;激活数据库及集合的分片&lt;br /&gt;db.runCommand({ enablesharding:"test" })&lt;br /&gt;db.runCommand({ shardcollection: "test.users", key: { _id:1 }})&lt;br /&gt;&lt;br /&gt;91 / 91&lt;br /&gt;23.6 验证Sharding正常工作&lt;br /&gt;连接到其中一台机器的端口30000 的mongos 进程，并切换到test 数据库，以便添加测试数&lt;br /&gt;据&lt;br /&gt;use test&lt;br /&gt;for(var i=1;i&amp;lt;=200000;i++) db.users.insert({id:i,addr_1:"Beijing",addr_2:"Shanghai"});&lt;br /&gt;db.users.stats()&lt;br /&gt;{&lt;br /&gt;"sharded" : true,&lt;br /&gt;"ns" : "test.users",&lt;br /&gt;"count" : 200000,&lt;br /&gt;"size" : 25600384,&lt;br /&gt;"avgObjSize" : 128,&lt;br /&gt;"storageSize" : 44509696,&lt;br /&gt;"nindexes" : 2,&lt;br /&gt;"nchunks" : 15,&lt;br /&gt;"shards" : {&lt;br /&gt;"shard0000" : {&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;},&lt;br /&gt;"shard0001" : {&lt;br /&gt;&amp;#8230;&amp;#8230;&lt;br /&gt;}&lt;br /&gt;},&lt;br /&gt;"ok" : 1&lt;br /&gt;}&lt;br /&gt;可以看到Sharding搭建成功了，跟我们期望的结果一致，至此我们就将Replica Sets与Sharding&lt;br /&gt;结合的架构也学习完毕了。 &lt;br /&gt;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2507777.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507777.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507376.html</id><title type="text">SQL Server 索引中include的魅力（具有包含性列的索引）</title><summary type="text">开文之前首先要讲讲几个概念 【覆盖查询】 当索引包含查询引用的所有列时，它通常称为“覆盖查询”。 【索引覆盖】 如果返回的数据列就包含于索引的键值中，或者包含于索引的键值+聚集索引的键值中，那么就不会发生Bookup Lookup，因为找到索引项，就已经找到所需的数据了，没有必要再到数据行去找了。这种情况，叫做索引覆盖； 【复合索引】 和复合索引相对的就是单一索引了，就是索引只包含一个字段，所以复合索引就是包含两个或者多个字段的索引； 【非键列】 键列就是在索引中所包含的列，当然非键列就是该索引之外的列了；下面就开始今天的主题 【摘要1】Code h...</summary><published>2012-05-18T02:44:00Z</published><updated>2012-05-18T02:44:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507376.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507376.html"/><content type="html">&lt;p&gt;开文之前首先要讲讲几个概念&lt;/p&gt;&lt;p&gt;【覆盖查询】&lt;br /&gt;&lt;/p&gt;&lt;p&gt;当索引包含查询引用的所有列时，它通常称为&amp;#8220;覆盖查询&amp;#8221;。 &lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;【索引覆盖】&lt;/p&gt;&lt;p&gt; 如果返回的数据列就包含于索引的键值中，或者包含于索引的键值+聚集索引的键值中，那么就不会发生Bookup Lookup，因为找到索引项，就已经找到所需的数据了，没有必要再到数据行去找了。这种情况，叫做索引覆盖；&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【复合索引】&lt;br /&gt;&lt;/p&gt;&lt;p&gt;和复合索引相对的就是单一索引了，就是索引只包含一个字段，所以复合索引就是包含两个或者多个字段的索引；&lt;/p&gt;&lt;p&gt; &lt;br /&gt;&lt;/p&gt;&lt;p&gt;【非键列】&lt;/p&gt;&lt;p&gt;键列就是在索引中所包含的列，当然非键列就是该索引之外的列了；&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;下面就开始今天的主题&lt;/p&gt;&lt;p&gt;【摘要1】&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000"&gt;在&amp;nbsp;SQL&amp;nbsp;Server&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;2005&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;中，可以通过将非键列添加到非聚集索引的叶级别来扩展非聚集索引的功能。通过包含非键列，可以创建覆盖更多查询的非聚集索引。这是因为非键列具有下列优点：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;它们可以是不允许作为索引键列的数据类型。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;在计算索引键列数或索引键大小时，数据库引擎不考虑它们。&lt;br /&gt;当查询中的所有列都作为键列或非键列包含在索引中时，带有包含性非键列的索引可以显著提高查询性能。这样可以实现性能提升，因为查询优化器可以在索引中找到所有列值；不访问表或聚集索引数据，从而减少磁盘&amp;nbsp;I&lt;/span&gt;&lt;span style="color: #000000"&gt;/&lt;/span&gt;&lt;span style="color: #000000"&gt;O&amp;nbsp;操作。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;说明：第一：只能是针对&lt;span style="color: #000000"&gt;非聚集索引；第二：比起复合索引是有性能上的提升的，因为索引的大小变小了；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【摘要2】&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000"&gt;键列存储在索引的所有级别中，而非键列仅存储在叶级别中。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;说明：这就表现为包含与不包含的关系了。有关索引级别的详细信息，请参阅&lt;a id="ctl00_MTCS_main_ctl02" onclick="javascript:Track('ctl00_MTCS_main_ctl00|ctl00_MTCS_main_ctl02',this);" href="http://msdn.microsoft.com/zh-cn/library/ms189051%28SQL.90%29.aspx" target="_blank"&gt;&lt;font color="#3d81ee"&gt;表组织和索引组织&lt;/font&gt;&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【摘要3】&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000"&gt;使用包含性列以避免大小限制&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;可以将非键列包含在非聚集索引中，以避免超过当前索引大小的限制（最大键列数为&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;16&lt;/span&gt;&lt;span style="color: #000000"&gt;，最大索引键大小为&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;900&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;字节）。数据库引擎计算索引键列数或索引键大小时，不考虑非键列。&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;例如，假设要为&amp;nbsp;AdventureWorks&amp;nbsp;示例数据库的&amp;nbsp;Document&amp;nbsp;表中的以下列建立索引：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Title&amp;nbsp;nvarchar(&lt;/span&gt;&lt;span style="color: #800080"&gt;50&lt;/span&gt;&lt;span style="color: #000000"&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Revision&amp;nbsp;nchar(&lt;/span&gt;&lt;span style="color: #800080"&gt;5&lt;/span&gt;&lt;span style="color: #000000"&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FileName&amp;nbsp;nvarchar(&lt;/span&gt;&lt;span style="color: #800080"&gt;400&lt;/span&gt;&lt;span style="color: #000000"&gt;)&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;因为&amp;nbsp;nchar&amp;nbsp;和&amp;nbsp;nvarchar&amp;nbsp;数据类型的每个字符需要&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;2&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;个字节，所以包含这三列的索引将超出&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;900&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;字节的大小限制&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;10&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;个字节&amp;nbsp;(&lt;/span&gt;&lt;span style="color: #800080"&gt;455&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;2&lt;/span&gt;&lt;span style="color: #000000"&gt;)。使用&amp;nbsp;CREATE&amp;nbsp;INDEX&amp;nbsp;语句的&amp;nbsp;INCLUDE&amp;nbsp;子句，可以将索引键定义为&amp;nbsp;(Title,&amp;nbsp;Revision)，将&amp;nbsp;FileName&amp;nbsp;定义为非键列。这样，索引键大小将为&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;110&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;个字节&amp;nbsp;(&lt;/span&gt;&lt;span style="color: #800080"&gt;55&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;2&lt;/span&gt;&lt;span style="color: #000000"&gt;)，并且索引仍将包含所需的所有列。下面的语句就创建了这样的索引。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;说明：当你把一个nvarchar(500)的字段设置为主键的时候，你就可以看到不能超出900字节的提示了。一般来说我们是不太会做这些操作的，所以那个错误提示也是不常见的，也许你可能还见过。&lt;/p&gt;&lt;p&gt;一个数据页的大小才8k，所以我们合理的设置每个字段的大小，不要浪费太多的空间，这样对查询也是有好处的，这个include就比较好的的解决了索引和空间的问题，虽然那些include的数据也会占用空间。&lt;/p&gt;&lt;p&gt;虽然可以设置include，但是也尽量不要使用太多的字段作为索引包含的非键列。 &lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【摘要4】&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000"&gt;带有包含性列的索引准则&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;设计带有包含性列的非聚集索引时，请考虑下列准则：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;在&amp;nbsp;CREATE&amp;nbsp;INDEX&amp;nbsp;语句的&amp;nbsp;INCLUDE&amp;nbsp;子句中定义非键列。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;只能对表或索引视图的非聚集索引定义非键列。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;除&amp;nbsp;text、ntext&amp;nbsp;和&amp;nbsp;image&amp;nbsp;之外，允许所有数据类型。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;精确或不精确的确定性计算列都可以是包含性列。有关详细信息，请参阅为计算列创建索引。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;与键列一样，只要允许将计算列数据类型作为非键索引列，从&amp;nbsp;image、ntext&amp;nbsp;和&amp;nbsp;text&amp;nbsp;数据类型派生的计算列就可以作为非键（包含性）列。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;不能同时在&amp;nbsp;INCLUDE&amp;nbsp;列表和键列列表中指定列名。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;INCLUDE&amp;nbsp;列表中的列名不能重复。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;说明：include不能使用在聚集索引中。后面的两点，这个在实际中很难想象会有这样的需求要把重复列放到一个索引中。如果有朋友遇到过这样的需求可以告知一些，不胜感激。那如果有是否可以通过不同的列名（其实保存是同样的值）来解决这个问题呢？？&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【摘要5】 &lt;br /&gt;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000"&gt;列大小准则&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;必须至少定义一个键列。最大非键列数为&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;1023&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;列。也就是最大的表列数减&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;1&lt;/span&gt;&lt;span style="color: #000000"&gt;。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;索引键列（不包括非键）必须遵守现有索引大小的限制（最大键列数为&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;16&lt;/span&gt;&lt;span style="color: #000000"&gt;，总索引键大小为&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;900&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;字节）。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;所有非键列的总大小只受&amp;nbsp;INCLUDE&amp;nbsp;子句中所指定列的大小限制；例如，varchar(max)&amp;nbsp;列限制为&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080"&gt;2&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;GB。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;说明：&lt;span style="color: #000000"&gt;varchar(max)&lt;/span&gt;这样的定义是在2005之后才有的，所以这些数值也是对2005后的版本才生效的。&lt;/p&gt;&lt;p&gt;最大的表列数为：1024&lt;/p&gt;&lt;p&gt;&lt;span style="color: #000000"&gt;最大非键列数为：&lt;/span&gt;&lt;span style="color: #800080"&gt;1023&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【摘要6】&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000"&gt;修改已定义为包含性列的表列时，要受下列限制：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;除非先删除索引，否则无法从表中删除非键列。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;除进行下列更改外，不能对非键列进行其他更改：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;o&amp;nbsp;将列的为空性从&amp;nbsp;NOT&amp;nbsp;NULL&amp;nbsp;改为&amp;nbsp;NULL。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;o&amp;nbsp;增加&amp;nbsp;varchar、nvarchar&amp;nbsp;或&amp;nbsp;varbinary&amp;nbsp;列的长度。&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;这些列修改限制也适用于索引键列。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;说明：这些细小的东西一直没有注意过。所以要记录下来，用来&amp;#8220;防身&amp;#8221;，呵呵。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【摘要7】&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000"&gt;设计建议&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;重新设计索引键大小较大的非聚集索引，以便只有用于搜索和查找的列为键列。将覆盖查询的所有其他列设置为包含性非键列。这样，将具有覆盖查询所需的所有列，但索引键本身较小，而且效率高。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;说明：也就是说把常用的where后面的条件查询的字段作为索引的&lt;span style="color: #000000"&gt;键列&lt;/span&gt;，而需要返回的字段就作为索引包含的&lt;span style="color: #000000"&gt;非键列&lt;/span&gt;。&lt;/p&gt;&lt;p&gt;如果where的是两个或两个以上的谓词的话，这个索引就可以创建为复合索引了。以前天真的认为要返回的字段只能通过在复合索引中入这些字段，不管它是否会用来做谓词。看到这篇文章，才有了豁然开朗的感觉。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【摘要8】&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000"&gt;USE&amp;nbsp;AdventureWorks;&lt;br /&gt;GO&lt;br /&gt;CREATE&amp;nbsp;INDEX&amp;nbsp;IX_Address_PostalCode&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;ON&amp;nbsp;Person.Address&amp;nbsp;(PostalCode)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;INCLUDE&amp;nbsp;(AddressLine1,&amp;nbsp;AddressLine2,&amp;nbsp;City,&amp;nbsp;StateProvinceID);&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;说明：这个是使用include的语法，在表的设计中的索引设计中是没有办法选择的；&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;【摘要9】&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000"&gt;性能注意事项&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;避免添加不必要的列。添加过多的索引列（键列或非键列）会对性能产生下列影响：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;一页上能容纳的索引行将更少。这样会使&amp;nbsp;I&lt;/span&gt;&lt;span style="color: #000000"&gt;/&lt;/span&gt;&lt;span style="color: #000000"&gt;O&amp;nbsp;增加并降低缓存效率。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;需要更多的磁盘空间来存储索引。特别是，将&amp;nbsp;varchar(max)、nvarchar(max)、varbinary(max)&amp;nbsp;或&amp;nbsp;xml&amp;nbsp;数据类型添加为非键索引列会显著增加磁盘空间要求。这是因为列值被复制到了索引叶级别。因此，它们既驻留在索引中，也驻留在基表中。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000"&gt;*&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;索引维护可能会增加对基础表或索引视图执行修改、插入、更新或删除操作所需的时间。&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;您应该确定修改数据时在查询性能上的提升是否超过了对性能的影响，以及是否需要额外的磁盘空间要求。有关评估查询性能的详细信息，请参阅查询优化。&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;说明：&amp;#8220;&lt;span style="color: #000000"&gt;这是因为列值被复制到了索引叶级别&lt;/span&gt;&amp;#8221;这句很好的说明了物理上的存储结构和原理。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【图片解析】&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div align="center"&gt;&lt;img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/gaizai/Include%E7%B4%A2%E5%BC%953.png" width="451" height="406" /&gt;&lt;br /&gt;&lt;/div&gt;&amp;nbsp;上图也说明了为什么不能在聚集索引中建立具有包含性列的索引，因为非聚集索引的叶层是由索引页而不是由数据页组成，这就得说到聚集和非聚集索引的的物理存储了，聚集索引的顺序排序和存储就是基表的顺序和存储结构。 &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;　&lt;/p&gt;&lt;p&gt;【一个例子】&lt;/p&gt;&lt;div style="border-right-width: 1px; border-top-width: 1px; border-bottom-width: 1px; border-left-width: 1px"&gt;&lt;div&gt;&lt;p&gt;SELECT UserName,Password,RealName,Mobile,Age FROM bw_Users WHERE UserName = XXX AND Age = XX&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;说明：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;这是一个我们很常见的查询语句，我们如何提高查询效率呢？&lt;/li&gt;&lt;li&gt;首先我们来看看谓词，这条语句是通过UserName = XXX AND Age = XX作为条件的，那么我们就应该建立一个组合索引，也称为复合索引，注意索引中的键列的位置，先UserName后Age；&lt;/li&gt;&lt;li&gt;其实上面那个是一个非聚集索引，那我们就可以把Password,RealName,Mobile这三列作为索引包含列；&lt;/li&gt;&lt;li&gt;所以，最终就是建立一个以UserName 和 Age做为键列、Password,RealName,Mobile作为非键列的非聚集索引；&lt;/li&gt;&lt;li&gt;通常来说我们系统的用户表并不是很大，所以这样的优化起不了很明显的效果，如果有兴趣的可以使用大表进行性能测试；&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;【其它】&lt;/p&gt;&lt;ol&gt;&lt;li&gt;有一点我很奇怪，那就是为什么在修改表的时候，为什么【包含的列】是不可用的？只能通过命令来编写该类索引？&lt;/li&gt;&lt;li&gt;另外一点我想说，微软的MSDN的确是最好的学习工具，在网络上搜索出来的东西很多都是重复的，而且说的不全，不过能讲的比较简单、通俗而已。所以有空还是多看看MSDN吧。这句话是对自己说的。呵呵。 &lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2507376.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/18/2507376.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/16/2504158.html</id><title type="text">lucene.net 高级应用之排序、设置权重、优化、分布式搜索</title><summary type="text">本文仅记录一些简单的使用方法，供初学者参考。以下例子采用 Lucene.NET 1.9 版本，可取去 Lucene.Net 下载。1. 基本应用using System;using System.Collections.Generic;using System.Text;using Lucene.Net;using Lucene.Net.Analysis;using Lucene.Net.Analysis.Standard;using Lucene.Net.Documents;using Lucene.Net.Index;using Lucene.Net.QueryParsers;using </summary><published>2012-05-16T08:04:00Z</published><updated>2012-05-16T08:04:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/16/2504158.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/16/2504158.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;本文仅记录一些简单的使用方法，供初学者参考。&lt;br /&gt;&lt;p&gt;以下例子采用 Lucene.NET 1.9 版本，可取去 &lt;a title="http://incubator.apache.org/lucene.net/" href="http://incubator.apache.org/lucene.net/" target="_blank"&gt;&lt;font color="#1d58d1"&gt;Lucene.Net&lt;/font&gt;&lt;/a&gt; 下载。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1. 基本应用&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;&lt;div&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using Lucene.Net;&lt;br /&gt;using Lucene.Net.Analysis;&lt;br /&gt;using Lucene.Net.Analysis.Standard;&lt;br /&gt;using Lucene.Net.Documents;&lt;br /&gt;using Lucene.Net.Index;&lt;br /&gt;using Lucene.Net.QueryParsers;&lt;br /&gt;using Lucene.Net.Search;&lt;br /&gt;using Lucene.Net.Store;&lt;br /&gt;using Lucene.Net.Util;&lt;br /&gt;&lt;br /&gt;namespace ConsoleApplication1.Lucene&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;public class LuceneTest&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private const string FieldName = "name";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private const string FieldValue = "value";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private Directory directory = new RAMDirectory();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private Analyzer analyzer = new StandardAnalyzer();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public LuceneTest()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private void Index()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IndexWriter writer = new IndexWriter(directory, analyzer, true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer.maxFieldLength = 1000;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int i = 1; i &amp;lt;= 100; i++)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Document document = new Document();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.Add(new Field(FieldName, "name" + i, Field.Store.YES, Field.Index.UN_TOKENIZED));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.Add(new Field(FieldValue, "Hello, World!", Field.Store.YES, Field.Index.TOKENIZED));&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer.AddDocument(document);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer.Optimize();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer.Close();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private void Search()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Query query = QueryParser.Parse("name*", FieldName, analyzer);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IndexSearcher searcher = new IndexSearcher(directory);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Hits hits = searcher.Search(query);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Console.WriteLine("符合条件记录:{0}; 索引库记录总数:{1}", hits.Length(), searcher.Reader.NumDocs());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int i = 0; i &amp;lt; hits.Length(); i++)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int docId = hits.Id(i);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string name = hits.Doc(i).Get(FieldName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string value = hits.Doc(i).Get(FieldValue);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;float score = hits.Score(i);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Console.WriteLine("{0}: DocId:{1}; Name:{2}; Value:{3}; Score:{4}",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i + 1, docId, name, value, score);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;searcher.Close();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/div&gt;&lt;p&gt;&lt;br /&gt;除 了 RAMDirectory，还可以使用 FSDirectory。(注意 FSDirectory.GetDirectory 的 create 参数，为 true 时将删除已有索引库文件，可以通过 IndexReader.IndexExists() 方法判断。)&lt;br /&gt;&lt;br /&gt;从指定目录打开已有索引库。&lt;/p&gt;&lt;div&gt;private Directory directory = FSDirectory.GetDirectory("c:\index", false);&lt;/div&gt;&lt;p&gt;&lt;br /&gt;将索引库载入内存，以提高搜索速度。&lt;/p&gt;&lt;div&gt;private Directory directory = new RAMDirectory(FSDirectory.GetDirectory(@"c:\index", false));&lt;br /&gt;//或&lt;br /&gt;//private Directory directory = new RAMDirectory(c:\index");&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;strong&gt;2. 多字段搜索&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;使用 MultiFieldQueryParser 可以指定多个搜索字段。&lt;br /&gt;&lt;/p&gt;&lt;div&gt;Query query = MultiFieldQueryParser.Parse("name*", new string[] { FieldName, FieldValue }, analyzer);&lt;br /&gt;&lt;br /&gt;IndexReader reader = IndexReader.Open(directory);&lt;br /&gt;IndexSearcher searcher = new IndexSearcher(reader);&lt;br /&gt;Hits hits = searcher.Search(query);&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;strong&gt;3. 多条件搜索&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;除了使用 QueryParser.Parse 分解复杂的搜索&lt;a title="http://lucene.apache.org/java/docs/queryparsersyntax.html" href="http://lucene.apache.org/java/docs/queryparsersyntax.html" target="_blank"&gt;&lt;font color="#1d58d1"&gt;语法&lt;/font&gt;&lt;/a&gt;外，还可以通过组合多个 Query 来达到目的。&lt;br /&gt;&lt;/p&gt;&lt;div&gt;Query query1 = new TermQuery(new Term(FieldValue, "name1")); // 词语搜索&lt;br /&gt;Query query2 = new WildcardQuery(new Term(FieldName, "name*")); // 通配符&lt;br /&gt;//Query query3 = new PrefixQuery(new Term(FieldName, "name1")); // 字段搜索 Field:Keyword，自动在结尾添加 *&lt;br /&gt;//Query query4 = new RangeQuery(new Term(FieldNumber, NumberTools.LongToString(11L)), new Term(FieldNumber, NumberTools.LongToString(13L)), true); // 范围搜索&lt;br /&gt;//Query query5 = new FilteredQuery(query, filter); // 带过滤条件的搜索&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;BooleanQuery query = new BooleanQuery();&lt;br /&gt;query.Add(query1, BooleanClause.Occur.MUST);&lt;br /&gt;query.Add(query2, BooleanClause.Occur.MUST);&lt;br /&gt;&lt;br /&gt;IndexSearcher searcher = new IndexSearcher(reader);&lt;br /&gt;Hits hits = searcher.Search(query);&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;strong&gt;4. 设置权重&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;可以给 Document 和 Field 增加权重(Boost)，使其在搜索结果排名更加靠前。缺省情况下，搜索结果以 Document.Score 作为排序依据，该数值越大排名越靠前。Boost 缺省值为 1。&lt;br /&gt;&lt;/p&gt;&lt;div&gt;Score = Score * Boost&lt;/div&gt;&lt;p&gt;&lt;br /&gt;通过上面的公式，我们就可以设置不同的权重来影响排名。&lt;br /&gt;&lt;br /&gt;如下面的例子中根据 VIP 级别设定不同的权重。&lt;/p&gt;&lt;div&gt;Document document = new Document();&lt;br /&gt;switch (vip)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;case VIP.Gold: document.SetBoost(2F); break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;case VIP.Argentine: document.SetBoost(1.5F); break;&lt;br /&gt;}&lt;/div&gt;&lt;p&gt;&lt;br /&gt;只要 Boost 足够大，那么就可以让某个命中结果永远排第一位，这就是百度等网站的"收费排名"业务。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5. 排序&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;通过 SortField 的构造参数，我们可以设置排序字段，排序条件，以及倒排。&lt;br /&gt;&lt;/p&gt;&lt;div&gt;Sort sort = new Sort(new SortField(FieldName, SortField.DOC, false));&lt;br /&gt;&lt;br /&gt;IndexSearcher searcher = new IndexSearcher(reader);&lt;br /&gt;Hits hits = searcher.Search(query, sort);&lt;/div&gt;&lt;p&gt;&lt;br /&gt;排序对搜索速度影响还是很大的，尽可能不要使用多个排序条件。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;6. 过滤&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;使用 Filter 对搜索结果进行过滤，可以获得更小范围内更精确的结果。&lt;br /&gt;&lt;br /&gt;举个例子，我们搜索上架时间在 2005-10-1 到 2005-10-30 之间的商品。&lt;br /&gt;对于日期时间，我们需要转换一下才能添加到索引库，同时还必须是索引字段。&lt;/p&gt;&lt;div&gt;// index&lt;br /&gt;document.Add(FieldDate, DateField.DateToString(date), Field.Store.YES, Field.Index.UN_TOKENIZED);&lt;br /&gt;&lt;br /&gt;//...&lt;br /&gt;&lt;br /&gt;// search&lt;br /&gt;Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-1"), DateTime.Parse("2005-10-30"));&lt;br /&gt;Hits hits = searcher.Search(query, filter);&lt;/div&gt;&lt;p&gt;&lt;br /&gt;除了日期时间，还可以使用整数。比如搜索价格在 100 ~ 200 之间的商品。&lt;br /&gt;Lucene.Net NumberTools 对于数字进行了补位处理，如果需要使用浮点数可以自己参考源码进行。&lt;/p&gt;&lt;div&gt;// index&lt;br /&gt;document.Add(new Field(FieldNumber, NumberTools.LongToString((long)price), Field.Store.YES, Field.Index.UN_TOKENIZED));&lt;br /&gt;&lt;br /&gt;//...&lt;br /&gt;&lt;br /&gt;// search&lt;br /&gt;Filter filter = new RangeFilter(FieldNumber, NumberTools.LongToString(100L), NumberTools.LongToString(200L), true, true);&lt;br /&gt;Hits hits = searcher.Search(query, filter);&lt;/div&gt;&lt;p&gt;&lt;br /&gt;使用 Query 作为过滤条件。&lt;/p&gt;&lt;div&gt;QueryFilter filter = new QueryFilter(QueryParser.Parse("name2", FieldValue, analyzer));&lt;/div&gt;&lt;p&gt;&lt;br /&gt;我们还可以使用 FilteredQuery 进行多条件过滤。&lt;br /&gt;&lt;/p&gt;&lt;div&gt;Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-10"), DateTime.Parse("2005-10-15"));&lt;br /&gt;Filter filter2 = new RangeFilter(FieldNumber, NumberTools.LongToString(11L), NumberTools.LongToString(13L), true, true);&lt;br /&gt;&lt;br /&gt;Query query = QueryParser.Parse("name*", FieldName, analyzer);&lt;br /&gt;query = new FilteredQuery(query, filter);&lt;br /&gt;query = new FilteredQuery(query, filter2);&lt;br /&gt;&lt;br /&gt;IndexSearcher searcher = new IndexSearcher(reader);&lt;br /&gt;Hits hits = searcher.Search(query);&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;strong&gt;7. 分布搜索&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;我们可以使用 MultiReader 或 MultiSearcher 搜索多个索引库。&lt;br /&gt;&lt;/p&gt;&lt;div&gt;MultiReader reader = new MultiReader(new IndexReader[] { IndexReader.Open(@"c:\index"), IndexReader.Open(@"\\server\index") });&lt;br /&gt;IndexSearcher searcher = new IndexSearcher(reader);&lt;br /&gt;Hits hits = searcher.Search(query);&lt;/div&gt;&lt;p&gt;&lt;br /&gt;或&lt;br /&gt;&lt;/p&gt;&lt;div&gt;IndexSearcher searcher1 = new IndexSearcher(reader1);&lt;br /&gt;IndexSearcher searcher2 = new IndexSearcher(reader2);&lt;br /&gt;MultiSearcher searcher = new MultiSearcher(new Searchable[] { searcher1, searcher2 });&lt;br /&gt;Hits hits = searcher.Search(query);&lt;/div&gt;&lt;p&gt;&lt;br /&gt;还可以使用 ParallelMultiSearcher 进行多线程并行搜索。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;8. 合并索引库&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;将 directory1 合并到 directory2 中。&lt;/p&gt;&lt;div&gt;Directory directory1 = FSDirectory.GetDirectory("index1", false);&lt;br /&gt;Directory directory2 = FSDirectory.GetDirectory("index2", false);&lt;br /&gt;&lt;br /&gt;IndexWriter writer = new IndexWriter(directory2, analyzer, false);&lt;br /&gt;writer.AddIndexes(new Directory[] { directory });&lt;br /&gt;Console.WriteLine(writer.DocCount());&lt;br /&gt;writer.Close();&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;strong&gt;9. 显示搜索语法字符串&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;我们组合了很多种搜索条件，或许想看看与其对等的搜索语法串是什么样的。&lt;/p&gt;&lt;div&gt;BooleanQuery query = new BooleanQuery();&lt;br /&gt;query.Add(query1, true, false);&lt;br /&gt;query.Add(query2, true, false);&lt;br /&gt;//...&lt;br /&gt;&lt;br /&gt;Console.WriteLine("Syntax: {0}", query.ToString());&lt;/div&gt;&lt;p&gt;&lt;br /&gt;输出：&lt;br /&gt;Syntax: +(name:name* value:name*) +number:[0000000000000000b TO 0000000000000000d]&lt;br /&gt;&lt;br /&gt;呵呵，就这么简单。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;10. 操作索引库&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;删除 (软删除，仅添加了删除标记。调用 IndexWriter.Optimize() 后真正删除。)&lt;/p&gt;&lt;div&gt;IndexReader reader = IndexReader.Open(directory);&lt;br /&gt;&lt;br /&gt;// 删除指定序号(DocId)的 Document。&lt;br /&gt;reader.Delete(123);&lt;br /&gt;&lt;br /&gt;// 删除包含指定 Term 的 Document。&lt;br /&gt;reader.Delete(new Term(FieldValue, "Hello"));&lt;br /&gt;&lt;br /&gt;// 恢复软删除。&lt;br /&gt;reader.UndeleteAll();&lt;br /&gt;&lt;br /&gt;reader.Close();&lt;/div&gt;&lt;p&gt;&lt;br /&gt;增量更新 (只需将 create 参数设为 false，即可往现有索引库添加新数据。)&lt;/p&gt;&lt;div&gt;Directory directory = FSDirectory.GetDirectory("index", false);&lt;br /&gt;IndexWriter writer = new IndexWriter(directory, analyzer, false);&lt;br /&gt;writer.AddDocument(doc1);&lt;br /&gt;writer.AddDocument(doc2);&lt;br /&gt;writer.Optimize();&lt;br /&gt;writer.Close();&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;strong&gt;11. 优化&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;批量向 FSDirectory 增加索引时，增大合并因子(mergeFactor )和最小文档合并数(minMergeDocs)有助于提高性能，减少索引时间。&lt;br /&gt;&lt;/p&gt;&lt;div&gt;IndexWriter writer = new IndexWriter(directory, analyzer, true);&lt;br /&gt;&lt;br /&gt;writer.maxFieldLength = 1000; // 字段最大长度&lt;br /&gt;writer.mergeFactor = 1000;&lt;br /&gt;writer.minMergeDocs = 1000;&lt;br /&gt;&lt;br /&gt;for (int i = 0; i &amp;lt; 10000; i++)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;// Add Documentes...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;writer.Optimize();&lt;br /&gt;writer.Close();&lt;/div&gt;&lt;p&gt;&lt;br /&gt;相关参数说明&lt;br /&gt;&lt;br /&gt;&lt;span style="color: green"&gt;&lt;br /&gt;转自《&lt;a title="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/index.html" href="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/index.html" target="_blank"&gt;&lt;font color="#1d58d1"&gt;深入 Lucene 索引机制&lt;/font&gt;&lt;/a&gt;》&lt;br /&gt;&lt;br /&gt;利 用 Lucene，在创建索引的工程中你可以充分利用机器的硬件资源来提高索引的效率。当你需要索引大量的文件时，你会注意到索引过程的瓶颈是在往磁盘上写索 引文件的过程中。为了解决这个问题, Lucene 在内存中持有一块缓冲区。但我们如何控制 Lucene 的缓冲区呢？幸运的是，Lucene 的类 IndexWriter 提供了三个参数用来调整缓冲区的大小以及往磁盘上写索引文件的频率。&lt;br /&gt;&lt;br /&gt;1．合并因子 (mergeFactor)&lt;br /&gt;&lt;br /&gt;这 个参数决定了在 Lucene 的一个索引块中可以存放多少文档以及把磁盘上的索引块合并成一个大的索引块的频率。比如，如果合并因子的值是 10，那么当内存中的文档数达到 10 的时候所有的文档都必须写到磁盘上的一个新的索引块中。并且，如果磁盘上的索引块的隔数达到 10 的话，这 10 个索引块会被合并成一个新的索引块。这个参数的默认值是 10，如果需要索引的文档数非常多的话这个值将是非常不合适的。对批处理的索引来讲，为这个参数赋一个比较大的值会得到比较好的索引效果。&lt;br /&gt;&lt;br /&gt;2．最小合并文档数 (minMergeDocs)&lt;br /&gt;&lt;br /&gt;这个参数也会影响索引的性能。它决定了内存中的文档数至少达到多少才能将它们写回磁盘。这个参数的默认值是10，如果你有足够的内存，那么将这个值尽量设的比较大一些将会显著的提高索引性能。&lt;br /&gt;&lt;br /&gt;3．最大合并文档数 (maxMergeDocs)&lt;br /&gt;&lt;br /&gt;这个参数决定了一个索引块中的最大的文档数。它的默认值是 Integer.MAX_VALUE，将这个参数设置为比较大的值可以提高索引效率和检索速度，由于该参数的默认值是整型的最大值，所以我们一般不需要改动这个参数。&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2504158.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/16/2504158.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/15/2501186.html</id><title type="text">mssql-索引视图-无法对视图创建 索引，因为该视图未绑定到架构</title><summary type="text">如题：在创建视图后创建索引提示 无法对视图创建 索引，因为该视图未绑定到架构修改此问题 需要在 创建视图语句中加上 with SCHEMABINDINGcreate View myView(id,code) with SCHEMABINDING as select id,code from dbo.mytable注意，表的表达式必须使用两段式 dbo.mytable 否则会报"名称必须由两部分构成，并且对象不能引用自身。"</summary><published>2012-05-15T03:16:00Z</published><updated>2012-05-15T03:16:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/15/2501186.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/15/2501186.html"/><content type="html">&lt;p&gt;如题：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;在创建视图后创建索引&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;提示 无法对视图创建 索引，因为该视图未绑定到架构&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;修改此问题 需要在 创建视图语句中加上&amp;nbsp; with SCHEMABINDING&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;create View&amp;nbsp; myView(id,code)&amp;nbsp; with&amp;nbsp;&amp;nbsp; SCHEMABINDING &amp;nbsp; as select id,code from dbo.mytable&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;注意，表的表达式必须使用两段式&amp;nbsp; dbo.mytable 否则会报&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;"名称必须由两部分构成，并且对象不能引用自身。"&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2501186.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/15/2501186.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/15/2501158.html</id><title type="text">can&amp;#39;t map file memory - mongo requires 64 bit build for larger datasets</title><summary type="text">处理一次32bit mongdb 数据超过2G问题can't map file memory - mongo requires 64 bit build for larger datasets 生产环境下某32bit windows机器安装了mongodb，一日数据超过2G然后，任何操作都会报错{ "assertion" : "can't map file memory - mongo requires 64 bit build for larger datasets", "assertionCode" : 10084</summary><published>2012-05-15T03:05:00Z</published><updated>2012-05-15T03:05:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/15/2501158.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/15/2501158.html"/><content type="html">&lt;p&gt;处理一次32bit mongdb 数据超过2G问题&lt;/p&gt;&lt;p&gt;&lt;span&gt;can't map file memory - mongo requires 64 bit build for larger datasets&lt;/span&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;生产环境下某32bit windows机器安装了mongodb，一日数据超过2G&lt;/p&gt;&lt;p&gt;然后，任何操作都会报错&lt;/p&gt;&lt;p&gt;&lt;span&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "assertion" : "can't map file memory - mongo requires 64 bit build for larger datasets",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "assertionCode" : 10084,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "errmsg" : "db assertion failure",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "ok" : 0&lt;br /&gt;}&lt;/span&gt; &lt;/p&gt;&lt;p&gt;解决：&lt;/p&gt;&lt;p&gt;1.在windows机器拷贝下物理文件，拷贝到64bit linux机器&lt;/p&gt;&lt;p&gt;2.用原物理文件启动一个mongo实例&lt;/p&gt;&lt;p&gt;&lt;span&gt;/usr/local/mongodb/bin/mongod --dbpath=/data1/mongodb/dataep/ --logpath=/data1/mongodb/logs/mongodbep.log --logappend --fork --port=27010 --rest --profile=1 --slowms=500&lt;/span&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;3.从新实例备份出需要的数据库&lt;/p&gt;&lt;p&gt;/usr/local/mongodb/bin/mongodump&amp;nbsp; --port 27010 -d ep -o /data1/backup/mongodbep/&amp;nbsp; &lt;/p&gt;&lt;p&gt;4. 将备份文件还原到生产环境下mongodb&lt;/p&gt;&lt;p&gt;/usr/local/mongodb/bin/mongorestore&amp;nbsp; --host 172.21.5.49 --port 21017 -d ep /data1/backup/mongodbep/ep/&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2501158.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/15/2501158.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/14/2498982.html</id><title type="text">群发“站内信”的实现</title><summary type="text">在很多网站系统（如CMS系统，SNS系统等），都有“站内信”的功能。 “站内信”不同于电子邮件，电子邮件通过专门的邮件服务器发送、保存。而“站内信”是系统内的消息，说白了，“站内信”的实现，就是通过数据库插入记录来实现的。 “站内信”有两个基本功能。一：点到点的消息传送。用户给用户发送站内信；管理员给用户发送站内信。二：点到面的消息传送。管理员给用户（指定满足某一条件的用户群）群发消息。点到点的消息传送很容易实现，本文不再详述。下面将根据不同的情况，来说说“站内信”的群发是如何实现的。 第一种情况，站内的用户是少量级别的。（几十到上百） 这种情况，由于用户的数量非常少，因此，没有必...</summary><published>2012-05-14T02:46:00Z</published><updated>2012-05-14T02:46:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/14/2498982.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/14/2498982.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;在很多网站系统（如CMS系统，SNS系统等），都有&amp;#8220;站内信&amp;#8221;的功能。&lt;/p&gt;&lt;p&gt;&amp;#8220;站内信&amp;#8221;不同于电子邮件，电子邮件通过专门的邮件服务器发送、保存。而&amp;#8220;站内信&amp;#8221;是系统内的消息，说白了，&amp;#8220;站内信&amp;#8221;的实现，就是通过数据库插入记录来实现的。&lt;/p&gt;&lt;p&gt;&amp;#8220;站内信&amp;#8221;有两个基本功能。一：点到点的消息传送。用户给用户发送站内信；管理员给用户发送站内信。二：点到面的消息传送。管理员给用户（指定满足某一条件的用户群）群发消息。点到点的消息传送很容易实现，本文不再详述。下面将根据不同的情况，来说说&amp;#8220;站内信&amp;#8221;的群发是如何实现的。&lt;/p&gt;&lt;p&gt;第一种情况，站内的用户是少量级别的。（几十到上百）&lt;/p&gt;&lt;p&gt;这种情况，由于用户的数量非常少，因此，没有必要过多的考虑数据库的优化，采用简单的表格，对系统的设计也来的简单，后期也比较容易维护，是典型的用空间换时间的做法。&lt;/p&gt;&lt;p&gt;数据库的设计如下：表名：Message&lt;/p&gt;&lt;p&gt;ID：编号；SendID：发送者编号；RecID：接受者编号（如为0，则接受者为所有人）；Message：站内信内容；Statue：站内信的查看状态；PDate：站内信发送时间；&lt;/p&gt;&lt;p&gt;如果，某一个管理员要给所有人发站内信，则先遍历用户表，再按照用户表中的所有用户依次将站内信插入到Message表中。这样，如果有56个用户，则群发一条站内信要执行56个插入操作。这个理解上比较简单，比较耗损空间。&lt;/p&gt;&lt;p&gt;某一个用户登陆后，查看站内信的语句则为：&lt;/p&gt;&lt;p&gt;Select * FROM Message Where RecID=&amp;#8216;ID&amp;#8217; OR RecID=0&lt;/p&gt;&lt;p&gt;第二种情况，站内的用户中量级别的（上千到上万）。&lt;/p&gt;&lt;p&gt;如果还是按照第一种情况的思路。那发一条站内信的后果基本上就是后台崩溃了。因为，发一条站内信，得重复上千个插入记录，这还不是最主要的，关键是上千乃至上万条记录，Message字段的内容是一样的，而Message有大量的占用存储空间。比方说，Message字段有100个汉字，占用200个字节，那么5万条，就占用200&amp;#215;50000=10000000个字节=10M。简单的一份站内信，就占用10M，这还让不让人活了。&lt;/p&gt;&lt;p&gt;因此，将原先的表格拆分为两个表，将Message的主体放在一个表内，节省空间的占用&lt;/p&gt;&lt;p&gt;数据库的设计如下：&lt;/p&gt;&lt;p&gt;表名：Message&lt;/p&gt;&lt;p&gt;ID：编号；SendID：发送者编号；RecID：接受者编号（如为0，则接受者为所有人）；MessageID：站内信编号；Statue：站内信的查看状态；&lt;/p&gt;&lt;p&gt;表名：MessageText　&lt;/p&gt;&lt;p&gt;ID：编号；Message：站内信的内容；PDate：站内信发送时间；&lt;/p&gt;&lt;p&gt;在管理员发一封站内信的时候，执行两步操作。先在MessageText表中，插入站内信的内容。然后在Message表中给所有的用户插入一条记录，标识有一封站内信。&lt;/p&gt;&lt;p&gt;这样的设计，将重复的站内信的主体信息（站内信的内容，发送时间）放在一个表内，大量的节省存储空间。不过，在查询的时候，要比第一种情况来的复杂。&lt;/p&gt;&lt;p&gt;第三种情况，站内的用户是大量级的（上百万），并且活跃的用户只占其中的一部分。&lt;/p&gt;&lt;p&gt;大家都有这样的经历，某日看一个网站比较好，一时心情澎湃，就注册了一个用户。过了一段时间，由于种种原因，就忘记了注册时的用户名和密码，也就不再登陆了。那么这个用户就称为不活跃的。从实际来看，不活跃的用户占着不小的比例。&lt;/p&gt;&lt;p&gt;我们以注册用户2百万，其中活跃用户只占其中的10%。&lt;/p&gt;&lt;p&gt;就算是按照第二种的情况，发一封&amp;#8220;站内信&amp;#8221;，那得执行2百万个插入操作。但是其中的有效操作只有10%，因为另外的90%的用户可能永远都不会再登陆了。&lt;/p&gt;&lt;p&gt;在这种情况下，我们还得把思路换换。&lt;/p&gt;&lt;p&gt;数据库的设计和第二种情况一样：&lt;/p&gt;&lt;p&gt;表名：Message&lt;/p&gt;&lt;p&gt;ID：编号；SendID：发送者编号；RecID：接受者编号（如为0，则接受者为所有人）；MessageID：站内信编号；Statue：站内信的查看状态；&lt;/p&gt;&lt;p&gt;表名：MessageText　&lt;/p&gt;&lt;p&gt;ID：编号；Message：站内信的内容；PDate：站内信发送时间；&lt;/p&gt;&lt;p&gt;管理员发站内信的时候，只在MessageText插入站内信的主体内容。Message里不插入记录。&lt;/p&gt;&lt;p&gt;那么，用户在登录以后，首先查询MessageText中的那些没有在Message中有记录的记录，表示是未读的站内信。在查阅站内信的内容时，再将相关的记录插入到Message中。&lt;/p&gt;&lt;p&gt;这个方法和第二种的比较起来。如果，活跃用户是100%。两者效率是一样的。而活跃用户的比例越低，越能体现第三种的优越来。只插入有效的记录，那些不活跃的，就不再占用空间了。&lt;/p&gt;&lt;p&gt;以上，是我对群发&amp;#8220;站内信&amp;#8221;的实现的想法。也欢迎各位提出自己的建议，大家互相借鉴，共同进步。&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;&lt;div&gt;作者：&lt;a href="http://grenet.cnblogs.com/" target="_blank"&gt;&lt;font color="#075db3"&gt;万仓一黍&lt;/font&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;出处：&lt;a href="http://grenet.cnblogs.com/" target="_blank"&gt;&lt;font color="#075db3"&gt;http://grenet.cnblogs.com/&lt;/font&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;本文版权归作者和博客园共有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。 &lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2498982.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/14/2498982.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/14/2498852.html</id><title type="text">一个站内短信的数据库设计</title><summary type="text">先说一下需求和环境： 一个系统的站内信模块，有存在大量的按部门群发的可能，相对的个人对个人的群发是比较少的。 数据库是采用的mysql5.0。最先的数据库设计如下： 两张表： 一张Msg表，字段如下： id int 自增长id senderid int 外键关联发送者id title varchar(128) 短信标题 content varchar(512) 短信内容 createTime datatime 发信时间 status tinyint 发件箱中的状态：0--普通；1--删除 一张user_has_msg表，字段如下： id int departmentid int 部门群发的时候</summary><published>2012-05-14T01:18:00Z</published><updated>2012-05-14T01:18:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/14/2498852.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/14/2498852.html"/><content type="html">先说一下需求和环境：&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一个系统的站内信模块，有存在大量的按部门群发的可能，相对的个人对个人的群发是比较少的。&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 数据库是采用的mysql5.0。&amp;nbsp;&lt;br /&gt;&lt;br /&gt;最先的数据库设计如下：&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 两张表：&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一张Msg表，字段如下：&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; id&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 自增长id&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; senderid&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 外键关联发送者id&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; title&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; varchar(128)&amp;nbsp;&amp;nbsp; 短信标题&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; content&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; varchar(512)&amp;nbsp;&amp;nbsp; 短信内容&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; createTime&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; datatime&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 发信时间&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; status&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tinyint&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 发件箱中的状态：0--普通；1--删除&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一张user_has_msg表，字段如下：&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; id&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; departmentid&amp;nbsp;&amp;nbsp; int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 部门群发的时候外键关联部门id，可以为空&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; receverid&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 外键关联收信人，可以为空&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; msgid&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 外键关联短信息&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; status&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tinyint&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 收件箱状态：0--普通；1--删除&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; readStatus&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tinyint&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 阅读状态：0--未读；1--已读&amp;nbsp;&lt;br /&gt;这样设计是基于如下考虑的：&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 首先，msg表包含了发件箱所需要的所有信息，程序的时候写发件箱的时候可以只考虑操作一张数据库表。&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第二，user_has_msg中，departmentid主要考虑的是存在大量的按照部门群发的可能，这样的话，群发给一个部门的时候之需要在两张表上个记录一条数据，而不需要在user_has_msg中记录该部门员工数条记录。&amp;nbsp;&lt;br /&gt;&lt;br /&gt;但是，后来这个方案被我自己和同事讨论后否决了，原因如下：&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 首先,departmentid的存在使得没有用户可以删除收件箱中的站内信，因为删除了，其他人的收件箱里也看不到。&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第二，msg表不能保证显示完所有的发件箱所需要的数据，因为只有着一张表的是后读不出来收件人信息。&amp;nbsp;&lt;br /&gt;&lt;br /&gt;修改后的版本是：&amp;nbsp;&lt;br /&gt;将msg修改为只保纯粹的信息的表：&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; id&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 自增长id&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; title&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; varchar(128)&amp;nbsp;&amp;nbsp; 短信标题&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; content&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; varchar(512)&amp;nbsp;&amp;nbsp; 短信内容&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; createTime&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; datatime&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 发信时间&amp;nbsp;&lt;br /&gt;将user_has_msg修改为保存各种关系和状态的表：&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; id&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; senderid&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 外键关联发送者id&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; receverid&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 外键关联收信人&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; msgid&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 外键关联短信息&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sendStatus&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tinyint&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 发件箱中的状态：0--普通；1--删除&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; receveStatus&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tinyint&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 收件箱状态：0--普通；1--删除&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; readStatus&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tinyint&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 阅读状态：0--未读；1--已读&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;我记录这个的想法一方面想记录我自己的一些积累，另一方面是想像在网上找到更好的设计方法，尤其是解决群发这个问题。&amp;nbsp;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2498852.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/14/2498852.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/cxd4321/archive/2012/05/07/2487388.html</id><title type="text">在C#中使用SqlDbType.Xml类型参数</title><summary type="text">在sql server2005以及之后的sql server中引入了Xml数据类型，在C#中使用Xml数据类型需要指定参数类型为SqlDbType，参数值类型需要用SqlXml，如下示例：假定有一种表A，A表有两个字段：ID 类型 int，Data 类型 Xml，我要用C#往表中插入一行记录：?12345678910111213141516171819202122232425static void InsertA(int aid, string contentXml) { //ConnString是连接字符串，需要额外定义 using (SqlConnection conn = new Sql</summary><published>2012-05-07T06:43:00Z</published><updated>2012-05-07T06:43:00Z</updated><author><name>剑飘红</name><uri>http://www.cnblogs.com/cxd4321/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cxd4321/archive/2012/05/07/2487388.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cxd4321/archive/2012/05/07/2487388.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;在sql server2005以及之后的sql server中引入了Xml数据类型，在C#中使用Xml数据类型需要指定参数类型为SqlDbType，参数值类型需要用SqlXml，如下示例：&lt;/p&gt;&lt;p&gt;假定有一种表A，A表有两个字段：ID 类型 int，Data 类型 Xml，我要用C#往表中插入一行记录：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;div id="highlighter_260419" class="syntaxhighlighter  csharp ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="http://www.cnblogs.com/yukaizhao/archive/2011/08/17/sqlxml_sqldbtype_xml.html#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;div class="line number2 index1 alt1"&gt;2&lt;/div&gt;&lt;div class="line number3 index2 alt2"&gt;3&lt;/div&gt;&lt;div class="line number4 index3 alt1"&gt;4&lt;/div&gt;&lt;div class="line number5 index4 alt2"&gt;5&lt;/div&gt;&lt;div class="line number6 index5 alt1"&gt;6&lt;/div&gt;&lt;div class="line number7 index6 alt2"&gt;7&lt;/div&gt;&lt;div class="line number8 index7 alt1"&gt;8&lt;/div&gt;&lt;div class="line number9 index8 alt2"&gt;9&lt;/div&gt;&lt;div class="line number10 index9 alt1"&gt;10&lt;/div&gt;&lt;div class="line number11 index10 alt2"&gt;11&lt;/div&gt;&lt;div class="line number12 index11 alt1"&gt;12&lt;/div&gt;&lt;div class="line number13 index12 alt2"&gt;13&lt;/div&gt;&lt;div class="line number14 index13 alt1"&gt;14&lt;/div&gt;&lt;div class="line number15 index14 alt2"&gt;15&lt;/div&gt;&lt;div class="line number16 index15 alt1"&gt;16&lt;/div&gt;&lt;div class="line number17 index16 alt2"&gt;17&lt;/div&gt;&lt;div class="line number18 index17 alt1"&gt;18&lt;/div&gt;&lt;div class="line number19 index18 alt2"&gt;19&lt;/div&gt;&lt;div class="line number20 index19 alt1"&gt;20&lt;/div&gt;&lt;div class="line number21 index20 alt2"&gt;21&lt;/div&gt;&lt;div class="line number22 index21 alt1"&gt;22&lt;/div&gt;&lt;div class="line number23 index22 alt2"&gt;23&lt;/div&gt;&lt;div class="line number24 index23 alt1"&gt;24&lt;/div&gt;&lt;div class="line number25 index24 alt2"&gt;25&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;static&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;InsertA(&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;aid, &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;contentXml) &lt;/code&gt;&lt;/div&gt;&lt;div class="line number2 index1 alt1"&gt;&lt;code class="csharp plain"&gt;{ &lt;/code&gt;&lt;/div&gt;&lt;div class="line number3 index2 alt2"&gt;&lt;code class="csharp comments"&gt;&lt;font color="#008200"&gt;//ConnString是连接字符串，需要额外定义 &lt;/font&gt;&lt;/code&gt;&lt;/div&gt;&lt;div class="line number4 index3 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;using&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;(SqlConnection conn = &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;SqlConnection(ConnString)) &lt;/code&gt;&lt;/div&gt;&lt;div class="line number5 index4 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;{ &lt;/code&gt;&lt;/div&gt;&lt;div class="line number6 index5 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;conn.Open(); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number7 index6 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;sql = &lt;/code&gt;&lt;code class="csharp string"&gt;&lt;font color="#0000ff"&gt;"INSERT INTO [A] ([ID],[Content])VALUES(@id,@content)"&lt;/font&gt;&lt;/code&gt;&lt;code class="csharp plain"&gt;; &lt;/code&gt;&lt;/div&gt;&lt;div class="line number8 index7 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;using&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;(SqlCommand comm = &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;SqlCommand(sql, conn)) &lt;/code&gt;&lt;/div&gt;&lt;div class="line number9 index8 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;{ &lt;/code&gt;&lt;/div&gt;&lt;div class="line number10 index9 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;using&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;(XmlTextReader rdr = &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;XmlTextReader(contentXml, XmlNodeType.Document, &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;null&lt;/font&gt;&lt;/code&gt;&lt;code class="csharp plain"&gt;)) &lt;/code&gt;&lt;/div&gt;&lt;div class="line number11 index10 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;{ &lt;/code&gt;&lt;/div&gt;&lt;div class="line number12 index11 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;SqlXml sqlXml = &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;SqlXml(rdr); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number13 index12 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&lt;/code&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="line number14 index13 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;SqlParameter parmID = &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;SqlParameter(&lt;/code&gt;&lt;code class="csharp string"&gt;&lt;font color="#0000ff"&gt;"@id"&lt;/font&gt;&lt;/code&gt;&lt;code class="csharp plain"&gt;, aid); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number15 index14 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;SqlParameter parmContent = &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;SqlParameter(&lt;/code&gt;&lt;code class="csharp string"&gt;&lt;font color="#0000ff"&gt;"@content"&lt;/font&gt;&lt;/code&gt;&lt;code class="csharp plain"&gt;, SqlDbType.Xml, sqlXml.Value.Length); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number16 index15 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;parmContent.Value = sqlXml; &lt;/code&gt;&lt;/div&gt;&lt;div class="line number17 index16 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&lt;/code&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="line number18 index17 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;comm.Parameters.Add(parmID); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number19 index18 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;comm.Parameters.Add(parmContent); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number20 index19 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;comm.ExecuteNonQuery(); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number21 index20 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;} &lt;/code&gt;&lt;/div&gt;&lt;div class="line number22 index21 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;} &lt;/code&gt;&lt;/div&gt;&lt;div class="line number23 index22 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;conn.Close(); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number24 index23 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;} &lt;/code&gt;&lt;/div&gt;&lt;div class="line number25 index24 alt2"&gt;&lt;code class="csharp plain"&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;插入数据时需要用SqlXml数据类型作为参数值，但读出Xml类型数据时的C#数据类型是string。如下示例：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;div id="highlighter_13609" class="syntaxhighlighter  csharp ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="http://www.cnblogs.com/yukaizhao/archive/2011/08/17/sqlxml_sqldbtype_xml.html#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;div class="line number2 index1 alt1"&gt;2&lt;/div&gt;&lt;div class="line number3 index2 alt2"&gt;3&lt;/div&gt;&lt;div class="line number4 index3 alt1"&gt;4&lt;/div&gt;&lt;div class="line number5 index4 alt2"&gt;5&lt;/div&gt;&lt;div class="line number6 index5 alt1"&gt;6&lt;/div&gt;&lt;div class="line number7 index6 alt2"&gt;7&lt;/div&gt;&lt;div class="line number8 index7 alt1"&gt;8&lt;/div&gt;&lt;div class="line number9 index8 alt2"&gt;9&lt;/div&gt;&lt;div class="line number10 index9 alt1"&gt;10&lt;/div&gt;&lt;div class="line number11 index10 alt2"&gt;11&lt;/div&gt;&lt;div class="line number12 index11 alt1"&gt;12&lt;/div&gt;&lt;div class="line number13 index12 alt2"&gt;13&lt;/div&gt;&lt;div class="line number14 index13 alt1"&gt;14&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;GetContent(&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;id) &lt;/code&gt;&lt;/div&gt;&lt;div class="line number2 index1 alt1"&gt;&lt;code class="csharp plain"&gt;{ &lt;/code&gt;&lt;/div&gt;&lt;div class="line number3 index2 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;sql = &lt;/code&gt;&lt;code class="csharp string"&gt;&lt;font color="#0000ff"&gt;"SELECT [Content] FROM [A] WHERE [ID] = "&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;+ id; &lt;/code&gt;&lt;/div&gt;&lt;div class="line number4 index3 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&lt;/code&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="line number5 index4 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;using&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;(SqlConnection conn = &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;SqlConnection(ConnString)) &lt;/code&gt;&lt;/div&gt;&lt;div class="line number6 index5 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;{ &lt;/code&gt;&lt;/div&gt;&lt;div class="line number7 index6 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;conn.Open(); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number8 index7 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;using&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;(SqlCommand comm = &lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;SqlCommand(sql,conn)) &lt;/code&gt;&lt;/div&gt;&lt;div class="line number9 index8 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;{ &lt;/code&gt;&lt;/div&gt;&lt;div class="line number10 index9 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;xml = (&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/code&gt;&lt;code class="csharp plain"&gt;)comm.ExecuteScalar(); &lt;/code&gt;&lt;/div&gt;&lt;div class="line number11 index10 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp keyword"&gt;&lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;/code&gt; &lt;code class="csharp plain"&gt;xml; &lt;/code&gt;&lt;/div&gt;&lt;div class="line number12 index11 alt1"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;} &lt;/code&gt;&lt;/div&gt;&lt;div class="line number13 index12 alt2"&gt;&lt;code class="csharp spaces"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/code&gt;&lt;code class="csharp plain"&gt;} &lt;/code&gt;&lt;/div&gt;&lt;div class="line number14 index13 alt1"&gt;&lt;code class="csharp plain"&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;需要注意的是，在插入数据时Xml字段的参数值类型不可以为string，直接用string会报编码错误异常。&lt;/p&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/cxd4321/aggbug/2487388.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cxd4321/archive/2012/05/07/2487388.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
