星星之火

燎原之势不可挡
posts - 127, comments - 374, trackbacks - 0, articles - 3
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

MongoDB学习总结(二)

Posted on 2017-03-23 23:07  星星之火116  阅读(...)  评论(...编辑  收藏

            前言:学习札记!

 

MongoDB学习总结(二)

1.  安装、初识

 

之前写过一篇MongoDB的快速上手文章,里边详细的讲了如何安装、启动MongoDB,这里就不再累述安装过程,简单介绍一下Mongodb的基本操作。

打开命令行窗口,输入“mongo”命令,默认会连接到test数据库。

l  Insert

db.person.insert({"name":"Olive","age":18})

db.person.insert({"name":"Momo","age":17})

l  find

db.person.find()//查找所有的person数据

{ "_id" :ObjectId("58a03991d57e6773c574e485"), "name" :"Olive", "age" : 18 }

{ "_id" :ObjectId("58a039a8d57e6773c574e486"), "name" :"Momo", "age" : 17 }

db.person.find({"name":"Olive"})//查找名为Olive的person数据

{ "_id" :ObjectId("58a03991d57e6773c574e485"), "name" :"Olive", "age" : 18 }

u  "$gt","$gte", "$lt", "$lte", "$ne", "没有特殊关键字"(对应的为>,>=,<,<=,!=)

db.user.find({"age":{$gt:22}})//年龄大于22

{ "_id" : ObjectId("58a03dc7d57e6773c574e488"),"name" : "joe", "password" : "123456","age" : 25, "address" : { "province" :"henan", "city" : "zhengzhou" },"favourite" : [ "money", "girl" ] }

db.user.find({"age":{$lt:22}})//年龄小于22

{ "_id" :ObjectId("58a03cc0d57e6773c574e487"), "name" :"jack", "password" : "123456", "age" :20, "address" : { "province" : "beijing","city" : "chaoyang" }, "favourite" : ["apple", "banana" ] }

db.user.find({"age":{$ne:25}})//年龄不等于25

{ "_id" :ObjectId("58a03cc0d57e6773c574e487"), "name" :"jack", "password" : "123456", "age" :20, "address" : { "province" : "beijing","city" : "chaoyang" }, "favourite" : ["apple", "banana" ] }

u  "无关键字“, "$or", "$in","$nin"

db.user.find({$or:[{"name":"jack"},{"age":25}]})//名字为jack或age为25的user

{ "_id" :ObjectId("58a03cc0d57e6773c574e487"), "name" :"jack", "password" : "123456", "age" :20, "address" : { "province" : "beijing","city" : "chaoyang" }, "favourite" : ["apple", "banana" ] }

{ "_id" :ObjectId("58a03dc7d57e6773c574e488"), "name" :"joe", "password" : "123456", "age" :25, "address" : { "province" : "henan","city" : "zhengzhou" }, "favourite" : ["money", "girl" ] }

db.user.find({"address.city":{$in:["chaoyang","zhengzhou"]}})//城市为chaoyang或zhengzhou的user

{ "_id" :ObjectId("58a03cc0d57e6773c574e487"), "name" :"jack", "password" : "123456", "age" :20, "address" : { "province" : "beijing","city" : "chaoyang" }, "favourite" : ["apple", "banana" ] }

{ "_id" :ObjectId("58a03dc7d57e6773c574e488"), "name" :"joe", "password" : "123456", "age" :25, "address" : { "province" : "henan","city" : "zhengzhou" }, "favourite" : ["money", "girl" ] }

db.user.find({"address.city":{$nin:["chaoyang1","zhengzhou"]}})//查找城市不是chaoyang1或不是zhengzhou的user

{ "_id" :ObjectId("58a03cc0d57e6773c574e487"), "name" :"jack", "password" : "123456", "age" :20, "address" : { "province" : "beijing","city" : "chaoyang" }, "favourite" : ["apple", "banana" ] }

u  正则表达式匹配

db.user.find({$or:[{"name":/^j/},{"name":/e$/}]})//匹配名字以j开头或者名字以e结尾的user

{ "_id" :ObjectId("58a03cc0d57e6773c574e487"), "name" :"jack", "password" : "123456", "age" :20, "address" : { "province" : "beijing","city" : "chaoyang" }, "favourite" : ["apple", "banana" ] }

{ "_id" :ObjectId("58a03dc7d57e6773c574e488"), "name" :"joe", "password" : "123456", "age" :25, "address" : { "province" : "henan","city" : "zhengzhou" }, "favourite" : ["money", "girl" ] }

u  $where

db.user.find({$where:function(){return this.name=="joe"}})//用$where的方式查询名为joe的user

{ "_id" :ObjectId("58a03dc7d57e6773c574e488"), "name" :"joe", "password" : "123456", "age" :25, "address" : { "province" : "henan","city" : "zhengzhou" }, "favourite" : ["money", "girl" ] }

 

l  Update

db.person.update({"name":"Olive"},{"name":"Olive116","age":19})//更新名为Olive的person信息

u  $inc 修改器(自增$inc指定的值,如果“文档”中没有此key,则会创建key,局部修改)

db.user.update({"name":"jack"},{$inc:{"age":10}})

u  $set修改器(局部修改age值)

db.user.update({"name":"jack"},{$set:{"age":10}})

u upsert操作(如果没有查到,就在数据库里面新增一条)

db.user.update({"name":"jackson"},{$inc:{"age":1}},true)//更新名为jackson的age值,如果该user不存在,则新增一条user信息

u  批量更新

db.user.update({“name”:“hxf”},{$inc:{“age”:10}},true)//批量更新名为hxf的user

l  remove

db.person.remove({"name":"Momo"})//移除名为Momo的person信息

2.  聚合

2.1count

db.user.count({"age":25})//统计age为25的user个数

2.2distinct

db.user.distinct("age")

2.3group

db.user.group({"key":{"age":true},"initial":{"person":[]},"$reduce":function(cur,prev){prev.person.push(cur.name);}})

key:  这个就是分组的key,我们这里是对年龄分组。

initial: 每组都分享一个”初始化函数“,特别注意:是每一组,比如这个的age=20的value的list分享一个initial函数,age=22同样也分享一个initial函数。

$reduce: 这个函数的第一个参数是当前的文档对象,第二个参数是上一次function操作的累计对象,第一次为initial中的{”perosn“:[]}。有多少个文档, $reduce就会调用多少次。

结果:

[

        {

                "age" : 10,

                "person" : [

                        "jack"

                ]

        },

        {

                "age" : 25,

                "person" : [

                        "joe"

                ]

        },

        {

                "age" : 1,

                "person" : [

                        "jackson"

                ]

        }

]

db.user.group({"key":{"age":true},"initial":{"person":[]},"reduce":function(doc,out){out.person.push(doc.name);},"finallize":function(out){out.count=out.person.length;},"condition":{"age":{$lt:25}}})

user数组里面的人员太多,想加上一个count属性标明一下。

 针对上面的需求,在group里面还是很好办到的,因为group有这么两个可选参数: condition 和 finalize。

condition:  这个就是过滤条件。

finalize:这是个函数,每一组文档执行完后,多会触发此方法

结果:

[

        {

                "age" : 10,

                "person" : [

                        "jack"

                ]

        },

        {

                "age" : 1,

                "person" : [

                        "jackson"

                ]

        }

]

2.4mapReduce

mapReduce其实是一种编程模型,用在分布式计算中,其中有一个“map”函数,一个”reduce“函数。

l  map:

         这个称为映射函数,里面会调用emit(key,value),集合会按照你指定的key进行映射分组。

l  reduce:

        这个称为简化函数,会对map分组后的数据进行分组简化,注意:在reduce(key,value)中的key就是

     emit中的key,vlaue为emit分组后的emit(value)的集合,这里也就是很多{"count":1}的数组。

l  mapReduce:

         这个就是最后执行的函数,参数为map,reduce和一些可选参数

mapfunction(){ emit(this.name,{count:1});

reducefunction(key,value){ var result={count:0}; for(var i=0;i<value.length;i++){result.count+=value[i].count;}return result;}

db.user.mapReduce(map,reduce,{“output”:“collection”});

3.  游标

Mongodb里的游标是申明一个查询结构,并没有出具体的数据,只有在遍历时才加载过来,通过游标读取,枚举完成之后销毁游标。

Varlist=db.user.find()

list.Foreach(function(item){print(item.name);});

同时在构造查询时,还可以根据需要进行复杂的查询构造,例如:分页、排序等

Varlist=db.user.find().sort({“name”,1}).skip(3).limit(3);

4.  索引

4.1 性能分析函数(explain)

在数据库中插入10万条数据,如下:

for(vari=0;i<1000000;i++) {

...var rand=parseInt(i*Math.random());

...db.user.insert({"name":"HXF"+i,"age":i})

... }

查找一条数据,并利用“explain”分析函数,进行查询分析。

db.user.find({"name":"HXF9999"}).explain()

{

        "queryPlanner" : {

                "plannerVersion" : 1,

                "namespace" :"test.user",

                "indexFilterSet" :false,

                "parsedQuery" : {

                        "name" : {

                                "$eq": "HXF9999"

                        }

                },

                "winningPlan" : {

                        "stage" :"COLLSCAN",

                        "filter" : {

                               "name" : {

                                       "$eq" : "HXF9999"

                                }

                        },

                        "direction" :"forward"

                },

                "rejectedPlans" : [ ]

        },

        "serverInfo" : {

                "host" :"WIN-GJ07N56QAK7",

                "port" : 27017,

                "version" :"3.0.6",

                "gitVersion" :"1ef45a23a4c5e3480ac919b28afcba3c615488f2"

        },

        "ok" : 1

}

4.2 建立索引(ensureIndex)

db.user.ensureIndex({“name”:1});

利用ensureIndex方法为name字段添加索引,“1”表示照name升序,“-1”表示照name降序。

db.user.find({name:“墨遥”}).explain();

4.3 唯一索引

db.user.ensureIndex({"name":1},{"unique":true})

创建唯一索引,重复的键就不能再插入。

4.4 组合索引

多条件查询时,可以通过创建组合索引来加速查询。

db.user.ensureIndex({"name":1,"birthday":1})

创建组合索引,按照name升序,birthday升序。升序和降序的不同都会产生不同的索引。

我们可以通过getindexes来查看user下创建的所有索引。

db.user.getIndexes();

查询优化器在做查询时,会使用我们建立的这些索引来创建查询方案,如果某一个先执行完则其他查询方案被close掉,这种方案会被mongodb保存起来,当然如果非要用自己指定的查询方案,这也是可以的,在mongodb中给我们提供了hint方法让我们可以暴力执行。

db.user.find({name:‘HXF’,age:27}).hint({age:1,name:1});

4.5 删除索引

db.user.dropIndex(“name_1”)

5.  主从复制

Mongodb主从复制的部署架构可以实现数据的备份、数据恢复、读写分离。

部署实践:

在一台服务器上启动Mongodb并将该数据库指定为主数据库,命令如下:

mongod --dbpath D:\MangoDB\Data –master

在另一台服务器上启动mongodb并将该数据库指定为从数据库,命令如下:

mongod –dbpath E:\MongoDB\Data –-slave –source=主服务器IP:27017

动态的添加从属服务器:

在新增的mongodb服务器上,使用local数据库,并在sources中添加一个host

地址,如下:

在新的mongodb服务器上启动mongodb数据库,

use local

db.sources.insert({“host”:”主服务器Ip:端口”});//127.0.0.1:27017

l  读写分离

在从属服务器中,执行rs.slaveOk()即可支持从从属数据库读取信息

6.  副本集

副本集也是属于主从集群,但是跟上边的集群有区别的。

l  副本集的集群没有特定的主数据库

l  如果某个主数据库宕机了,集群会自动推选一个从属数据库作为主数据库顶上,具备了自动故障恢复功能。

实践如下:

创建集群,启动D盘的mongodb程序,指定端口2222,,其中集群的名字为HXFX,--replSet表示告知服务器HXFX集群下还有其他的数据库(即指定的端口3333的数据库)

mongod--dbpath D:\MongoDB\Data --port 2222 --replSet HXFX/127.0.0.1:3333

打开端口为3333的数据库

mongod--dbpath D:\SubMongoDB\Data --port 3333 --replSet HXFX/127.0.0.1:2222

 

连接到任意一台服务器,并以admin登录数据库,进行副本集的初始化,命令如下:

mongo127.0.0.1:2222/admin

 

rs.runCommand({"replSetInitiate":{"_id":"HXFX"},"members":[{"_id":1,"host":"127.0.0.1:2222"},{"_id":2,"host":"127.0.0.1:3333"}]}})

 

新增一台服务器,作为仲裁服务器,如下:

mongod--dbpath D:\ThreeMongoDB\Data --port 4444 --replSet HXFX/127.0.0.3:2222

 

然后我们在admin集合中使用rs.addArb()追加即可。如下:

rs.addArb("127.0.0.1:4444")

 

7.  分片

当数据量达到T级别的时候,mongodb采用将集合进行拆分,将拆分的数据分摊到几个片上。我们要了解”片键“的概念,也就是说拆分集合的依据是,按照键值进行拆分集合。这里需要一个路由服务器(mongos),根据管理员设置的“片键”将数据分摊到自己管理的mongod集群,同时需要一个config服务器,用来保存数据和片的对应关系以及相应的配置信息。同时还需要若干的mongodb服务器。具体实践如下:

7.1 开启config服务器

Config服务主要用来存储数据和片的对应关系,所应该最先开启。

--开启Config服务器

mongod --dbpath D:\SubMongoDB\Data --port 2222

7.2 开启mongos服务器

Mongos服务器就是一个路由服务器,同时要为其指定config服务器

--开启mongos服务器

mongos --port 3333 --configdb 127.0.0.1:2222

7.3 添加mongodb服务器(也就是要添加的片)

--启动mongod服务器(4444,5555)

mongod --dbpath D:\ThreeMongoDB\Data --port 4444

 

mongod --dbpath D:\FourMongoDB\Data --port 5555

7.4 连接到mongos服务器并将mongodb服务添加分片

--连接到mongods服务器并将(4444,5555)服务器添加分片

mongo 127.0.0.1:3333/admin

 

db.runCommand({"addshard":"127.0.0.1:4444",allowLocal:true})

 

db.runCommand({"addshard":"127.0.0.1:5555",allowLocal:true})

 

7.5 mongos服务器设置片键切分数据

--开启数据库的分片功能

mongo 127.0.0.1:3333/admin

db.runCommand({"enablesharding":"test"})—为test数据开启分片功能

--指定集合中分片的键

db.runCommand({"shardcollection":"test.user","key":{"name":1}})—指定test数据库中user集合的name为片键

7.6 插入数据,查看效果

通过mongos向mongodb中插入10w数据,并通过db.printShardingStatus()来查看效果

 

 

插入的1万条数据分布在4444和5555服务器上。

8.  安装部署

Mongod –dbpath D:\MongoDB\Data –-logpath D:\MongoDB\Log\log.txt–port 2222 –install

设置MongoDB数据存储路径,日志路径,同时开启了安装服务寄宿。

9.  状态监控

通过db.serverStatus()来查看服务器的统计信息(全局锁、索引、用户操作行为等)

通过mongostat实时刷新,观看数据实时变化。

Mongostat –port 3333

10.          安全认证

Use admin

Db.system.users.remove({“user”:”XXX”})

 

11.          备份和恢复

Mongodump和mongorestore内置工具,保证不关闭服务器仍能copy数据。使用如下:

Mongodump -–port2222 –d test –o D:\MongoDB\Backup

将test数据库备份到D盘的mongoDB文件夹下的Backup文件夹下

Mongorestore–-port 2222 –d test –-drop  D:\MongoDB\Backup\test

将D:\MongoDB\Backup\test还原到test数据库,并删除备份文件

通过加锁和释放锁的方式保证数据,能够全部的备份下来

 db.runCommand({"fsync":1,"lock":1})

 释放锁: db.$cmd.unlock.findOne()

12.          驱动示例

官网驱动示例:(从官网下载Mongodb.net 的驱动)

App.config内容:

<configuration>

    <startup>

       <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>

    </startup>

  <appSettings><!—配置连接字符串、数据库-->

    <add key="ConnString" value="mongodb://127.0.0.1:27017"/>

    <add key="DataBase" value="HXFDB"/>

  </appSettings>

</configuration>

 

    public staticclass MongoDBFactory

    {

        privatestatic string conn = ConfigurationManager.AppSettings["ConnString"];

        privatestatic string database = ConfigurationManager.AppSettings["DataBase"];

        private static MongoClient client = new MongoClient(conn);

        privatestatic MongoServer server = client.GetServer();

        privatestatic MongoDatabase db = server.GetDatabase(database);

        //增       

public static void Insert(string name)

        {

           MongoCollection collection = db.GetCollection("User");

           User user = new User() { ID = 0, Name = name, Sex = "男", Age = 27, Code = "MY001" , _id=newBson.ObjectId()};

           collection.Insert<User>(user);

 

           QueryDocument query = new QueryDocument { { "Name", name } };

           var list = collection.FindAs<User>(query);

          

           foreach (var u in list)

           {

               Console.WriteLine("Name: {0}, Sex: {1}, Age: {2}, Code: {3}", u.Name,u.Sex, u.Age, u.Code);

           }

        }

        //改

        publicstatic void Update1(string name)

        {

           MongoCollection collection = db.GetCollection("User");

           QueryDocument query = new QueryDocument { {"Name",name}};

           IMongoUpdate update = Update.Set("Code", "Olive001");

            collection.Update(query,update);

           var list = collection.FindAllAs<User>();

           foreach (var u in list)

           {

               Console.WriteLine("Name: {0}, Sex: {1}, Age: {2}, Code: {3}", u.Name,u.Sex, u.Age, u.Code);

           }

        }

        //删

        publicstatic void Remove(string name)

        {

           MongoCollection collection = db.GetCollection("User");

           QueryDocument query = new QueryDocument { { "Name", name } };

           collection.Remove(query);

           var list = collection.FindAllAs<User>();

           foreach (var u in list)

           {

               Console.WriteLine("Name: {0}, Sex: {1}, Age: {2}, Code: {3}", u.Name,u.Sex, u.Age, u.Code);

           }

        }

        //查

        publicstatic void Query()

        {

           MongoCollection collection = db.GetCollection("User");

           Console.WriteLine("SELECT * FROM table ");

            //SELECT * FROM table

           var list = collection.FindAllAs<User>();

           foreach (var u in list)

           {

               Console.WriteLine("Name: {0}, Sex: {1}, Age: {2}, Code: {3}", u.Name,u.Sex, u.Age, u.Code);

           }

           Console.WriteLine("SELECT * FROM table WHERE Uid > 10 AND Uid< 20");

           // sql : SELECT * FROM table WHERE Uid > 10 AND Uid < 20

           QueryDocument query = new QueryDocument { };

           BsonDocument bd=new BsonDocument ();

           bd.Add("$gte",0);

           bd.Add("$lt",5);

           query.Add("ID", bd);

           list = collection.FindAs<User>(query);

           foreach (var u in list)

           {

               Console.WriteLine("Name: {0}, Sex: {1}, Age: {2}, Code: {3}", u.Name,u.Sex, u.Age, u.Code);

           }

           Console.WriteLine("SELECT Name FROM table WHERE Uid > 10 AND Uid< 20");

           // SELECT Name FROM table WHERE Uid > 10 AND Uid < 20

           FieldsDocument f = new FieldsDocument();

           f.Add("Name", 1);

           list = collection.FindAs<User>(query).SetFields(f);

           foreach (var u in list)

           {

               Console.WriteLine("Name: {0}", u.Name);

           }

           Console.WriteLine("SELECT * FROM table ORDER BY Uid DESC LIMIT10,10");

           //SELECT * FROM table ORDER BY Name DESC LIMIT 10,10

           SortByDocument s = new SortByDocument();

           s.Add("Name", -1);

           list = collection.FindAs<User>(query).SetSortOrder(s).SetSkip(1).SetLimit(10);

           foreach (var u in list)

           {

               Console.WriteLine("Name: {0}, Sex: {1}, Age: {2}, Code: {3}", u.Name,u.Sex, u.Age, u.Code);

           }

        }

    }

}