前言:学习札记!
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);
}
}
}
}