您的当前位置:首页Mongodb学习总结-1(基础入门)

Mongodb学习总结-1(基础入门)

2023-11-09 来源:六九路网

关于mongodb的好处,优点之类的这里就不说了,唯一要讲的一点就是mongodb中有三元素:数据库,集合,文档,其中“集合”

就是对应关系数据库中的“表”,“文档”对应“行”。

 

一: 下载

      上MongoDB官网 ,我们发现有32bit和64bit,这个就要看你系统了,不过这里有两点注意:

          ①:根据业界规则,偶数为“稳定版”(如:1.6.X,1.8.X),奇数为“开发版”(如:1.7.X,1.9.X),这两个版本的区别相信大家都知道吧。

          ②:32bit的mongodb最大只能存放2G的数据,64bit就没有限制。

     我这里就下载"2.0.2版本,32bit“,ok,下载之后我就放到”E盘“,改下文件夹名字为”mongodb“。

 

二:启动

     ①:启动之前,我们要给mongodb指定一个文件夹,这里取名为”db",用来存放mongodb的数据。

               技术分享

     ②:微软徽标+R,输入cmd,首先找到“mongodb”的路径,然后运行mongod开启命令,同时用--dbpath指定数据存放地点为“db”文件夹。

 

              技术分享

 

      ③:最后要看下是否开启成功,从图中的信息中获知,mongodb采用27017端口,那么我们就在浏览器里面键入“http://localhost:27017/”,

             打开后,mongodb告诉我们在27017上Add 1000可以用http模式查看mongodb的管理信息。

           

               技术分享

 

三:基本操作

     由于是开篇,就大概的说下基本的“增删查改“,我们再开一个cmd,输入mongo命令打开shell,其实这个shell就是mongodb的客户端,

同时也是一个js的编译器,默认连接的是“test”数据库。

 

    技术分享

 

    <1>  insert 操作

             好,数据库有了,下一步就是集合,这里就取集合名为“person”,要注意的就是文档是一个json的扩展(Bson)形式。

    技术分享

  <2> find 操作

       我们将数据插入后,肯定是要find出来,不然插了也白插,这里要注意两点:

           ① “_id": 这个字段是数据库默认给我们加的GUID,目的就是保证数据的唯一性。

           ② 严格的按照Bson的形式书写文档,不过也没关系,错误提示还是很强大的。

  技术分享

 <3> update操作

      update方法的第一个参数为“查找的条件”,第二个参数为“更新的值”,学过C#,相信还是很好理解的。

  技术分享

<4> remove操作

      remove中如果不带参数将删除所有数据,呵呵,很危险的操作,在mongodb中是一个不可撤回的操作,三思而后行。

技术分享

Mongodb学习总结-1(基础入门)

标签:

小编还为您整理了以下内容,可能对您也有帮助:

MongoDB入门实操《一》

什么是MongoDB

MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。

如何搭建MongoDB服务

step1: docker pull mongo

step2: docker run -d --name mongotest -p 27017:27017 mongo --auth (推荐)

或者: docker run -d --name mongotest -p 27017:27017 mongo

备注:--auth 表示需要密码才可以访问容器服务

无--auth 可以直接访问

step3: 进入容器 docker exec -it mongotest bash 或者 通过dashboard 进入(推荐)

Mongo的常规使用

在容器里面操作使用Mongo数据库:

总结:今天的文章主要介绍了什么是Mongo DB, Mongo DB服务的构建及Mongo的一些基础操作命令,如创建用户,用户授权,创建数据,显示数据,创建集合,显示集合,集合数据查询,集合删除等(并对比着mysql命令进行了解释),希望对新手有帮助。

MongoDB学习笔记 用MongoDB的文档结构描述数据关系

MongoDB的集合(collection)可以看做关系型数据库的表,文档对象(document)可以看做关系型数据库的一条记录。但两者并 不完全对等。表的结构是固定的,MongoDB集合并没有这个约束;另外,存入集合的文档对象甚至可以嵌入子文档,或者“子集合”。他们最终都可以用类似 于BJSON的格式描述。我们今天就来分析MongoDB这一特性带来的独特数据管理方式。我们还是以samus驱动为例来分析,samus驱动支持两种 方式访问数据库,基本方式和linq方式,基本方式在上篇以介绍过,linq方式我不想单独讲解应用实例,这篇我会用两种方式来对比介绍。

一、包含子文档的集合操作

有这么一个应用场景,某网站提供会员登录的功能,用户需要注册账号才能享受会员服务,但是注册者可能会因为用户资料表单输入项过大而放弃填写,因此用户信息分为主要资料和详细资料两项,初次注册只需要填写主要资料就行了。我们打算把详细信息设计为子文档存储。

1) linq方式实现

1. 新建数据描述类,描述用户信息

复制代码 代码如下:

/// summary

   /// 用户主要资料

   /// /summary

   public class UserInfo

   {

public string UserId { get; set; }

public string UserName { get; set; }

public string PassWord { get; set; }

public Detail Detail { get; set; }

   }

/// summary

   /// 用户详细资料

   /// /summary

   public class Detail

   {

public string Address { get; set; }

public int Age { get; set; }

public string Email { get; set; }

   }

2. 我们要新建一个用户业务操作类“UserBLL”。这个时候要让驱动知道UserInfo类描述了“用户资料”的字段信息,在GetMongo()方法实现了配置步骤,UserBLL完整代码如下:

复制代码 代码如下:

public class UserBLL

   {

public string connectionString = "mongodb://localhost";

public string databaseName = "myDatabase";

private Mongo mongo;

private MongoDatabase mongoDatabase;

//注意这里泛型类型为“UserInfo”

private MongoCollectionUserInfo mongoCollection;

public UserBLL()

{

   mongo = GetMongo();

   mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;

   mongoCollection = mongoDatabase.GetCollectionUserInfo() as MongoCollectionUserInfo;

   mongo.Connect();

}

~UserBLL()

{

   mongo.Disconnect();

}

/// summary

/// 配置Mongo,将类UserInfo映射到集合

/// /summary

private Mongo GetMongo()

{

   var config = new MongoConfigurationBuilder();

   config.Mapping(mapping =

   {

mapping.DefaultProfile(profile =

{

   profile.SubClassesAre(t = t.IsSubclassOf(typeof(UserInfo)));

});

mapping.MapUserInfo();

   });

   config.ConnectionString(connectionString);

   return new Mongo(config.BuildConfiguration());

}

   }

3. 接着,在“UserBLL”类中定义一个方法“InsertSomeData()”来插入一些数据:

复制代码 代码如下:

/// summary

   /// 插入一些数据

   /// /summary

   public void InsertSomeData()

   {

UserInfo userInfo1 = new UserInfo()

{

   UserId = "1001",

   UserName = "张三",

   PassWord = "123456"

};

mongoCollection.Save(userInfo1);

UserInfo userInfo2 = new UserInfo()

{

   UserId = "1002",

   UserName = "李四",

   PassWord = "123456",

   Detail = new Detail()

   {

Address = "湖北",

Age = 20,

Email = "lisi@163.com"

   }

};

mongoCollection.Save(userInfo2);

UserInfo userInfo3 = new UserInfo()

{

   UserId = "1003",

   UserName = "王五",

   PassWord = "123456",

   Detail = new Detail()

   {

Address = "广东",

Age = 20,

Email = "wangwu@163.com"

   }

};

mongoCollection.Save(userInfo3);

UserInfo userInfo4 = new UserInfo()

{

   UserId = "1004",

   UserName = "赵六",

   PassWord = "123456",

   Detail = new Detail()

   {

Address = "湖北"

   }

};

mongoCollection.Save(userInfo4);

    }

4. 定义一个查找数据的方法“Select”,它将查找用户详细信息中,地址在湖北的全部用户:

复制代码 代码如下:

/// summary

   /// 查询详细资料地址为湖北的用户信息

   /// /summary

   public ListUserInfo Select()

   {

return mongoCollection.Linq().Where(x = x.Detail.Address == "湖北").ToList();

   }

5. 还定义一个删除数据的方法,将删除集合全部数据:

复制代码 代码如下:

/// summary

   /// 删除全部用户信息

   /// /summary

   public void DeleteAll()

   {

mongoCollection.Remove(x = true);

   }

6. 在Main方法中添加如下代码:

复制代码 代码如下:

static void Main(string[] args)

   {

UserBLL userBll = new UserBLL();

userBll.InsertSomeData();

var users = userBll.Select();

foreach (var user in users)

{

   Console.WriteLine(user.UserName + "是湖北人");

};

userBll.DeleteAll();

   }

7. 最后执行程序,打印如下信息:

李四是湖北人

   赵六是湖北人

1) 普通实现

普通方式实现不想多讲,直接贴代码,看看与linq方式有什么区别:

复制代码 代码如下:

class Program

   {

static void Main(string[] args)

{

   UserBLL userBll = new UserBLL();

   userBll.InsertSomeData();

   var users = userBll.Select();

   foreach (var user in users)

   {

Console.WriteLine(user["UserName"].ToString() + "是湖北人");

   };

   userBll.DeleteAll();

Console.ReadLine();

}

   }

public class UserBLL

   {

public string connectionString = "mongodb://localhost";

public string databaseName = "myDatabase";

public string collectionName = "UserInfo";

private Mongo mongo;

private MongoDatabase mongoDatabase;

private MongoCollectionDocument mongoCollection;

public UserBLL()

{

   mongo = new Mongo(connectionString);

   mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;

   mongoCollection = mongoDatabase.GetCollectionDocument(collectionName) as MongoCollectionDocument;

   mongo.Connect();

}

~UserBLL()

{

   mongo.Disconnect();

}

/// summary

/// 插入一些数据

/// /summary

public void InsertSomeData()

{

   Document userInfo1 = new Document();

   userInfo1["UserId"] = "1001";

   userInfo1["UserName"] = "张三";

   userInfo1["PassWord"] = "123456";

   mongoCollection.Save(userInfo1);

Document userInfo2 = new Document();

   userInfo2["UserId"] = "1002";

   userInfo2["UserName"] = "李四";

   userInfo2["PassWord"] = "123456";

   //子文档

   var userInfo2Detail = new Document();

   userInfo2Detail["Address"] = "湖北";

   userInfo2Detail["Age"] = 20;

   userInfo2Detail["Email"] = "lisi@163.com";

   userInfo2["Detail"] = userInfo2Detail;

   mongoCollection.Save(userInfo2);

Document userInfo3 = new Document();

   userInfo3["UserId"] = "1003";

   userInfo3["UserName"] = "王五";

   userInfo3["PassWord"] = "123456";

   var userInfo3Detail = new Document();

   userInfo3Detail["Address"] = "广东";

   userInfo3Detail["Age"] = 20;

   userInfo3Detail["Email"] = "wangwu@163.com";

   userInfo3["Detail"] = userInfo3Detail;

   mongoCollection.Save(userInfo3);

Document userInfo4 = new Document();

   userInfo4["UserId"] = "1004";

   userInfo4["UserName"] = "赵六";

   userInfo4["PassWord"] = "123456";

   var userInfo4Detail = new Document();

   userInfo4Detail["Address"] = "湖北";

   userInfo4["Detail"] = userInfo4Detail;

   mongoCollection.Save(userInfo4);

}

/// summary

/// 查询详细资料地址为湖北的用户信息

/// /summary

public IEnumerableDocument Select()

{

   return mongoCollection.Find(new Document { { "Detail.Address", "湖北" } }).Documents;

}

/// summary

/// 删除全部用户信息

/// /summary

public void DeleteAll()

{

   mongoCollection.Remove(new Document { });

}

   }

最后,我们通过这段代码输出全部用户资料信息的BJSON格式:

复制代码 代码如下:

/// summary

   /// 打印数据BJSON

   /// /summary

   public void PrintBJSON()

   {

string BJSON = string.Empty;

foreach (var documet in mongoCollection.FindAll().Documents)

{

BJSON += documet.ToString();

}

Console.WriteLine(BJSON);

   }

结果如下:

复制代码 代码如下:

{ "UserId": "1001", "UserName": "张三", "PassWord": "123456", "_id": "4d80ec1ab8a4731338000001" }

   { "UserId": "1002", "UserName": "李四", "PassWord": "123456", "Detail": { "Address": "湖北", "Age": 20, "Email": "lisi@163.com" }, "_id": "4d80ec1ab8a4731338000002" }

   { "UserId": "1003", "UserName": "王五", "PassWord": "123456", "Detail": { "Address": "广东", "Age": 20, "Email": "wangwu@163.com" }, "_id": "4d80ec1ab8a4731338000003" }

   { "UserId": "1004", "UserName": "赵六", "PassWord": "123456", "Detail": { "Address": "湖北" }, "_id": "4d80ec1ab8a4731338000004" }

二、包含“子集合”的集合操作

同样举个例子:有一个学校人事管理系统要统计班级和学生的信息,现在定义了一个“班级集合”,这个集合里面的学生字段是一个“学生集合”,包含了本班全部学生。

1) linq方式实现

基础配置我就不多说了,数据类定义如下:

复制代码 代码如下:

/// summary

   /// 班级信息

   /// /summary

   public class ClassInfo

   {

public string ClassName { get; set; }

public ListStudent Students { get; set; }

   }

/// summary

   /// 学生信息

   /// /summary

   public class Student

   {

public string Name { get; set; }

public int Age { get; set; }

   }

查询叫“张三”的学生在哪个班级,以及他的详细信息:

(这里其实是ToList后在内存中查的,linq方式直接查询好像驱动不支持。)

复制代码 代码如下:

public ListClassInfo Select()

   {

return mongoCollection.Linq().ToList().Where(x = x.Students.Exists(s = s.Name == "张三")).ToList();

   }

1) 普通实现

查询叫“张三”的学生在哪个班级,以及他的详细信息:

复制代码 代码如下:

public ListDocument Select()

   {

var mongocollection = mongoDatabase.GetCollection("ClassInfo");

return mongocollection.Find(new Document { { "Students.Name", "张三" } }).Documents.ToList();

   }

打印数据的BJSON:

复制代码 代码如下:

{ "_id": "4d814bae5c5f000000005f63", "ClassName": "1001", "Students": [ { "Name": "张三", "Age": 10 }, { "Name": "李四", "Age": 0 } ] }

   { "_id": "4d814bae5c5f000000005f64", "ClassName": "1002", "Students": [ ] }

   { "_id": "4d814bae5c5f000000005f65", "ClassName": "1003", "Students": [ { "Name": "王五", "Age": 11 }, { "Name": "赵六", "Age": 9 } ] }

三、小结

通过本节例子我们发现,MongoDB有它独特的文档结构可以描述数据对象之间的一些关系特征。它虽然没有关系型数据库多表符合查询那样强大的表 间查询方式,但也可以通过文档结构描述更灵活的关系特性,可以这么说,关系型数据库能做的,MongoDB基本上也可以做到。甚至有些关系数据库不容易做 到的,MongoDB也可以轻松做到,比如,描述数据类的继承关系等。

作者:李盼(Lipan)

   出处:[Lipan] (http://www.cnblogs.com/lipan/)

MongoDB学习笔记 用MongoDB的文档结构描述数据关系

MongoDB的集合(collection)可以看做关系型数据库的表,文档对象(document)可以看做关系型数据库的一条记录。但两者并 不完全对等。表的结构是固定的,MongoDB集合并没有这个约束;另外,存入集合的文档对象甚至可以嵌入子文档,或者“子集合”。他们最终都可以用类似 于BJSON的格式描述。我们今天就来分析MongoDB这一特性带来的独特数据管理方式。我们还是以samus驱动为例来分析,samus驱动支持两种 方式访问数据库,基本方式和linq方式,基本方式在上篇以介绍过,linq方式我不想单独讲解应用实例,这篇我会用两种方式来对比介绍。

一、包含子文档的集合操作

有这么一个应用场景,某网站提供会员登录的功能,用户需要注册账号才能享受会员服务,但是注册者可能会因为用户资料表单输入项过大而放弃填写,因此用户信息分为主要资料和详细资料两项,初次注册只需要填写主要资料就行了。我们打算把详细信息设计为子文档存储。

1) linq方式实现

1. 新建数据描述类,描述用户信息

复制代码 代码如下:

/// summary

   /// 用户主要资料

   /// /summary

   public class UserInfo

   {

public string UserId { get; set; }

public string UserName { get; set; }

public string PassWord { get; set; }

public Detail Detail { get; set; }

   }

/// summary

   /// 用户详细资料

   /// /summary

   public class Detail

   {

public string Address { get; set; }

public int Age { get; set; }

public string Email { get; set; }

   }

2. 我们要新建一个用户业务操作类“UserBLL”。这个时候要让驱动知道UserInfo类描述了“用户资料”的字段信息,在GetMongo()方法实现了配置步骤,UserBLL完整代码如下:

复制代码 代码如下:

public class UserBLL

   {

public string connectionString = "mongodb://localhost";

public string databaseName = "myDatabase";

private Mongo mongo;

private MongoDatabase mongoDatabase;

//注意这里泛型类型为“UserInfo”

private MongoCollectionUserInfo mongoCollection;

public UserBLL()

{

   mongo = GetMongo();

   mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;

   mongoCollection = mongoDatabase.GetCollectionUserInfo() as MongoCollectionUserInfo;

   mongo.Connect();

}

~UserBLL()

{

   mongo.Disconnect();

}

/// summary

/// 配置Mongo,将类UserInfo映射到集合

/// /summary

private Mongo GetMongo()

{

   var config = new MongoConfigurationBuilder();

   config.Mapping(mapping =

   {

mapping.DefaultProfile(profile =

{

   profile.SubClassesAre(t = t.IsSubclassOf(typeof(UserInfo)));

});

mapping.MapUserInfo();

   });

   config.ConnectionString(connectionString);

   return new Mongo(config.BuildConfiguration());

}

   }

3. 接着,在“UserBLL”类中定义一个方法“InsertSomeData()”来插入一些数据:

复制代码 代码如下:

/// summary

   /// 插入一些数据

   /// /summary

   public void InsertSomeData()

   {

UserInfo userInfo1 = new UserInfo()

{

   UserId = "1001",

   UserName = "张三",

   PassWord = "123456"

};

mongoCollection.Save(userInfo1);

UserInfo userInfo2 = new UserInfo()

{

   UserId = "1002",

   UserName = "李四",

   PassWord = "123456",

   Detail = new Detail()

   {

Address = "湖北",

Age = 20,

Email = "lisi@163.com"

   }

};

mongoCollection.Save(userInfo2);

UserInfo userInfo3 = new UserInfo()

{

   UserId = "1003",

   UserName = "王五",

   PassWord = "123456",

   Detail = new Detail()

   {

Address = "广东",

Age = 20,

Email = "wangwu@163.com"

   }

};

mongoCollection.Save(userInfo3);

UserInfo userInfo4 = new UserInfo()

{

   UserId = "1004",

   UserName = "赵六",

   PassWord = "123456",

   Detail = new Detail()

   {

Address = "湖北"

   }

};

mongoCollection.Save(userInfo4);

    }

4. 定义一个查找数据的方法“Select”,它将查找用户详细信息中,地址在湖北的全部用户:

复制代码 代码如下:

/// summary

   /// 查询详细资料地址为湖北的用户信息

   /// /summary

   public ListUserInfo Select()

   {

return mongoCollection.Linq().Where(x = x.Detail.Address == "湖北").ToList();

   }

5. 还定义一个删除数据的方法,将删除集合全部数据:

复制代码 代码如下:

/// summary

   /// 删除全部用户信息

   /// /summary

   public void DeleteAll()

   {

mongoCollection.Remove(x = true);

   }

6. 在Main方法中添加如下代码:

复制代码 代码如下:

static void Main(string[] args)

   {

UserBLL userBll = new UserBLL();

userBll.InsertSomeData();

var users = userBll.Select();

foreach (var user in users)

{

   Console.WriteLine(user.UserName + "是湖北人");

};

userBll.DeleteAll();

   }

7. 最后执行程序,打印如下信息:

李四是湖北人

   赵六是湖北人

1) 普通实现

普通方式实现不想多讲,直接贴代码,看看与linq方式有什么区别:

复制代码 代码如下:

class Program

   {

static void Main(string[] args)

{

   UserBLL userBll = new UserBLL();

   userBll.InsertSomeData();

   var users = userBll.Select();

   foreach (var user in users)

   {

Console.WriteLine(user["UserName"].ToString() + "是湖北人");

   };

   userBll.DeleteAll();

Console.ReadLine();

}

   }

public class UserBLL

   {

public string connectionString = "mongodb://localhost";

public string databaseName = "myDatabase";

public string collectionName = "UserInfo";

private Mongo mongo;

private MongoDatabase mongoDatabase;

private MongoCollectionDocument mongoCollection;

public UserBLL()

{

   mongo = new Mongo(connectionString);

   mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;

   mongoCollection = mongoDatabase.GetCollectionDocument(collectionName) as MongoCollectionDocument;

   mongo.Connect();

}

~UserBLL()

{

   mongo.Disconnect();

}

/// summary

/// 插入一些数据

/// /summary

public void InsertSomeData()

{

   Document userInfo1 = new Document();

   userInfo1["UserId"] = "1001";

   userInfo1["UserName"] = "张三";

   userInfo1["PassWord"] = "123456";

   mongoCollection.Save(userInfo1);

Document userInfo2 = new Document();

   userInfo2["UserId"] = "1002";

   userInfo2["UserName"] = "李四";

   userInfo2["PassWord"] = "123456";

   //子文档

   var userInfo2Detail = new Document();

   userInfo2Detail["Address"] = "湖北";

   userInfo2Detail["Age"] = 20;

   userInfo2Detail["Email"] = "lisi@163.com";

   userInfo2["Detail"] = userInfo2Detail;

   mongoCollection.Save(userInfo2);

Document userInfo3 = new Document();

   userInfo3["UserId"] = "1003";

   userInfo3["UserName"] = "王五";

   userInfo3["PassWord"] = "123456";

   var userInfo3Detail = new Document();

   userInfo3Detail["Address"] = "广东";

   userInfo3Detail["Age"] = 20;

   userInfo3Detail["Email"] = "wangwu@163.com";

   userInfo3["Detail"] = userInfo3Detail;

   mongoCollection.Save(userInfo3);

Document userInfo4 = new Document();

   userInfo4["UserId"] = "1004";

   userInfo4["UserName"] = "赵六";

   userInfo4["PassWord"] = "123456";

   var userInfo4Detail = new Document();

   userInfo4Detail["Address"] = "湖北";

   userInfo4["Detail"] = userInfo4Detail;

   mongoCollection.Save(userInfo4);

}

/// summary

/// 查询详细资料地址为湖北的用户信息

/// /summary

public IEnumerableDocument Select()

{

   return mongoCollection.Find(new Document { { "Detail.Address", "湖北" } }).Documents;

}

/// summary

/// 删除全部用户信息

/// /summary

public void DeleteAll()

{

   mongoCollection.Remove(new Document { });

}

   }

最后,我们通过这段代码输出全部用户资料信息的BJSON格式:

复制代码 代码如下:

/// summary

   /// 打印数据BJSON

   /// /summary

   public void PrintBJSON()

   {

string BJSON = string.Empty;

foreach (var documet in mongoCollection.FindAll().Documents)

{

BJSON += documet.ToString();

}

Console.WriteLine(BJSON);

   }

结果如下:

复制代码 代码如下:

{ "UserId": "1001", "UserName": "张三", "PassWord": "123456", "_id": "4d80ec1ab8a4731338000001" }

   { "UserId": "1002", "UserName": "李四", "PassWord": "123456", "Detail": { "Address": "湖北", "Age": 20, "Email": "lisi@163.com" }, "_id": "4d80ec1ab8a4731338000002" }

   { "UserId": "1003", "UserName": "王五", "PassWord": "123456", "Detail": { "Address": "广东", "Age": 20, "Email": "wangwu@163.com" }, "_id": "4d80ec1ab8a4731338000003" }

   { "UserId": "1004", "UserName": "赵六", "PassWord": "123456", "Detail": { "Address": "湖北" }, "_id": "4d80ec1ab8a4731338000004" }

二、包含“子集合”的集合操作

同样举个例子:有一个学校人事管理系统要统计班级和学生的信息,现在定义了一个“班级集合”,这个集合里面的学生字段是一个“学生集合”,包含了本班全部学生。

1) linq方式实现

基础配置我就不多说了,数据类定义如下:

复制代码 代码如下:

/// summary

   /// 班级信息

   /// /summary

   public class ClassInfo

   {

public string ClassName { get; set; }

public ListStudent Students { get; set; }

   }

/// summary

   /// 学生信息

   /// /summary

   public class Student

   {

public string Name { get; set; }

public int Age { get; set; }

   }

查询叫“张三”的学生在哪个班级,以及他的详细信息:

(这里其实是ToList后在内存中查的,linq方式直接查询好像驱动不支持。)

复制代码 代码如下:

public ListClassInfo Select()

   {

return mongoCollection.Linq().ToList().Where(x = x.Students.Exists(s = s.Name == "张三")).ToList();

   }

1) 普通实现

查询叫“张三”的学生在哪个班级,以及他的详细信息:

复制代码 代码如下:

public ListDocument Select()

   {

var mongocollection = mongoDatabase.GetCollection("ClassInfo");

return mongocollection.Find(new Document { { "Students.Name", "张三" } }).Documents.ToList();

   }

打印数据的BJSON:

复制代码 代码如下:

{ "_id": "4d814bae5c5f000000005f63", "ClassName": "1001", "Students": [ { "Name": "张三", "Age": 10 }, { "Name": "李四", "Age": 0 } ] }

   { "_id": "4d814bae5c5f000000005f64", "ClassName": "1002", "Students": [ ] }

   { "_id": "4d814bae5c5f000000005f65", "ClassName": "1003", "Students": [ { "Name": "王五", "Age": 11 }, { "Name": "赵六", "Age": 9 } ] }

三、小结

通过本节例子我们发现,MongoDB有它独特的文档结构可以描述数据对象之间的一些关系特征。它虽然没有关系型数据库多表符合查询那样强大的表 间查询方式,但也可以通过文档结构描述更灵活的关系特性,可以这么说,关系型数据库能做的,MongoDB基本上也可以做到。甚至有些关系数据库不容易做 到的,MongoDB也可以轻松做到,比如,描述数据类的继承关系等。

作者:李盼(Lipan)

   出处:[Lipan] (http://www.cnblogs.com/lipan/)

MongoDB 是什么 ? 能干嘛?

最近在回顾mongodb的相关知识,输出一篇文章做为MongoDB知识点的总结。

总结的目的在于回顾MongoDB的相关知识点,明确MongoDB在企业级应用中充当的角色,为之后的技术选型提供一个可查阅的信息简报。

MongoDB是一款为web应用程序和互联网基础设施设计的数据库管理系统。没错MongoDB就是数据库,是NoSQL类型的数据库

(1)MongoDB提出的是文档、集合的概念,使用BSON(类JSON)作为其数据模型结构,其结构是面向对象的而不是二维表,存储一个用户在MongoDB中是这样子的。

使用这样的数据模型,使得MongoDB能在生产环境中提供高读写的能力,吞吐量较于mysql等SQL数据库大大增强。

(2)易伸缩,自动故障转移。易伸缩指的是提供了分片能力,能对数据集进行分片,数据的存储压力分摊给多台服务器。自动故障转移是副本集的概念,MongoDB能检测主节点是否存活,当失活时能自动提升从节点为主节点,达到故障转移。

(3)数据模型因为是面向对象的,所以可以表示丰富的、有层级的数据结构,比如博客系统中能把“评论”直接怼到“文章“的文档中,而不必像myqsl一样创建三张表来描述这样的关系。

SQL类型的数据库是正规化的,可以通过主键或者外键的约束保证数据的完整性与唯一性,所以SQL类型的数据库常用于对数据完整性较高的系统。MongoDB在这一方面是不如SQL类型的数据库,且MongoDB没有固定的Schema,正因为MongoDB少了一些这样的约束条件,可以让数据的存储数据结构更灵活,存储速度更加快。

MongoDB保留了关系型数据库即时查询的能力,保留了索引(底层是基于B tree)的能力。这一点汲取了关系型数据库的优点,相比于同类型的NoSQL redis 并没有上述的能力。

MongoDB自身提供了副本集能将数据分布在多台机器上实现冗余,目的是可以提供自动故障转移、扩展读能力。

MongoDB的驱动实现一个写入语义 fire and forget ,即通过驱动调用写入时,可以立即得到返回得到成功的结果(即使是报错),这样让写入的速度更加快,当然会有一定的不安全性,完全依赖网络。

MongoDB提供了Journaling日志的概念,实际上像mysql的bin-log日志,当需要插入的时候会先往日志里面写入记录,再完成实际的数据操作,这样如果出现停电,进程突然中断的情况,可以保障数据不会错误,可以通过修复功能读取Journaling日志进行修复。

MongoDB使用分片技术对数据进行扩展,MongoDB能自动分片、自动转移分片里面的数据块,让每一个服务器里面存储的数据都是一样大小。

MongoDB核心服务器主要是通过mongod程序启动的,而且在启动时不需对MongoDB使用的内存进行配置,因为其设计哲学是内存管理最好是交给操作系统,缺少内存配置是MongoDB的设计亮点,另外,还可通过mongos路由服务器使用分片功能。

MongoDB的主要客户端是可以交互的js shell 通过mongo启动,使用js shell能使用js直接与MongoDB进行交流,像使用sql语句查询mysql数据一样使用js语法查询MongoDB的数据,另外还提供了各种语言的驱动包,方便各种语言的接入。

mongomp和mongorestore,备份和恢复数据库的标准工具。输出BSON格式,迁移数据库。

mongoexport和mongoimport,用来导入导出JSON、CSV和TSV数据,数据需要支持多格式时有用。mongoimport还能用与大数据集的初始导入,但是在导入前顺便还要注意一下,为了能充分利用好mongoDB通常需要对数据模型做一些调整。

mongosniff,网络嗅探工具,用来观察发送到数据库的操作。基本就是把网络上传输的BSON转换为易于人们阅读的shell语句。

因此,可以总结得到,MongoDB结合键值存储和关系数据库的最好特性。因为简单,所以数据极快,而且相对容易伸缩还提供复杂查询机制的数据库。MongoDB需要跑在64位的服务器上面,且最好单独部署,因为是数据库,所以也需要对其进行热备、冷备处理。

因为本篇文章不是API手册,所有这里对shell的使用也是基础的介绍什么功能可以用什么语句,主要是为了展示使用MongoDB shell的方便性,如果需要知道具体的MongoDB shell语法可以查阅官方文档。

创建数据库并不是必须的操作,数据库与集合只有在第一次插入文档时才会被创建,与对数据的动态处理方式是一致的。简化并加速开发过程,而且有利于动态分配命名空间。如果担心数据库或集合被意外创建,可以开启严格模式

以上的命令只是简单实例,假设如果你之前没有学习过任何数据库语法,同时开始学sql查询语法和MongoDB 查询语法,你会发现哪一个更简单呢?如果你使用的是java驱动去操作MongoDB,你会发现任何的查询都像Hibernate提供出来的查询方式一样,只要构建好一个查询条件对象,便能轻松查询(接下来会给出示例),博主之前熟悉ES6,所以入手MongoDB js shell完成没问题,也正因为这样简洁,完善的查询机制,深深的爱上了MongoDB。

这里引用的是最新的驱动包,提供了一套新的访问连接方式

这里只举例了简单的链接与简单的MongoDB操作,可见其操作的容易性。使用驱动时是基于TCP套接字与MongoDB进行通信的,如果查询结果较多,恰好无法全部放进第一服务器中,将会向服务器发送一个getmore指令获取下一批查询结果。

插入数据到服务器时间,不会等待服务器的响应,驱动会假设写入是成功的,实际是使用客户端生成对象id,但是该行为可以通过配置配置,可以通过安全模式开启,安全模式可以校验服务器端插入的错误。

MongoDB是什么,怎么用?看完你就知道了

MongoDB是一款为web应用程序和互联网基础设施设计的数据库管理系统。没错MongoDB就是数据库,是NoSQL类型的数据库。

(1)MongoDB提出的是文档、集合的概念,使用BSON(类JSON)作为其数据模型结构,其结构是面向对象的而不是二维表,存储一个用户在MongoDB中是这样子的。

使用这样的数据模型,使得MongoDB能在生产环境中提供高读写的能力,吞吐量较于mysql等SQL数据库大大增强。

(2)易伸缩,自动故障转移。易伸缩指的是提供了分片能力,能对数据集进行分片,数据的存储压力分摊给多台服务器。自动故障转移是副本集的概念,MongoDB能检测主节点是否存活,当失活时能自动提升从节点为主节点,达到故障转移。

(3)数据模型因为是面向对象的,所以可以表示丰富的、有层级的数据结构,比如博客系统中能把“评论”直接怼到“文章“的文档中,而不必像myqsl一样创建三张表来描述这样的关系。

(1)文档数据类型

SQL类型的数据库是正规化的,可以通过主键或者外键的约束保证数据的完整性与唯一性,所以SQL类型的数据库常用于对数据完整性较高的系统。MongoDB在这一方面是不如SQL类型的数据库,且MongoDB没有固定的Schema,正因为MongoDB少了一些这样的约束条件,可以让数据的存储数据结构更灵活,存储速度更加快。

(2)即时查询能力

MongoDB保留了关系型数据库即时查询的能力,保留了索引(底层是基于B tree)的能力。这一点汲取了关系型数据库的优点,相比于同类型的NoSQL redis 并没有上述的能力。

(3)复制能力

MongoDB自身提供了副本集能将数据分布在多台机器上实现冗余,目的是可以提供自动故障转移、扩展读能力。

(4)速度与持久性

MongoDB的驱动实现一个写入语义 fire and forget ,即通过驱动调用写入时,可以立即得到返回得到成功的结果(即使是报错),这样让写入的速度更加快,当然会有一定的不安全性,完全依赖网络。

MongoDB提供了Journaling日志的概念,实际上像mysql的bin-log日志,当需要插入的时候会先往日志里面写入记录,再完成实际的数据操作,这样如果出现停电,进程突然中断的情况,可以保障数据不会错误,可以通过修复功能读取Journaling日志进行修复。

(5)数据扩展

MongoDB使用分片技术对数据进行扩展,MongoDB能自动分片、自动转移分片里面的数据块,让每一个服务器里面存储的数据都是一样大小。

MongoDB核心服务器主要是通过mongod程序启动的,而且在启动时不需对MongoDB使用的内存进行配置,因为其设计哲学是内存管理最好是交给操作系统,缺少内存配置是MongoDB的设计亮点,另外,还可通过mongos路由服务器使用分片功能。

MongoDB的主要客户端是可以交互的js shell 通过mongo启动,使用js shell能使用js直接与MongoDB进行交流,像使用sql语句查询mysql数据一样使用js语法查询MongoDB的数据,另外还提供了各种语言的驱动包,方便各种语言的接入。

mongomp和mongorestore,备份和恢复数据库的标准工具。输出BSON格式,迁移数据库。

mongoexport和mongoimport,用来导入导出JSON、CSV和TSV数据,数据需要支持多格式时有用。mongoimport还能用与大数据集的初始导入,但是在导入前顺便还要注意一下,为了能充分利用好mongoDB通常需要对数据模型做一些调整。

mongosniff,网络嗅探工具,用来观察发送到数据库的操作。基本就是把网络上传输的BSON转换为易于人们阅读的shell语句。

因此,可以总结得到,MongoDB结合键值存储和关系数据库的最好特性。因为简单,所以数据极快,而且相对容易伸缩还提供复杂查询机制的数据库。MongoDB需要跑在64位的服务器上面,且最好单独部署,因为是数据库,所以也需要对其进行热备、冷备处理。

因为本篇文章不是API手册,所有这里对shell的使用也是基础的介绍什么功能可以用什么语句,主要是为了展示使用MongoDB shell的方便性,如果需要知道具体的MongoDB shell语法可以查阅官方文档。

创建数据库并不是必须的操作,数据库与集合只有在第一次插入文档时才会被创建,与对数据的动态处理方式是一致的。简化并加速开发过程,而且有利于动态分配命名空间。如果担心数据库或集合被意外创建,可以开启严格模式。

以上的命令只是简单实例,假设如果你之前没有学习过任何数据库语法,同时开始学sql查询语法和MongoDB 查询语法,你会发现哪一个更简单呢?如果你使用的是java驱动去操作MongoDB,你会发现任何的查询都像Hibernate提供出来的查询方式一样,只要构建好一个查询条件对象,便能轻松查询(接下来会给出示例),博主之前熟悉ES6,所以入手MongoDB js shell完成没问题,也正因为这样简洁,完善的查询机制,深深的爱上了MongoDB。

使用java驱动链接MongoDB是一件非常简单的事情,简单的引用,简单的做增删改查。在使用完java驱动后我才发现spring 对MongoDB 的封装还不如官方自身提供出来的东西好用,下面简单的展示一下使用。

这里只举例了简单的链接与简单的MongoDB操作,可见其操作的容易性。使用驱动时是基于TCP套接字与MongoDB进行通信的,如果查询结果较多,恰好无法全部放进第一服务器中,将会向服务器发送一个getmore指令获取下一批查询结果。

插入数据到服务器时间,不会等待服务器的响应,驱动会假设写入是成功的,实际是使用客户端生成对象id,但是该行为可以通过配置配置,可以通过安全模式开启,安全模式可以校验服务器端插入的错误。

要清楚了解MongoDB的基本数据单元。在关系型数据库中有带列和行的数据表。而MongoDB数据的基本单元是BSON文档,在键值中有指向不定类型值的键,MongoDB拥有即时查询,但不支持联结操作,简单的键值存储只能根据单个键来获取值,不支持事务,但支持多种原子更新操作。

如读写比是怎样的,需要何种查询,数据是如何更新的,会不会存在什么并发问题,数据结构化的程度是要求高还是低。系统本身的需求决定mysql还是MongoDB。

在关于schema 的设计中要注意一些原则,比如:

数据库是集合的逻辑与物理分组,MongoDB没有提供创建数据库的语法,只有在插入集合时,数据库才开始建立。创建数据库后会在磁盘分配一组数据文件,所有集合、索引和数据库的其他元数据都保存在这些文件中,查阅数据库使用磁盘状态可通过。

集合是结构上或概念上相似得文档的容器,集合的名称可以包含数字、字母或 . 符号,但必须以字母或数字开头,完全。

限定集合名不能超过128个字符,实际上 . 符号在集合中很有用,能提供某种虚拟命名空间,这是一种组织上的原则,和其他集合是一视同仁的。在集合中可以使用。

其次是键值,在MongoDB里面所有的字符串都是UTF-8类型。数字类型包括double、int、long。日期类型都是UTC格式,所以在MongoDB里面看到的时间会比北京时间慢8小时。整个文档大小会*在16m以内,因为这样可以防止创建难看的数据类型,且小文档可以提升性能,批量插入文档理想数字范围是10~200,大小不能超过16MB。

(1)索引能显著减少获取文档的所需工作量,具体的对比可以通过 .explain()方法进行对比

(2)解析查询时MongoDB通过最优计划选择一个索引进行查询,当没有最适合索引时,会先不同的使用各个索引进行查询,最终选出一个最优索引做查询

(3)如果有一个a-b的复合索引,那么仅针对a的索引是冗余的

(4)复合索引里的键的顺序是很重要的

(1)单键索引

(2)复合索引

(3)唯一性索引

(4)稀疏索引

如索引的字段会出现null的值,或是大量文档都不包含被索引的键。

如果数据集很大时,构建索引将会花费很长的时间,且会影响程序性能,可通过

当使用 mongorestore 时会重新构建索引。当曾经执行过大规模的删除时,可使用

对索引进行压缩,重建。

(1)查阅慢查询日志

(2)分析慢查询

注意新版本的MongoDB 的explain方法是需要参数的,不然只显示普通的信息。

本节同样主要简单呈现MongoDB副本集搭建的简易性,与副本集的强壮性,监控容易性

提供主从复制能力,热备能力,故障转移能力

实际上MongoDB对副本集的操作跟mysql主从操作是差不多的,先看一下mysql的主从数据流动过程

而MongoDB主要依赖的日志文件是oplog

写操作先被记录下来,添加到主节点的oplog里。与此同时,所有从结点复制oplog。首先,查看自己oplog里最后一条的时间戳;其次,查询主节点oplog里所有大于此时间戳的条目;最后,把那些条目添加到自己的oplog里并应用到自己的库里。从节点使用长轮询立即应用来自主结点oplog的新条目。

当遇到以下情况,从节点会停止复制

local数据库保存了所有副本集元素据和oplog日志

可以使用以下命令查看复制情况

每个副本集成员每秒钟ping一次其他所有成员,可以通过rs.status()看到节点上次的心跳检测时间戳和 健康 状况。

这个点没必要过多描述,但是有一个特殊场景,如果从节点和仲裁节点都被杀了,只剩下主节点,他会把自己降级成为从节点。

如果主节点的数据还没有写到从库,那么数据不能算提交,当该主节点变成从节点时,便会触发回滚,那些没写到从库的数据将会被删除,可以通过rollback子目录中的BSON文件恢复回滚的内容。

(1)使用单节点链接

只能链接到主节点,如果链接到从节点的话,会被拒绝写入操作,但是如果没有使用安全模式,因为mongo的fire and forget 特性,会把拒绝写入的异常给吃掉。

(2)使用副本集方式链接

能根据写入的情况自动进行故障转移,但是当副本集进行新的选举时,还是会出现故障,如果不使用安全模式,依旧会出现写不进去,但现实成功的情况。

分片是数据库切分的一个概念实现,这里也是简单总结为什么要使用分片以及分片的原理,操作。

当数据量过大,索引和工作数据集占用的内存就会越来越多,所以需要通过分片负载来解决这个问题

(1)分片组件

(2)分片的核心操作

分片一个集合:分片是根据一个属性的范围进行划分的,MongoDB使用所谓的分片键让每个文档在这些范围里找到自己的位置

块:是位于一个分片中的一段连续的分片键范围,可以理解为若干个块组成分片,分片组成MongoDB的全部数据

(3)拆分与迁移

块的拆分:初始化时只有一个块,达到最大块尺寸64MB或100000个文档就会触发块的拆分。把原来的范围一分为二,这样就有了两个块,每个块都有相同数量的文档。

迁移:当分片中的数据大小不一时会产生迁移的动作,比如分片A的数据比较多,会将分片A里面的一些块转移到分片B里面去。分片集群通过在分片中移动块来实现均衡,是由名为均衡器的软件进程管理的,任务是确保数据在各个分片中保持均匀分布,当集群中拥有块最多的分片与拥有块最少分片的块差大于8时,均衡器就会发起一次均衡处理。

启动两个副本集、三个配置服务器、一个mongos进程

配置分片

(1)分片查询类型

(2)索引

分片集合只允许在_id字段和分片键上添加唯一性索引,其他地方不行,因为这需要在分片间进行通信,实施起来很复杂。

当创建分片时,会根据分片键创建一个索引。

(1)分片键是不可修改的、分片键的选择非常重要

(2)低效的分片键

(3)理想的分片键

(1)部署拓扑

根据不同的数据中心划分

这里写图片描述

(2)最低要求

(3)配置的注意事项

需要估计集群大小,可使用以下命令对现有集合进行分片处理

(4)备份分片集群

备份分片时需要停止均衡器

(1)部署架构

使用64位机器、32位机器会制约mongodb的内存,使其最大值为1.5GB

(2)cpu

mongodb 只有当索引和工作集都可放入内存时,才会遇到CPU瓶颈,CPU在mongodb使用中的作用是用来检索数据,如果看到CPU使用饱和的情况,可以通过查询慢查询日志,排查是不是查询的问题导致的,如果是可以通过添加索引来解决问题

mongodb写入数据时会使用到CPU,但是mongodb写入时间一次只用到一个核,如果有频繁的写入行为,可以通过分片来解决这个问题

(3)内存

大内存是mongodb的保障,如果工作集大小超过内存,将会导致性能下降,因为这将会增加数据加载入内存的动作

(4)硬盘

mongodb默认每60s会与磁盘强制同步一次,称为后台刷新,会产生I/O操作。在重启时mongodb会将磁盘里面的数据加载至内存,高速磁盘将会减少同步的时间

(5)文件系统

使用ext4 和 xfs 文件系统

禁用最后访问时间

(6)文件描述符

linux 默认文件描述符是1024,需要大额度的提升这个额度

(7)时钟

mongodb各个节点服务器之间使用ntp服务器

(1)绑定IP

启动时使用 - -bind_ip 命令

(2)身份验证

启动时使用 - -auth 命令

(3)副本集身份认证

使用keyFile,注意keyFile文件的权限必须是600,不然会启动不起来

(1)拓扑结构

搭建副本集至少需要两个节点,其中仲裁结点不需要有自己的服务器

(2)Journaling日志

写数据时会先写入日志,而此时的数据也不是直接写入硬盘,而是写入内存

但是Journaling日志会消耗内存,所以可以在主库上面关闭,在从库上面启动

可以单独为Journaling日志使用一块固态硬盘

在插入时,可以通过驱动确保Journaling插入后再反馈,但是会非常影响性能。

logpath 选项指定日志存储地址

-vvvvv 选项(v越多,输出越详细)

db.runCommand({logrotare:1}) 开启滚动日志

(1)serverStatus

这里写图片描述

(2)top

(3)db.currentOp()

动态展示mongodb活动数据

占用当前mongodb监听端口往上1000号的端口

(1)mongomp

把数据库内容导出成BSON文件,而mongorestore能读取并还原这些文件

(2)mongorestore

把导出的BSON文件还原到数据库

(3)备份原始数据文件

可以这么做,但是,操作之前需要进行锁库处理 db.runCommand({fsync:1,lock:true})

db.$cmd.sys.unlock.findOne() 请求解锁操作,但是数据库不会立刻解锁,需要使用db.currentOp()验证。

(1)修复

mongd --repair 修复所有数据库

db.runCommand({repairDatabase:1}) 修复单个数据库

修复就是根据Jourling文件读取和重写所有数据文件并重建各个索引

(2)压紧

压紧,会重写数据文件,并重建集合的全部索引,需要停机或者在从库上面运行,如果需要在主库上面运行,需要添加force参数 保证加写锁。

(1)监控磁盘状态

(2)为提升性能检查索引和查询

总的来说,扫描尽可能少的文档。

保证没有冗余的索引,冗余的索引会占用磁盘空间、消耗更多的内存,在每次写入时还需做更多工作

(3)添加内存

dataSize 数据大小 和 indexSize 索引大小,如果两者的和大于内存,那么将会影响性能。

storageSize超过dataSize 数据大小 两倍以上,就会因磁盘碎片而影响性能,需要压缩。

MongoDB是什么,怎么用?看完你就知道了

MongoDB是一款为web应用程序和互联网基础设施设计的数据库管理系统。没错MongoDB就是数据库,是NoSQL类型的数据库。

(1)MongoDB提出的是文档、集合的概念,使用BSON(类JSON)作为其数据模型结构,其结构是面向对象的而不是二维表,存储一个用户在MongoDB中是这样子的。

使用这样的数据模型,使得MongoDB能在生产环境中提供高读写的能力,吞吐量较于mysql等SQL数据库大大增强。

(2)易伸缩,自动故障转移。易伸缩指的是提供了分片能力,能对数据集进行分片,数据的存储压力分摊给多台服务器。自动故障转移是副本集的概念,MongoDB能检测主节点是否存活,当失活时能自动提升从节点为主节点,达到故障转移。

(3)数据模型因为是面向对象的,所以可以表示丰富的、有层级的数据结构,比如博客系统中能把“评论”直接怼到“文章“的文档中,而不必像myqsl一样创建三张表来描述这样的关系。

(1)文档数据类型

SQL类型的数据库是正规化的,可以通过主键或者外键的约束保证数据的完整性与唯一性,所以SQL类型的数据库常用于对数据完整性较高的系统。MongoDB在这一方面是不如SQL类型的数据库,且MongoDB没有固定的Schema,正因为MongoDB少了一些这样的约束条件,可以让数据的存储数据结构更灵活,存储速度更加快。

(2)即时查询能力

MongoDB保留了关系型数据库即时查询的能力,保留了索引(底层是基于B tree)的能力。这一点汲取了关系型数据库的优点,相比于同类型的NoSQL redis 并没有上述的能力。

(3)复制能力

MongoDB自身提供了副本集能将数据分布在多台机器上实现冗余,目的是可以提供自动故障转移、扩展读能力。

(4)速度与持久性

MongoDB的驱动实现一个写入语义 fire and forget ,即通过驱动调用写入时,可以立即得到返回得到成功的结果(即使是报错),这样让写入的速度更加快,当然会有一定的不安全性,完全依赖网络。

MongoDB提供了Journaling日志的概念,实际上像mysql的bin-log日志,当需要插入的时候会先往日志里面写入记录,再完成实际的数据操作,这样如果出现停电,进程突然中断的情况,可以保障数据不会错误,可以通过修复功能读取Journaling日志进行修复。

(5)数据扩展

MongoDB使用分片技术对数据进行扩展,MongoDB能自动分片、自动转移分片里面的数据块,让每一个服务器里面存储的数据都是一样大小。

MongoDB核心服务器主要是通过mongod程序启动的,而且在启动时不需对MongoDB使用的内存进行配置,因为其设计哲学是内存管理最好是交给操作系统,缺少内存配置是MongoDB的设计亮点,另外,还可通过mongos路由服务器使用分片功能。

MongoDB的主要客户端是可以交互的js shell 通过mongo启动,使用js shell能使用js直接与MongoDB进行交流,像使用sql语句查询mysql数据一样使用js语法查询MongoDB的数据,另外还提供了各种语言的驱动包,方便各种语言的接入。

mongomp和mongorestore,备份和恢复数据库的标准工具。输出BSON格式,迁移数据库。

mongoexport和mongoimport,用来导入导出JSON、CSV和TSV数据,数据需要支持多格式时有用。mongoimport还能用与大数据集的初始导入,但是在导入前顺便还要注意一下,为了能充分利用好mongoDB通常需要对数据模型做一些调整。

mongosniff,网络嗅探工具,用来观察发送到数据库的操作。基本就是把网络上传输的BSON转换为易于人们阅读的shell语句。

因此,可以总结得到,MongoDB结合键值存储和关系数据库的最好特性。因为简单,所以数据极快,而且相对容易伸缩还提供复杂查询机制的数据库。MongoDB需要跑在64位的服务器上面,且最好单独部署,因为是数据库,所以也需要对其进行热备、冷备处理。

因为本篇文章不是API手册,所有这里对shell的使用也是基础的介绍什么功能可以用什么语句,主要是为了展示使用MongoDB shell的方便性,如果需要知道具体的MongoDB shell语法可以查阅官方文档。

创建数据库并不是必须的操作,数据库与集合只有在第一次插入文档时才会被创建,与对数据的动态处理方式是一致的。简化并加速开发过程,而且有利于动态分配命名空间。如果担心数据库或集合被意外创建,可以开启严格模式。

以上的命令只是简单实例,假设如果你之前没有学习过任何数据库语法,同时开始学sql查询语法和MongoDB 查询语法,你会发现哪一个更简单呢?如果你使用的是java驱动去操作MongoDB,你会发现任何的查询都像Hibernate提供出来的查询方式一样,只要构建好一个查询条件对象,便能轻松查询(接下来会给出示例),博主之前熟悉ES6,所以入手MongoDB js shell完成没问题,也正因为这样简洁,完善的查询机制,深深的爱上了MongoDB。

使用java驱动链接MongoDB是一件非常简单的事情,简单的引用,简单的做增删改查。在使用完java驱动后我才发现spring 对MongoDB 的封装还不如官方自身提供出来的东西好用,下面简单的展示一下使用。

这里只举例了简单的链接与简单的MongoDB操作,可见其操作的容易性。使用驱动时是基于TCP套接字与MongoDB进行通信的,如果查询结果较多,恰好无法全部放进第一服务器中,将会向服务器发送一个getmore指令获取下一批查询结果。

插入数据到服务器时间,不会等待服务器的响应,驱动会假设写入是成功的,实际是使用客户端生成对象id,但是该行为可以通过配置配置,可以通过安全模式开启,安全模式可以校验服务器端插入的错误。

要清楚了解MongoDB的基本数据单元。在关系型数据库中有带列和行的数据表。而MongoDB数据的基本单元是BSON文档,在键值中有指向不定类型值的键,MongoDB拥有即时查询,但不支持联结操作,简单的键值存储只能根据单个键来获取值,不支持事务,但支持多种原子更新操作。

如读写比是怎样的,需要何种查询,数据是如何更新的,会不会存在什么并发问题,数据结构化的程度是要求高还是低。系统本身的需求决定mysql还是MongoDB。

在关于schema 的设计中要注意一些原则,比如:

数据库是集合的逻辑与物理分组,MongoDB没有提供创建数据库的语法,只有在插入集合时,数据库才开始建立。创建数据库后会在磁盘分配一组数据文件,所有集合、索引和数据库的其他元数据都保存在这些文件中,查阅数据库使用磁盘状态可通过。

集合是结构上或概念上相似得文档的容器,集合的名称可以包含数字、字母或 . 符号,但必须以字母或数字开头,完全。

限定集合名不能超过128个字符,实际上 . 符号在集合中很有用,能提供某种虚拟命名空间,这是一种组织上的原则,和其他集合是一视同仁的。在集合中可以使用。

其次是键值,在MongoDB里面所有的字符串都是UTF-8类型。数字类型包括double、int、long。日期类型都是UTC格式,所以在MongoDB里面看到的时间会比北京时间慢8小时。整个文档大小会*在16m以内,因为这样可以防止创建难看的数据类型,且小文档可以提升性能,批量插入文档理想数字范围是10~200,大小不能超过16MB。

(1)索引能显著减少获取文档的所需工作量,具体的对比可以通过 .explain()方法进行对比

(2)解析查询时MongoDB通过最优计划选择一个索引进行查询,当没有最适合索引时,会先不同的使用各个索引进行查询,最终选出一个最优索引做查询

(3)如果有一个a-b的复合索引,那么仅针对a的索引是冗余的

(4)复合索引里的键的顺序是很重要的

(1)单键索引

(2)复合索引

(3)唯一性索引

(4)稀疏索引

如索引的字段会出现null的值,或是大量文档都不包含被索引的键。

如果数据集很大时,构建索引将会花费很长的时间,且会影响程序性能,可通过

当使用 mongorestore 时会重新构建索引。当曾经执行过大规模的删除时,可使用

对索引进行压缩,重建。

(1)查阅慢查询日志

(2)分析慢查询

注意新版本的MongoDB 的explain方法是需要参数的,不然只显示普通的信息。

本节同样主要简单呈现MongoDB副本集搭建的简易性,与副本集的强壮性,监控容易性

提供主从复制能力,热备能力,故障转移能力

实际上MongoDB对副本集的操作跟mysql主从操作是差不多的,先看一下mysql的主从数据流动过程

而MongoDB主要依赖的日志文件是oplog

写操作先被记录下来,添加到主节点的oplog里。与此同时,所有从结点复制oplog。首先,查看自己oplog里最后一条的时间戳;其次,查询主节点oplog里所有大于此时间戳的条目;最后,把那些条目添加到自己的oplog里并应用到自己的库里。从节点使用长轮询立即应用来自主结点oplog的新条目。

当遇到以下情况,从节点会停止复制

local数据库保存了所有副本集元素据和oplog日志

可以使用以下命令查看复制情况

每个副本集成员每秒钟ping一次其他所有成员,可以通过rs.status()看到节点上次的心跳检测时间戳和 健康 状况。

这个点没必要过多描述,但是有一个特殊场景,如果从节点和仲裁节点都被杀了,只剩下主节点,他会把自己降级成为从节点。

如果主节点的数据还没有写到从库,那么数据不能算提交,当该主节点变成从节点时,便会触发回滚,那些没写到从库的数据将会被删除,可以通过rollback子目录中的BSON文件恢复回滚的内容。

(1)使用单节点链接

只能链接到主节点,如果链接到从节点的话,会被拒绝写入操作,但是如果没有使用安全模式,因为mongo的fire and forget 特性,会把拒绝写入的异常给吃掉。

(2)使用副本集方式链接

能根据写入的情况自动进行故障转移,但是当副本集进行新的选举时,还是会出现故障,如果不使用安全模式,依旧会出现写不进去,但现实成功的情况。

分片是数据库切分的一个概念实现,这里也是简单总结为什么要使用分片以及分片的原理,操作。

当数据量过大,索引和工作数据集占用的内存就会越来越多,所以需要通过分片负载来解决这个问题

(1)分片组件

(2)分片的核心操作

分片一个集合:分片是根据一个属性的范围进行划分的,MongoDB使用所谓的分片键让每个文档在这些范围里找到自己的位置

块:是位于一个分片中的一段连续的分片键范围,可以理解为若干个块组成分片,分片组成MongoDB的全部数据

(3)拆分与迁移

块的拆分:初始化时只有一个块,达到最大块尺寸64MB或100000个文档就会触发块的拆分。把原来的范围一分为二,这样就有了两个块,每个块都有相同数量的文档。

迁移:当分片中的数据大小不一时会产生迁移的动作,比如分片A的数据比较多,会将分片A里面的一些块转移到分片B里面去。分片集群通过在分片中移动块来实现均衡,是由名为均衡器的软件进程管理的,任务是确保数据在各个分片中保持均匀分布,当集群中拥有块最多的分片与拥有块最少分片的块差大于8时,均衡器就会发起一次均衡处理。

启动两个副本集、三个配置服务器、一个mongos进程

配置分片

(1)分片查询类型

(2)索引

分片集合只允许在_id字段和分片键上添加唯一性索引,其他地方不行,因为这需要在分片间进行通信,实施起来很复杂。

当创建分片时,会根据分片键创建一个索引。

(1)分片键是不可修改的、分片键的选择非常重要

(2)低效的分片键

(3)理想的分片键

(1)部署拓扑

根据不同的数据中心划分

这里写图片描述

(2)最低要求

(3)配置的注意事项

需要估计集群大小,可使用以下命令对现有集合进行分片处理

(4)备份分片集群

备份分片时需要停止均衡器

(1)部署架构

使用64位机器、32位机器会制约mongodb的内存,使其最大值为1.5GB

(2)cpu

mongodb 只有当索引和工作集都可放入内存时,才会遇到CPU瓶颈,CPU在mongodb使用中的作用是用来检索数据,如果看到CPU使用饱和的情况,可以通过查询慢查询日志,排查是不是查询的问题导致的,如果是可以通过添加索引来解决问题

mongodb写入数据时会使用到CPU,但是mongodb写入时间一次只用到一个核,如果有频繁的写入行为,可以通过分片来解决这个问题

(3)内存

大内存是mongodb的保障,如果工作集大小超过内存,将会导致性能下降,因为这将会增加数据加载入内存的动作

(4)硬盘

mongodb默认每60s会与磁盘强制同步一次,称为后台刷新,会产生I/O操作。在重启时mongodb会将磁盘里面的数据加载至内存,高速磁盘将会减少同步的时间

(5)文件系统

使用ext4 和 xfs 文件系统

禁用最后访问时间

(6)文件描述符

linux 默认文件描述符是1024,需要大额度的提升这个额度

(7)时钟

mongodb各个节点服务器之间使用ntp服务器

(1)绑定IP

启动时使用 - -bind_ip 命令

(2)身份验证

启动时使用 - -auth 命令

(3)副本集身份认证

使用keyFile,注意keyFile文件的权限必须是600,不然会启动不起来

(1)拓扑结构

搭建副本集至少需要两个节点,其中仲裁结点不需要有自己的服务器

(2)Journaling日志

写数据时会先写入日志,而此时的数据也不是直接写入硬盘,而是写入内存

但是Journaling日志会消耗内存,所以可以在主库上面关闭,在从库上面启动

可以单独为Journaling日志使用一块固态硬盘

在插入时,可以通过驱动确保Journaling插入后再反馈,但是会非常影响性能。

logpath 选项指定日志存储地址

-vvvvv 选项(v越多,输出越详细)

db.runCommand({logrotare:1}) 开启滚动日志

(1)serverStatus

这里写图片描述

(2)top

(3)db.currentOp()

动态展示mongodb活动数据

占用当前mongodb监听端口往上1000号的端口

(1)mongomp

把数据库内容导出成BSON文件,而mongorestore能读取并还原这些文件

(2)mongorestore

把导出的BSON文件还原到数据库

(3)备份原始数据文件

可以这么做,但是,操作之前需要进行锁库处理 db.runCommand({fsync:1,lock:true})

db.$cmd.sys.unlock.findOne() 请求解锁操作,但是数据库不会立刻解锁,需要使用db.currentOp()验证。

(1)修复

mongd --repair 修复所有数据库

db.runCommand({repairDatabase:1}) 修复单个数据库

修复就是根据Jourling文件读取和重写所有数据文件并重建各个索引

(2)压紧

压紧,会重写数据文件,并重建集合的全部索引,需要停机或者在从库上面运行,如果需要在主库上面运行,需要添加force参数 保证加写锁。

(1)监控磁盘状态

(2)为提升性能检查索引和查询

总的来说,扫描尽可能少的文档。

保证没有冗余的索引,冗余的索引会占用磁盘空间、消耗更多的内存,在每次写入时还需做更多工作

(3)添加内存

dataSize 数据大小 和 indexSize 索引大小,如果两者的和大于内存,那么将会影响性能。

storageSize超过dataSize 数据大小 两倍以上,就会因磁盘碎片而影响性能,需要压缩。

mongoDB应用篇-mongo聚合查询

如果我们在日常操作中,将部分数据存储在了MongoDB中,但是有需求要求我们将存储进去的文档数据,按照一定的条件进行查询过滤,得到想要的结果便于二次利用,那么我们就可以尝试使用MongoDB的聚合框架。

前面我们在学习文档查询的过程中,也介绍过一些查询的操作符,其中就有一部分是简单的查询聚合函数,例如 count 、 distinct 、 group 等,如果是简单的数据分析过滤,完全可以使用这些自带的聚合函数以及查询的操作符来完成文档的过滤查询操作

如果我们遇到了一些数据需要跨多个文本或者统计等操作,这个时候可能文档自身也较为复杂,查询操作符已经无法满足的时候,这个时候就需要使用MongoDB的聚合查询框架了。

使用聚合框架可以对集合中的文档进行变换和组合查询,基本上我们使用的时候,都是使用多个构件创建一个管道,用于对一连串的文档进行处理。这里的构件包括 筛选(filter) 、 投射(projecting) 、 分组(grouping) 、 排序(sorting) 、 (limiting) 以及 跳过(skipping)

MongoDB中需要使用聚合操作,一般使用 aggregate 函数来完成多个聚合之间的连接,aggregate() 方法的基本语法格式如下 :

现在假设我们有个集合articles,里面存储了文章的集合,大致如下:

但这时我们需要查询出来每一个作者写的文章数量,需要使用aggregate()计算 ,大致如下:

输出的结果为:

通过这个简单的案例我们就能输出想要的数据和属性名,大概分析一下刚刚的聚合查询语句, by_user字段进行分组,代表每个用户一条数据,而num_tutorial则是定义了数值类型计算的结果字段,$sum则是计算总和,相当于每个用户出现一次,都会+1,最终计算出来的总和通过num_tutorial字段进行输出

注:如果管道没有给出预期的结果,就需要进行调试操作,调试的时候,可以尝试先给一个管道操作符的条件,如果这个时候查询出来的结果是我们想要的,那么我们需要再去指定第二个管道操作符,依次操作,最后就会定位到出了问题的操作符

前面我们提到聚合查询会使用管道操作符,而每一个操作符就会接受一连串的文档,对这些文档进行一些类型转换,最后将转换以后的文档结果传递给下一个管道操作符来执行后续的操作,如果当前是最后一个管道操作符,那么则会显示给用户最后的文档数据。不同的管道操作符是可以按照顺序组合在一起使用,并且可以被重复执行多次,例如我们可以先使用$match然后再去、 match操作。

match管道操作符可以使用$gt、$lt、$in等操作符,进行过滤,不过需要注意的是不能在$match管道操作符中使用空间地理操作符。

在实际使用的过程中,尽可能的将 match操作符以后,再去投射或者执行分组操作的话,是可以利用索引的。

相比较一般的查询操作而言,使用管道操作,尤其是其中的投射操作更加强大。我们可以在查询文档结束以后利用 $project 操作符从文档中进行字段的提取,甚至于我们可以重命名字段,将部分字段映射成我们想要展示出去的字段,也可以对一部分字段进行一些有意义的处理。需要注意的是, $project 操作符可以传入两个参数,第一个是需要处理的属性名称,第二个则是0或者1,如果传入1,则代表当前的属性是需要显示出来的,如果是0或者不写,默认都是代表这个字段不需要显示出来

当然第二个参数也可以是一个表达式或者查询条件,满足当前表达式的数据也可以进行显示,接下来我们先准备一点数据:

接下来,我们来查询,条件是item字段为abc,quantity要大于5,并且我们只要item和price字段的结果,其他都排除掉:

可以看到结果为:

如果我们想要在原基础上改变某个字段的名称,例如将item改为item_code,可以利用$来完成,如下:

可以看到我们指定的名称item_code,而这个别名对应的字段item使用$作为前缀标记,代表将item字段映射为item_code,可以看到结果:

我们在投影的时候,除了可以将某个字段映射成其他字段以外,还可以针对某个字段进行一些简单的运算,最常见的就是 四则运算 ,即

加法( subtract )、乘法( divide )、求模( $mod ) ,

除此之外,还支持对字段进行 关系运算 (大小比较( " eq" )、大于( " gte" )、小于( " lte" )、不等于( " ifNull" ) )、

逻辑运算 (与( " or" )、非 ( " concat" )、截取( " toLower" ) )等

我们基于上面的需求,假设每一个价格是按照元为单位,现在要求输出W为单位,那么我们就需要对price进行除法运算,如下:

除此之外,我们也可以将计算完毕的price改名为priceW,即:

可以看到输出的结果为:

这时有一个需求,要求我们返回数据的同时还要yyyy-MM-dd格式的时间字符串,这个时候我们就需要对date字段进行时间函数和字符串混合处理了,如下:

这里需要注意的一点是, year:" substr函数将date字段的结果截取成字符串即可实现拼接

group的_id上,代表按照当前字段进行分组,例如,我们这里根据item进行分组:

在我们针对某个字段进行分组以后,我们可以针对每个分组进行一些操作符的使用,常见的例如: $sum 、 $avg 、 $min 、 $max 、 $first 、 $last 。

$avg 操作符用来返回每一个分组内的平均值

现在我们基于前面item的分组,我们想要算出来每个组内的平均价格是多少,如下:

$min 和 $max 操作符用于返回分组内最大的值和最小的值

除了平均值以外,我们现在将最贵的和最便宜的价格也要列出来,这个时候就可以使用这两个操作符了,如下:

$first 、 $last 则是可以获取当前分组中第一个或者最后一个的某个字段的结果,如下:

除此之外,我们还可以在分组的时候使用数组操作符,例如 $addToSet 可以判断,当前数组如果不包含某个条件,就添加到当前数组中, $push 则不管元素是否存在,都直接添加到数组中

注意:大部分管道操作符都是流式处理的,只要有新的文档进入,就可以对新的文档进行处理,但是 $group 代表必须收到全部文档以后才可以进行分组操作,才会将结果传递给后续的管道操作符,这就意味着,如果当前mongo是存在分片的,会先在每个分片上执行完毕以后,再把结果传递mongos进行统一的分组,剩下的管道操作符也不会在每个分片,而是mongos上执行了

如果我们现在遇到一些文档比较复杂,比如存在内嵌文档的存在,某个属性里面嵌套了一个数组,但是我们需要对内嵌的数组文档进行分析过滤等查询处理,这个时候就可以使用 $unwind 操作符将每一个文档中的嵌套数组文件拆分为一个个的文档便于进行后续的处理,例如我们需要将之前的set集合中关于请求的url以及ip的信息拆分出来,原始的格式如下:

我们可以使用命令进行拆分,如下:

结果为:

可以看到数据则是按照每一条信息的方式展示出来了,方便后续的计算以及输出,但是需要注意的一点是,这种方式,如果该文档中没有拆分的字段,或者是空数组,默认会直接排除,如果我们需要空数组等也输出计算出来,则可以指定 preserveNullAndEmptyArrays 参数,设置为true,则代表空数组或者不存在的文档也要拆分输出出来,即:

我们可以在管道查询的过程中,按照某个属性值或者多个属性的结果进行顺序排序,排序的方式与普通查询操作符中的sort操作符表现一致,与其他管道操作符一样,可以在任何阶段使用,但是,需要注意的一点是,建议在管道操作符第一阶段进行排序,因为此时的排序是可以触发索引的,如果在后续阶段进行排序,会消耗大量内存,并且耗时会很久,尤其是在有 $group 的情况下,如果放在 $group 操作符后面,会发现等到的时间很久,不仅仅是无法触发索引的问题,还和 $group 操作符是等待所有数据完毕才会触发的特性有关,因此需要格外注意。

结果如下,按照我们想要的结果进行了排序:

limit,只返回前两条数据,如下:

结果如下:

除了 skip,与之前的查询操作符作用也是一样的,用于在已经查询完毕的结果集中跳过前N条数据以后进行返回,我们将$skip加在刚刚的查询后面,如下:

这个时候可以看到返回的结果为空,什么结果都没有了,这是因为前一步管道已经了仅仅返回2条,而接着我们又跳过了前两条文档,因此返回的结果为空,我们将顺序调换一下,看看:

可以看到结果如下,与刚才的结果无异:

管道查询操作符有很多,除了上面学习的常用的部分,还有几十个,需要了解全部的可以参考官网:

https://docs.mongodb.com/manual/reference/command/aggregate/

除此之外,我们在学习的过程中了解到,部分查询操作符是可以触发索引的,例如 $project 、 $group 或者 $unwind 操作符,因此我们也建议 如果可以的话,尽量先使用这类管道操作符进行数据过滤,可以有效减少数据集大小和数量,而且管道如果不是直接从原先的集合中使用数据,那就无

法在筛选和排序中使用索引 ,例如我们先进行管道操作,再去将过滤好的数据进行 $sort 排序,会导致无法使用索引,效率大幅度下降,因此如果我们需要涉及到 $sort 操作的时候,如果可以尽可能在最开始就处理,这个时候可以使用索引,效率较高,然后再去进行管道查询筛选与分组等其他操作,可以有效的提高查询的效率。另外需要注意的一点是,在 MongoDB中会对每一个管道查询做,例如某一步管道查询操作导致内存占用超过20%,这个时候就会报错,无法继续使用管道 ,因为mongoDB本身每次最大是16Mb的数据量,为了尽可能避免或者减少这种问题,建议可以考虑尽可能的使用 $match 操作符过滤无用数据,减少数据总大小。同时也 因为管道查询是多步执行,例如 $group 则是等待所有数据完毕才会执行,因此可能会导致整体执行时间较久 ,也因为这样,才不建议在较高的实时查询需求上使用管道和查询,而是在 设计的时候尽可能直接使用查询操作符进行数据查询,触发更多的索引,更快的销量查询出来想要的结果。

mongodb使用场景是什么?

使用场景:

(1)网站数据:MongoDB适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。

(2)缓存:由于性能很高,MongoDB也适合作为信息基础设施的缓存层。在系统重启之后,由MongoDB搭建的持久化缓存层可以避免下层的数据源过载。

(3)大尺寸,低价值的数据。

(4)高伸缩性的场景:MongoDB适合由数十或数百台服务器组成的数据库。

(5)用于对象及JSON数据的存储:MongoDB的BSON数据格式适合文档化格式的存储及查询。

mongodb设计特点:

(1)面向集合存储,容易存储对象类型的数据。在MongoDB 中数据被分组存储在集合中,集合类似RDBMS 中的表,一个集合中可以存储无限多的文档。

(2)模式自由,采用无模式结构存储。在MongoDB 中集合中存储的数据是无模式的文档,采用无模式存储数据是集合区别于RDBMS 中的表的一个重要特征。

(3)支持完全索引,可以在任意属性上建立索引,包含内部对象。MongoDB的索引和RDBMS 的索引基本一样,可以在指定属性、内部对象上创建索引以提高查询的速度。除此之外,MongoDB 还提供创建基于地理空间的索引的能力。

(4)支持查询。MongoDB 支持丰富的查询操作,MongoDB 几乎支持SQL中的大部分查询。

(5)强大的聚合工具。MongoDB 除了提供丰富的查询功能外,还提供强大的聚合工具,如count、group 等,支持使用MapRece 完成复杂的聚合任务。

mongodb使用场景是什么?

使用场景:

(1)网站数据:MongoDB适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。

(2)缓存:由于性能很高,MongoDB也适合作为信息基础设施的缓存层。在系统重启之后,由MongoDB搭建的持久化缓存层可以避免下层的数据源过载。

(3)大尺寸,低价值的数据。

(4)高伸缩性的场景:MongoDB适合由数十或数百台服务器组成的数据库。

(5)用于对象及JSON数据的存储:MongoDB的BSON数据格式适合文档化格式的存储及查询。

mongodb设计特点:

(1)面向集合存储,容易存储对象类型的数据。在MongoDB 中数据被分组存储在集合中,集合类似RDBMS 中的表,一个集合中可以存储无限多的文档。

(2)模式自由,采用无模式结构存储。在MongoDB 中集合中存储的数据是无模式的文档,采用无模式存储数据是集合区别于RDBMS 中的表的一个重要特征。

(3)支持完全索引,可以在任意属性上建立索引,包含内部对象。MongoDB的索引和RDBMS 的索引基本一样,可以在指定属性、内部对象上创建索引以提高查询的速度。除此之外,MongoDB 还提供创建基于地理空间的索引的能力。

(4)支持查询。MongoDB 支持丰富的查询操作,MongoDB 几乎支持SQL中的大部分查询。

(5)强大的聚合工具。MongoDB 除了提供丰富的查询功能外,还提供强大的聚合工具,如count、group 等,支持使用MapRece 完成复杂的聚合任务。

怎样学 MongoDB

接下来这段时间,我将会学习mongodb,由浅入深,并且将学习的过程会详细记录下来。mongodb是什么?我想这一点不需要我过多的解释。好了,废话不多说,我们直奔主题,看看mongodb开发环境的搭建。说明一点,在学习mongodb期间,我的所有 操作都是在windows下完成的。下面我带大家一步一步学习windows下mongodb开发环境的搭建

下载mongodb

首先我们需要到mongodb的官网下载mongodb。
http://www.mongodb.org/downloads

新建一个文件夹mongodb

我在D盘下,新建了一个叫做mongodb的文件夹,该文件夹是以后用来存放mongodb的安装文件用的。之后解压缩我们下载的mongodb,并且将解压缩后的所有文件都copy到该文件夹下面。如图:
技术分享

新建一个文件夹mongodbData

我在D盘下新建一个mongodbData文件夹,该文件夹的作用是用来存放mongodb的数据的,包括了集合数据(也就是mysql中对应的表数据),和日志文件。

如何安装mongodb

我们可以发现在mongodb文件解压以后,会有一个bin目录,在该目录下有很多exe文件我们首先在命令行下进入该目录,执行如下命令:
mongod.exe –dbpath D:mongodbData
技术分享
解释一下,这里的”–dbpath D:mongodbData”,中的参数表示mongodb的数据文件存放的路径,这里我写的是刚才新建的目录,此时按回车键。会出现如下图所示的界面:
技术分享
在最后我们可以看出mongodb的默认端口是27017
此时在D盘下的mongodbData文件夹下会出现如下文件:
技术分享
至此,mongodb就安装完成了,不过还需要配置环境变量.

配置环境变量

将之前解压缩后的mongodb的bin目录配置到path环境变量当中,配置过jdk环境变量的同学应该都知道怎么操作,这里我就不多说了,如图:
技术分享
我们进入cmd命令行下输入:”mongod -help”出现如下界面,表示我们的环境变量配置成功。
技术分享

到现在为止,我们的mongodb已经成功的安装和配置成功,并且也指定了数据的存放位置,那么我们应该如何启动mongodb数据库呢??

启动mongodb数据库

进入cmd命令行,输入mongod –dbpath D:mongodbData 注意这里是两个”–”
技术分享
可以看到这时候,我们的mongodb服务已经开启了,并且会监听默认的端口27017,注意如果我们是”web”访问的话,需要加上1000,也就是28017端口。

mongodb简单入门

我们先来对比一下在mongodb和mysql中的存储等区别。
技术分享
目前在网络上传输的json数据格式是很常见的,在mongodb中,我们的数据的存储格式是在json的基础上增加的,也就是在mongodb中,存储的数据格式和json完全相同,都是通过”key:value”的形式来保存的,我们称之为”Gson”,在Gson中增加了很多种数据类型。在mongodb中,每一个文档(也就是mysql中的一行数据),可以包含不同的数据,在这也就是为什么mongodb具很强的扩展性的原因。

创建一个mongodb客户端

在正式创建数据库之前,我们需要创建一个mongodb客户端。
创建客户端也很简单,我们只需要在cmd命令行下输入”mongo 127.0.0.1 :27017 “即可,注意,在启动该客户端之前,必须先用命令”mongod –dbpath D:mongodbData”启动mongodb服务
技术分享
可以看到系统会默认使用”test”这个用户,如果我们需要切换到”admin”超级管理员,可以输入该命令,我重新打开一个cmd窗口输入”mongo 127.0.0.1:27017/admin”
技术分享

创建数据库添加数据

在mongodb中创建数据库很简单只需要输入”use 数据库名称”即可,可是,如果我们没有为该数据库添加任何数据的时候,当我们关闭客户端的时候,该数据库将会消失。我依次输入如下命令:
use mydb //创建数据库,如果此时没有添加任何数据,退出客户端的话,该数据库将会被删除。
db.students.insert({name:”zhangsan”}) //db表示mydb,students表示集合(对应mysql中的表),我们为students集合中添加一个key=”name”,value=”zhangsan”的文档
show dbs //查看当前系统中有那些数据库
show collections //查询系统有哪些集合
db.students.find() //查看集合中的数据
db.students.findOne() //查看集合中的第一条数据
技术分享
可以看到,我为students集合当中添加了一两条数据,系统自动为我加上了一个叫做”_id”的key

那么我如何更改数据呢??如下图:
技术分享
同种红色边框里面的就是我的修改的语句,这里用到了修改器”$set”,如果不用修改器,将会出现覆盖已有数据的现象,这样显然是不合理的。可以看到我将name=”zhangsan”的文档修改为name=”lisi”,可是细心的程序猿可能会发现当我再次查询的时候只是修改了第一条数据,这个我在后续的博客中会详细说明的。

如果需要删除数据,应该怎么做呢???比如我需要删除name=”zhangsan”的数据,我可以这样写:
db.persons.remove({name:”zhangsan”})
技术分享

好了,今天的mongodb搭建开发环境+简单入门,就到这里了,希望大家能够喜欢。

mongodb学习(搭建开发环境+简单入门)

标签:mongodb   环境搭建   nosql   

怎样学 MongoDB

接下来这段时间,我将会学习mongodb,由浅入深,并且将学习的过程会详细记录下来。mongodb是什么?我想这一点不需要我过多的解释。好了,废话不多说,我们直奔主题,看看mongodb开发环境的搭建。说明一点,在学习mongodb期间,我的所有 操作都是在windows下完成的。下面我带大家一步一步学习windows下mongodb开发环境的搭建

下载mongodb

首先我们需要到mongodb的官网下载mongodb。
http://www.mongodb.org/downloads

新建一个文件夹mongodb

我在D盘下,新建了一个叫做mongodb的文件夹,该文件夹是以后用来存放mongodb的安装文件用的。之后解压缩我们下载的mongodb,并且将解压缩后的所有文件都copy到该文件夹下面。如图:
技术分享

新建一个文件夹mongodbData

我在D盘下新建一个mongodbData文件夹,该文件夹的作用是用来存放mongodb的数据的,包括了集合数据(也就是mysql中对应的表数据),和日志文件。

如何安装mongodb

我们可以发现在mongodb文件解压以后,会有一个bin目录,在该目录下有很多exe文件我们首先在命令行下进入该目录,执行如下命令:
mongod.exe –dbpath D:mongodbData
技术分享
解释一下,这里的”–dbpath D:mongodbData”,中的参数表示mongodb的数据文件存放的路径,这里我写的是刚才新建的目录,此时按回车键。会出现如下图所示的界面:
技术分享
在最后我们可以看出mongodb的默认端口是27017
此时在D盘下的mongodbData文件夹下会出现如下文件:
技术分享
至此,mongodb就安装完成了,不过还需要配置环境变量.

配置环境变量

将之前解压缩后的mongodb的bin目录配置到path环境变量当中,配置过jdk环境变量的同学应该都知道怎么操作,这里我就不多说了,如图:
技术分享
我们进入cmd命令行下输入:”mongod -help”出现如下界面,表示我们的环境变量配置成功。
技术分享

到现在为止,我们的mongodb已经成功的安装和配置成功,并且也指定了数据的存放位置,那么我们应该如何启动mongodb数据库呢??

启动mongodb数据库

进入cmd命令行,输入mongod –dbpath D:mongodbData 注意这里是两个”–”
技术分享
可以看到这时候,我们的mongodb服务已经开启了,并且会监听默认的端口27017,注意如果我们是”web”访问的话,需要加上1000,也就是28017端口。

mongodb简单入门

我们先来对比一下在mongodb和mysql中的存储等区别。
技术分享
目前在网络上传输的json数据格式是很常见的,在mongodb中,我们的数据的存储格式是在json的基础上增加的,也就是在mongodb中,存储的数据格式和json完全相同,都是通过”key:value”的形式来保存的,我们称之为”Gson”,在Gson中增加了很多种数据类型。在mongodb中,每一个文档(也就是mysql中的一行数据),可以包含不同的数据,在这也就是为什么mongodb具很强的扩展性的原因。

创建一个mongodb客户端

在正式创建数据库之前,我们需要创建一个mongodb客户端。
创建客户端也很简单,我们只需要在cmd命令行下输入”mongo 127.0.0.1 :27017 “即可,注意,在启动该客户端之前,必须先用命令”mongod –dbpath D:mongodbData”启动mongodb服务
技术分享
可以看到系统会默认使用”test”这个用户,如果我们需要切换到”admin”超级管理员,可以输入该命令,我重新打开一个cmd窗口输入”mongo 127.0.0.1:27017/admin”
技术分享

创建数据库添加数据

在mongodb中创建数据库很简单只需要输入”use 数据库名称”即可,可是,如果我们没有为该数据库添加任何数据的时候,当我们关闭客户端的时候,该数据库将会消失。我依次输入如下命令:
use mydb //创建数据库,如果此时没有添加任何数据,退出客户端的话,该数据库将会被删除。
db.students.insert({name:”zhangsan”}) //db表示mydb,students表示集合(对应mysql中的表),我们为students集合中添加一个key=”name”,value=”zhangsan”的文档
show dbs //查看当前系统中有那些数据库
show collections //查询系统有哪些集合
db.students.find() //查看集合中的数据
db.students.findOne() //查看集合中的第一条数据
技术分享
可以看到,我为students集合当中添加了一两条数据,系统自动为我加上了一个叫做”_id”的key

那么我如何更改数据呢??如下图:
技术分享
同种红色边框里面的就是我的修改的语句,这里用到了修改器”$set”,如果不用修改器,将会出现覆盖已有数据的现象,这样显然是不合理的。可以看到我将name=”zhangsan”的文档修改为name=”lisi”,可是细心的程序猿可能会发现当我再次查询的时候只是修改了第一条数据,这个我在后续的博客中会详细说明的。

如果需要删除数据,应该怎么做呢???比如我需要删除name=”zhangsan”的数据,我可以这样写:
db.persons.remove({name:”zhangsan”})
技术分享

好了,今天的mongodb搭建开发环境+简单入门,就到这里了,希望大家能够喜欢。

mongodb学习(搭建开发环境+简单入门)

标签:mongodb   环境搭建   nosql   

【Python基础】mongodb存储文件的优缺点?

MongoDB是一个开源的、基于分布式的、面向文档存储的非关系型数据库。是非关系型数据库当中功能最丰富、最像关系数据库的。MongoDB高性能、易部署、易使用,存储数据非常方便。
MongoDB优点:
1、高性能:弱一致性,访问速度较快
2、文档结构的存储方式,能够更便捷的获取数、存储数据方便,高效存储二进制大对象
3、支持复制集、主备、互为主备、自动分片等特性
4、全索引支持,查询语言功能非常强大

MongoDB缺点:
1、不支持事务,实际开发时得搞清楚哪些功能需要使用数据库提供的事务支持
2、MongoDB占用空间大(需要强大硬盘支持)
3、相对于MySQL那样成熟的维护工具,MongoDB维护工具不够完善、成熟

【Python基础】mongodb存储文件的优缺点?

MongoDB是一个开源的、基于分布式的、面向文档存储的非关系型数据库。是非关系型数据库当中功能最丰富、最像关系数据库的。MongoDB高性能、易部署、易使用,存储数据非常方便。
MongoDB优点:
1、高性能:弱一致性,访问速度较快
2、文档结构的存储方式,能够更便捷的获取数、存储数据方便,高效存储二进制大对象
3、支持复制集、主备、互为主备、自动分片等特性
4、全索引支持,查询语言功能非常强大

MongoDB缺点:
1、不支持事务,实际开发时得搞清楚哪些功能需要使用数据库提供的事务支持
2、MongoDB占用空间大(需要强大硬盘支持)
3、相对于MySQL那样成熟的维护工具,MongoDB维护工具不够完善、成熟

云上MongoDB常见索引问题及最优索引规则大全

本文干货较多,建议收藏学习。先将文章结构速览奉上:

一、背景

二、MongoDB执行计划

2.1 queryPlanner信息

2.2 executionStats信息

2.3 allPlansExecution信息

三、云上用户建索引常见问题及优化方法

3.1 等值类查询常见问题及优化方法

3.1.1 同一类查询创建多个索引问题

3.1.2 多字段等值查询组合索引顺序非最优

3.1.3 最左原则包含关系引起的重复索引

3.1.4 唯一字段和其他字段组合引起的无用重复索引

3.2 非等值类查询常见问题及优化方法

3.2.1 非等值组合查询索引不合理创建

3.2.2 等值+非等值组合查询索引字段顺序不合理

3.2.3 不同类型非等值查询优先级问题

3.3 OR类查询常见问题及优化方法

3.3.1 普通OR类查询优化方法

3.3.2 复杂OR类查询优化方法

3.4 SORT类排序查询常见问题及优化方法

3.4.1 单字段正反序排序查询引起的重复索引

3.4.2 多字段排序查询正反序问题引起索引无效

3.4.3 等值查询+多字段排序组合查询

3.4.4 等值查询+非等值查询+SORT排序查询

3.4.5 OR+SORT组合排序查询

3.5 无用索引优化方法

四、MongoDB不同类型查询最优索引总结

腾讯云MongoDB当前已服务于 游戏 、电商、社交、教育、新闻资讯、金融、物联网、软件服务、 汽车 出行、音视频等多个行业。

腾讯MongoDB团队在配合用户分析问题过程中,发现云上用户存在如下索引共性问题,主要集中在如下方面:

本文重点分析总结腾讯云上用户索引创建不合理相关的问题,通过本文可以学习到MongoDB的以下知识点:

本文总结的《最优索引规则创建大全》不仅仅适用于MongoDB,很多规则同样适用于MySQL等关系型数据库

判断索引选择及不同索引执行家伙信息可以通过explain操作获取,MongoDB通过explain来获取SQL执行过程信息,当前持续explain的请求命令包含以下几种:

aggregate, count, distinct, find, findAndModify, delete, mapRece, and update。

详见explain官网链接:

https://docs.MongoDB.com/manual/reference/command/explain/

explain可以携带以下几个参数信息,各参数信息功能如下:

2.1 queryPlanner信息

获取MongoDB查询优化器选择的最优索引和拒绝掉的非最优索引,并给出各个候选索引的执行阶段信息,queryPlanner输出信息如下:

queryPlanner输出主要包括如下信息:

parsedQuery信息

内核对查询条件进行序列化,生成一棵expression tree信息,便于候选索引查询匹配。

winningPlan信息

rejectedPlans信息

输出信息和winningPlan类似,记录这些拒绝掉索引的执行stage信息。

2.2 executionStats信息

explain的executionStats参数除了提供上面的queryPlanner信息外,还提供了最优索引的执行过程信息,如下:

上面是通过executionStats获取执行过程的详细信息,其中字段信息较多,平时分析索引问题最常用的几个字段如下:

executionStats输出字段较多,其他字段将在后续《MongoDB内核index索引模块实现原理》中进行进一步说明。

在实际分析索引问题是否最优的时候,主要查看以下三个统计项:

executionStats.totalKeysExamined

executionStats.totalDocsExamined

executionStats .nReturned

如果存在以下情况则说明索引存在问题,可能索引不是最优的:

1. executionStats.totalKeysExamine远大于executionStats .nReturned

2. executionStats. totalDocsExamined远大于executionStats .nReturned

2.3 allPlansExecution信息

allPlansExecution参数对应输出信息和executionStats输出信息类似,只是多了所有候选索引(包括reject拒绝的非最优索引)的执行过程,这里不再详述。

2.4 总结

从上面的几个explain执行计划参数输出信息可以看出,各个参数的功能各不相同,总结如下:

queryPlanner

输出索引的候选索引,包括最优索引及其执行stage过程(winningPlan)+其他非最优候选索引及其执行stage过程。

注意:queryPlanner没有真正在表中执行整个SQL,只做了查询优化器获取候选索引过程,因此可以很快返回。

executionStats

相比queryPlanner参数,executionStats会记录查询优化器根据所选最优索引执行SQL的整个过程信息,会真正执行整个SQL。

allPlansExecution

和executionStats类似,只是多了所有候选索引的执行过程。

在和用户一起优化腾讯云上MongoDB集群索引过程中,以及和头部用户的交流中发现很多用户对如何创建最优索引有较为严重的错误认识,并且很多是绝大部分用户的共性问题,因此在本文中将这些问题汇总如下:

3.1 等值类查询常见问题及优化方法

如下三个查询:

用户创建了如下3个索引:

{a:1, b:1, c:1}

{b:1, a:1, c:1}

{c:1, a:1, b:1}

实际上这3个查询属于同一类查询,只是查询字段顺序不一样,因此只需创建任一个索引即可满足要求。验证过程如下:

从上面的expalin输出可以看出,3个查询都走向了同一个索引。

例如test表有多条数据,每条数据有3个字段,分别为a、b、c。其中a字段有10种取值,b字段有100种取值,c字段有1000种取值,称为各个字段值的“区分度”

用户查询条件为db.test.find({"a":"xxx", "b":"xxx", "c":"xxx"}),创建的索引为{a:1, b:1, c:1}。如果只是针对这个查询,该查询可以创建a,b,c三字段的任意组合,并且其SQL执行代价一样,通过hint强制走不通索引,验证过程如下:

从上面的执行计划可以看出,多字段等值查询各个字段的组合顺序对应执行计划代价一样。绝大部分用户在创建索引的时候,都是直接按照查询字段索引组合对应字段。

但是,单就这一个查询,这里有个不成文的建议,把区分度更高的字段放在组合索引左边,区分度低的字段放到右边。这样做有个好处,数据库组合索引遵从最左原则,就是当其他查询里面带有区分度最高的字段时,就可以快速排除掉更多不满足条件的数据。

例如用户有如下两个查询:

用户创建了如下两个索引:

{b:1, c:1}

{a:1,b:1,c:1}

这两个查询中,查询2中包含有查询1中的字段,因此可以用一个索引来满足这两个查询要求,按照最左原则,查询1字段放左边即可,该索引可以优化为:b,c字段索引+a字段索引,b,c字段顺序可以根据区分排序,加上c字段区分度比b高,则这两个查询可以合并为一个{c:1, b:1, a:1}。两个查询可以走同一个索引验证过程如下:

从上面输出可以看出,这两个查询都走了同一个索引。

例如用户有以下两个查询:

用户为这两个查询创建了两个索引,{a:1, b:1}和{a:1, c:1},但是a字段取值是唯一的,因此这两个查询中a以外的字段无用,一个{a:1}索引即可满足要求。

3.2 非等值类查询常见索引错误创建方法及如何创建最优索引

假设用户有如下查询:

a,c两个字段都是非等值查询,很多用户直接添加了{a:1, c:1}索引,实际上多个字段的非等值查询,只有最左边的字段才能走索引,例如这里只会走a字段索引,验证过程如下:

从上面执行计划可以看出,索引数据扫描了10行(也就是a字段满足a:{$gte:1}条件的数据多少),但是实际上只返回了4条满足{a:{$gte:1}, c:{$lte:1}}条件的数据,可以看出c字段无法做索引。

同理,当查询中包含多个字段的范围查询的适合,除了最左边第一个字段可以走索引,其他字段都无法走索引。因此,上面例子中的查询候选索引为{a:1}或者{b:1}中任何一个就可以了,组合索引中字段太多会占用更多存储成本、同时占用更多IO资源引起写放大。

例如下面查询:

如上查询,d字段为非等值查询,e字段为等值查询,很多用户遇到该类查询直接创建了{d:1, e:1}索引,由于d字段为非等值查询,因此e字段无法走索引,验证过程如下:

从上面验证过程可以看出,等值类和非等值类组合查询对应组合索引,最优索引应该优先把等值查询放到左边,上面查询对应最优索引{e:1, d:1}

前面用到的非等值查询操作符只提到了比较类操作符,实际上非等值查询还有其他操作符。常用非等值查询包括:$gt、$gte、$lt、$lte、$in、$nin、$ne、$exists、$type等,这些非等值查询在绝大部分情况下存在如下优先级:

从上到下优先级更高,例如下面的查询:

如上,该查询等值部分查询最优索引{a:1, b:1}(假设a区分度比b高);非等值部分,因为$in操作符优先级最高,排他性更好,加上多个字段非等值查询只会有一个字段走索引,因此非等值部分最优索引为{g:1}。

最终该查询最优索引为:”等值部分最优索引”与”非等值部分最优索引”拼接,也就是{a:1,b:1, g:1}

3.3 OR类查询常见索引错误创建方法及如何创建最优索引

例如下面的OR查询:

该查询很多用户直接创建了{b:1, d:1, c:1, a:1},用户创建该索引后,发现用户还是全表扫描。

OR类查询需要给数组中每个查询添加索引,例如上面or数组中实际包含{ b: 0, d:0 }和 {"c":1, "a":{$gte:4}}查询,需要创建两个查询的最优索引,也就是{b:1, d:1}和{c:1, a:1},执行计划验证过程如下(该测试表总共10条数据):

从上面执行计划可以看出,如果该OR类查询走{b:1, d:1, c:1, a:1}索引,则实际上做了全表扫描。如果同时创建{b:1, d:1}、{c:1, a:1}索引,则直接走两个索引,其执行key和doc扫描行数远远小于全表扫描。

这里在提升一下OR查询难度,例如下面的查询:

上面的查询可以转换为如下两个查询:

如上图,查询1拆分后的两个查询2和查询3组成or关系,因此对应最优索引需要创建两个,分表是:{f:1, g:1, b:1, d:1} 和 {f:1, g:1, b:1, d:1}。对应执行计划如下:

同理,不管怎么增加难度,OR查询最终可转换为多个等值、非等值或者等值与非等值组合类查询,通过如上变换最终可以起到举一反三的作用。

说明:这个例子中可能在一些特殊数据分布场景,最优索引也可能是{f:1, g:1}或者{f:1, g:1, b:1, d:-1}或者{ f:1, g:1, c:1, a:1},这里我们只考虑大部分通用场景。

3.4 SORT类排序查询常见索引错误创建方法及如何创建最优索引

例如用户有以下两个查询:

这两个查询都不带条件,排序方式不一样,因此很多创建了两个索引{a:1}和{a:-1},实际上这两个索引中的任何一个都可以满足两种查询要求,验证过程如下:

假设有如下查询:

其中a字段为正序,b字段为反序排序,很多用户直接创建{a:1, b:1}索引,这时候b字段内容就存在内存排序情况。多字段排序索引,如果没有携带查询条件,则最优索引即为排序字段对应索引,这里切记保持每个字段得正反序和sort完全一致,否则可能存在部分字段内存排序的情况,执行计划验证过程如下:

例如如下查询:

该类查询很多人直接创建{a:1, b:1, c:1, d:1},结果造成内存排序。这种组合查询最优索引=“多字段等值查询最优索引_多字段排序类组合最优索引”,例如该查询:

{ "a" : 3, "b" : 1}等值查询假设a区分度比b高,则对应最优索引为:{a:1, b:1}

{ c:-1, d:1}排序类查询最优索引保持正反序一致,也就是:{ c:-1, d:1}

因此整个查询就是这两个查询对应最优索引拼接,也就是{a:1, b:1, c:-1, d:1},对应执行计划过程验证如下:

假设有下面的查询:

腾讯云很多用户看到该查询直接创建{a:1, b:1, c:1, d:-1, e:1}索引,发现存在内存排序。等值+非等值+sort排序组合查询,由于非等值查询右边的字段不能走索引,因此如果把d, e放到c的右边,则d,e字段索引无效。

等值+非等值+sort排序最优索引组合字段顺序为:等值_sort排序_非等值,因此上面查询最优索引为:{a:1, b:1, d:-1, e:1, c:1}。执行计划验证过程如下:

例如如下查询:

上面组合很多人直接创建{b:1, d:1, c:1, a:1, e:1},该索引创建后还是会扫表和内存排序,实际上OR+SORT组合查询可以转换为下面两个查询:

所以这个复杂查询就可以拆分为等值组合查询+sort排序查询,拆分为上面的两个查询,这样我们只需要同时创建查询2和查询3对应最优索引即可。该查询最终拆分后对应最优索引需要添加如下两个:

{b:1, d:1, e:-1}和{c:1, a:1, e:-1}

非最优索引和最优索引执行计划验证过程如下:

OR+SORT类查询,最终可以《参考前面的OR类查询常见索引错误创建方法》把OR查询转换为多个等值、非等值或者等值与非等值组合查询,然后与sort排序对应索引字段拼接。例如下面查询:

拆分后的两个查询组成or关系,如下:

如上,查询1 = or: [查询2, 查询3],因此只需要创建查询2和查询3两个最优索引即可满足查询1要求,查询2和查询3最优索引可以参考前面《or类查询常见索引错误创建方法》,该查询最终需要创建如下两个索引:

{f:1, g:1, b:1, d:1, e:-1}和{ f:1, g:1, c:1, a:1, e:-1}

说明:这个例子中可能在一些特殊数据分布场景,最优索引也可能是{f:1, g:1}或者{f:1, g:1, b:1, d:1, e:-1}或者{ f:1, g:1, c:1, a:1, e:-1},这里我们只考虑通用场景。

3.5 避免创建太多无用索引及无用索引分析方法

在腾讯云上,我们还发现另外一个问题,很多实例存在大量无用索引,无用索引会引起以下问题:

存储成本增加

没增加一个索引,MongoDB内核就会创建一个index索引文件,记录该表的索引数据,造成存储成本增加。

影响写性能

用户没写入一条数据,就会在对应索引生成一条索引KV,实现索引与数据的一一对应,索引KV数据写入Index索引文件过程加剧写入负载。

影响读性能

MongoDB内核查询优化器原理是通过候选索引快速定位到满足条件的数据,然后采样评分。如果满足条件的候选索引越多,整个评分过程就会越长,增加内核选择最优索引的流程。

下面以一个真实线上实例为例,说明如何找出无用索引:

MongoDB默认提供有索引统计命令来获取各个索引命中的次数,该命令如下:

该聚合输出中的几个核心指标信息如下表:

上表中的ops代表命中次数,如果命中次数为0或者很小,说明该索引很少被选为最优索引使用,因此可以认为是无用索引,可以考虑删除。

说明:

本文总结的《最优索引规则大全》中的规则适用于绝大部分查询场景,但是一些特殊数据分布场景可能会有一定偏差,请根据实际数据分布进行查询计划分析。

DBbrain for MongoDB

最后,本文中所介绍的优化原理即将集成到腾讯云DBbrain for MongoDB的智能索引推荐(规则+代价计算)功能中,届时可帮助用户一键优化索引,无需亲自反复推敲验证,欢迎体验。

腾讯云MongoDB当前服务于 游戏 、电商、社交、教育、新闻资讯、金融、物联网、软件服务等多个行业;MongoDB团队(简称CMongo)致力于对开源MongoDB内核进行深度研究及持续性优化(如百万库表、物理备份、免密、审计等),为用户提供高性能、低成本、高可用性的安全数据库存储服务。后续持续分享MongoDB在腾讯内部及外部的典型应用场景、踩坑案例、性能优化、内核模块化分析。

叮咚买菜自建MangoDB上腾讯云实践

云上MongoDB常见索引问题及最优索引规则大全

本文干货较多,建议收藏学习。先将文章结构速览奉上:

一、背景

二、MongoDB执行计划

2.1 queryPlanner信息

2.2 executionStats信息

2.3 allPlansExecution信息

三、云上用户建索引常见问题及优化方法

3.1 等值类查询常见问题及优化方法

3.1.1 同一类查询创建多个索引问题

3.1.2 多字段等值查询组合索引顺序非最优

3.1.3 最左原则包含关系引起的重复索引

3.1.4 唯一字段和其他字段组合引起的无用重复索引

3.2 非等值类查询常见问题及优化方法

3.2.1 非等值组合查询索引不合理创建

3.2.2 等值+非等值组合查询索引字段顺序不合理

3.2.3 不同类型非等值查询优先级问题

3.3 OR类查询常见问题及优化方法

3.3.1 普通OR类查询优化方法

3.3.2 复杂OR类查询优化方法

3.4 SORT类排序查询常见问题及优化方法

3.4.1 单字段正反序排序查询引起的重复索引

3.4.2 多字段排序查询正反序问题引起索引无效

3.4.3 等值查询+多字段排序组合查询

3.4.4 等值查询+非等值查询+SORT排序查询

3.4.5 OR+SORT组合排序查询

3.5 无用索引优化方法

四、MongoDB不同类型查询最优索引总结

腾讯云MongoDB当前已服务于 游戏 、电商、社交、教育、新闻资讯、金融、物联网、软件服务、 汽车 出行、音视频等多个行业。

腾讯MongoDB团队在配合用户分析问题过程中,发现云上用户存在如下索引共性问题,主要集中在如下方面:

本文重点分析总结腾讯云上用户索引创建不合理相关的问题,通过本文可以学习到MongoDB的以下知识点:

本文总结的《最优索引规则创建大全》不仅仅适用于MongoDB,很多规则同样适用于MySQL等关系型数据库

判断索引选择及不同索引执行家伙信息可以通过explain操作获取,MongoDB通过explain来获取SQL执行过程信息,当前持续explain的请求命令包含以下几种:

aggregate, count, distinct, find, findAndModify, delete, mapRece, and update。

详见explain官网链接:

https://docs.MongoDB.com/manual/reference/command/explain/

explain可以携带以下几个参数信息,各参数信息功能如下:

2.1 queryPlanner信息

获取MongoDB查询优化器选择的最优索引和拒绝掉的非最优索引,并给出各个候选索引的执行阶段信息,queryPlanner输出信息如下:

queryPlanner输出主要包括如下信息:

parsedQuery信息

内核对查询条件进行序列化,生成一棵expression tree信息,便于候选索引查询匹配。

winningPlan信息

rejectedPlans信息

输出信息和winningPlan类似,记录这些拒绝掉索引的执行stage信息。

2.2 executionStats信息

explain的executionStats参数除了提供上面的queryPlanner信息外,还提供了最优索引的执行过程信息,如下:

上面是通过executionStats获取执行过程的详细信息,其中字段信息较多,平时分析索引问题最常用的几个字段如下:

executionStats输出字段较多,其他字段将在后续《MongoDB内核index索引模块实现原理》中进行进一步说明。

在实际分析索引问题是否最优的时候,主要查看以下三个统计项:

executionStats.totalKeysExamined

executionStats.totalDocsExamined

executionStats .nReturned

如果存在以下情况则说明索引存在问题,可能索引不是最优的:

1. executionStats.totalKeysExamine远大于executionStats .nReturned

2. executionStats. totalDocsExamined远大于executionStats .nReturned

2.3 allPlansExecution信息

allPlansExecution参数对应输出信息和executionStats输出信息类似,只是多了所有候选索引(包括reject拒绝的非最优索引)的执行过程,这里不再详述。

2.4 总结

从上面的几个explain执行计划参数输出信息可以看出,各个参数的功能各不相同,总结如下:

queryPlanner

输出索引的候选索引,包括最优索引及其执行stage过程(winningPlan)+其他非最优候选索引及其执行stage过程。

注意:queryPlanner没有真正在表中执行整个SQL,只做了查询优化器获取候选索引过程,因此可以很快返回。

executionStats

相比queryPlanner参数,executionStats会记录查询优化器根据所选最优索引执行SQL的整个过程信息,会真正执行整个SQL。

allPlansExecution

和executionStats类似,只是多了所有候选索引的执行过程。

在和用户一起优化腾讯云上MongoDB集群索引过程中,以及和头部用户的交流中发现很多用户对如何创建最优索引有较为严重的错误认识,并且很多是绝大部分用户的共性问题,因此在本文中将这些问题汇总如下:

3.1 等值类查询常见问题及优化方法

如下三个查询:

用户创建了如下3个索引:

{a:1, b:1, c:1}

{b:1, a:1, c:1}

{c:1, a:1, b:1}

实际上这3个查询属于同一类查询,只是查询字段顺序不一样,因此只需创建任一个索引即可满足要求。验证过程如下:

从上面的expalin输出可以看出,3个查询都走向了同一个索引。

例如test表有多条数据,每条数据有3个字段,分别为a、b、c。其中a字段有10种取值,b字段有100种取值,c字段有1000种取值,称为各个字段值的“区分度”

用户查询条件为db.test.find({"a":"xxx", "b":"xxx", "c":"xxx"}),创建的索引为{a:1, b:1, c:1}。如果只是针对这个查询,该查询可以创建a,b,c三字段的任意组合,并且其SQL执行代价一样,通过hint强制走不通索引,验证过程如下:

从上面的执行计划可以看出,多字段等值查询各个字段的组合顺序对应执行计划代价一样。绝大部分用户在创建索引的时候,都是直接按照查询字段索引组合对应字段。

但是,单就这一个查询,这里有个不成文的建议,把区分度更高的字段放在组合索引左边,区分度低的字段放到右边。这样做有个好处,数据库组合索引遵从最左原则,就是当其他查询里面带有区分度最高的字段时,就可以快速排除掉更多不满足条件的数据。

例如用户有如下两个查询:

用户创建了如下两个索引:

{b:1, c:1}

{a:1,b:1,c:1}

这两个查询中,查询2中包含有查询1中的字段,因此可以用一个索引来满足这两个查询要求,按照最左原则,查询1字段放左边即可,该索引可以优化为:b,c字段索引+a字段索引,b,c字段顺序可以根据区分排序,加上c字段区分度比b高,则这两个查询可以合并为一个{c:1, b:1, a:1}。两个查询可以走同一个索引验证过程如下:

从上面输出可以看出,这两个查询都走了同一个索引。

例如用户有以下两个查询:

用户为这两个查询创建了两个索引,{a:1, b:1}和{a:1, c:1},但是a字段取值是唯一的,因此这两个查询中a以外的字段无用,一个{a:1}索引即可满足要求。

3.2 非等值类查询常见索引错误创建方法及如何创建最优索引

假设用户有如下查询:

a,c两个字段都是非等值查询,很多用户直接添加了{a:1, c:1}索引,实际上多个字段的非等值查询,只有最左边的字段才能走索引,例如这里只会走a字段索引,验证过程如下:

从上面执行计划可以看出,索引数据扫描了10行(也就是a字段满足a:{$gte:1}条件的数据多少),但是实际上只返回了4条满足{a:{$gte:1}, c:{$lte:1}}条件的数据,可以看出c字段无法做索引。

同理,当查询中包含多个字段的范围查询的适合,除了最左边第一个字段可以走索引,其他字段都无法走索引。因此,上面例子中的查询候选索引为{a:1}或者{b:1}中任何一个就可以了,组合索引中字段太多会占用更多存储成本、同时占用更多IO资源引起写放大。

例如下面查询:

如上查询,d字段为非等值查询,e字段为等值查询,很多用户遇到该类查询直接创建了{d:1, e:1}索引,由于d字段为非等值查询,因此e字段无法走索引,验证过程如下:

从上面验证过程可以看出,等值类和非等值类组合查询对应组合索引,最优索引应该优先把等值查询放到左边,上面查询对应最优索引{e:1, d:1}

前面用到的非等值查询操作符只提到了比较类操作符,实际上非等值查询还有其他操作符。常用非等值查询包括:$gt、$gte、$lt、$lte、$in、$nin、$ne、$exists、$type等,这些非等值查询在绝大部分情况下存在如下优先级:

从上到下优先级更高,例如下面的查询:

如上,该查询等值部分查询最优索引{a:1, b:1}(假设a区分度比b高);非等值部分,因为$in操作符优先级最高,排他性更好,加上多个字段非等值查询只会有一个字段走索引,因此非等值部分最优索引为{g:1}。

最终该查询最优索引为:”等值部分最优索引”与”非等值部分最优索引”拼接,也就是{a:1,b:1, g:1}

3.3 OR类查询常见索引错误创建方法及如何创建最优索引

例如下面的OR查询:

该查询很多用户直接创建了{b:1, d:1, c:1, a:1},用户创建该索引后,发现用户还是全表扫描。

OR类查询需要给数组中每个查询添加索引,例如上面or数组中实际包含{ b: 0, d:0 }和 {"c":1, "a":{$gte:4}}查询,需要创建两个查询的最优索引,也就是{b:1, d:1}和{c:1, a:1},执行计划验证过程如下(该测试表总共10条数据):

从上面执行计划可以看出,如果该OR类查询走{b:1, d:1, c:1, a:1}索引,则实际上做了全表扫描。如果同时创建{b:1, d:1}、{c:1, a:1}索引,则直接走两个索引,其执行key和doc扫描行数远远小于全表扫描。

这里在提升一下OR查询难度,例如下面的查询:

上面的查询可以转换为如下两个查询:

如上图,查询1拆分后的两个查询2和查询3组成or关系,因此对应最优索引需要创建两个,分表是:{f:1, g:1, b:1, d:1} 和 {f:1, g:1, b:1, d:1}。对应执行计划如下:

同理,不管怎么增加难度,OR查询最终可转换为多个等值、非等值或者等值与非等值组合类查询,通过如上变换最终可以起到举一反三的作用。

说明:这个例子中可能在一些特殊数据分布场景,最优索引也可能是{f:1, g:1}或者{f:1, g:1, b:1, d:-1}或者{ f:1, g:1, c:1, a:1},这里我们只考虑大部分通用场景。

3.4 SORT类排序查询常见索引错误创建方法及如何创建最优索引

例如用户有以下两个查询:

这两个查询都不带条件,排序方式不一样,因此很多创建了两个索引{a:1}和{a:-1},实际上这两个索引中的任何一个都可以满足两种查询要求,验证过程如下:

假设有如下查询:

其中a字段为正序,b字段为反序排序,很多用户直接创建{a:1, b:1}索引,这时候b字段内容就存在内存排序情况。多字段排序索引,如果没有携带查询条件,则最优索引即为排序字段对应索引,这里切记保持每个字段得正反序和sort完全一致,否则可能存在部分字段内存排序的情况,执行计划验证过程如下:

例如如下查询:

该类查询很多人直接创建{a:1, b:1, c:1, d:1},结果造成内存排序。这种组合查询最优索引=“多字段等值查询最优索引_多字段排序类组合最优索引”,例如该查询:

{ "a" : 3, "b" : 1}等值查询假设a区分度比b高,则对应最优索引为:{a:1, b:1}

{ c:-1, d:1}排序类查询最优索引保持正反序一致,也就是:{ c:-1, d:1}

因此整个查询就是这两个查询对应最优索引拼接,也就是{a:1, b:1, c:-1, d:1},对应执行计划过程验证如下:

假设有下面的查询:

腾讯云很多用户看到该查询直接创建{a:1, b:1, c:1, d:-1, e:1}索引,发现存在内存排序。等值+非等值+sort排序组合查询,由于非等值查询右边的字段不能走索引,因此如果把d, e放到c的右边,则d,e字段索引无效。

等值+非等值+sort排序最优索引组合字段顺序为:等值_sort排序_非等值,因此上面查询最优索引为:{a:1, b:1, d:-1, e:1, c:1}。执行计划验证过程如下:

例如如下查询:

上面组合很多人直接创建{b:1, d:1, c:1, a:1, e:1},该索引创建后还是会扫表和内存排序,实际上OR+SORT组合查询可以转换为下面两个查询:

所以这个复杂查询就可以拆分为等值组合查询+sort排序查询,拆分为上面的两个查询,这样我们只需要同时创建查询2和查询3对应最优索引即可。该查询最终拆分后对应最优索引需要添加如下两个:

{b:1, d:1, e:-1}和{c:1, a:1, e:-1}

非最优索引和最优索引执行计划验证过程如下:

OR+SORT类查询,最终可以《参考前面的OR类查询常见索引错误创建方法》把OR查询转换为多个等值、非等值或者等值与非等值组合查询,然后与sort排序对应索引字段拼接。例如下面查询:

拆分后的两个查询组成or关系,如下:

如上,查询1 = or: [查询2, 查询3],因此只需要创建查询2和查询3两个最优索引即可满足查询1要求,查询2和查询3最优索引可以参考前面《or类查询常见索引错误创建方法》,该查询最终需要创建如下两个索引:

{f:1, g:1, b:1, d:1, e:-1}和{ f:1, g:1, c:1, a:1, e:-1}

说明:这个例子中可能在一些特殊数据分布场景,最优索引也可能是{f:1, g:1}或者{f:1, g:1, b:1, d:1, e:-1}或者{ f:1, g:1, c:1, a:1, e:-1},这里我们只考虑通用场景。

3.5 避免创建太多无用索引及无用索引分析方法

在腾讯云上,我们还发现另外一个问题,很多实例存在大量无用索引,无用索引会引起以下问题:

存储成本增加

没增加一个索引,MongoDB内核就会创建一个index索引文件,记录该表的索引数据,造成存储成本增加。

影响写性能

用户没写入一条数据,就会在对应索引生成一条索引KV,实现索引与数据的一一对应,索引KV数据写入Index索引文件过程加剧写入负载。

影响读性能

MongoDB内核查询优化器原理是通过候选索引快速定位到满足条件的数据,然后采样评分。如果满足条件的候选索引越多,整个评分过程就会越长,增加内核选择最优索引的流程。

下面以一个真实线上实例为例,说明如何找出无用索引:

MongoDB默认提供有索引统计命令来获取各个索引命中的次数,该命令如下:

该聚合输出中的几个核心指标信息如下表:

上表中的ops代表命中次数,如果命中次数为0或者很小,说明该索引很少被选为最优索引使用,因此可以认为是无用索引,可以考虑删除。

说明:

本文总结的《最优索引规则大全》中的规则适用于绝大部分查询场景,但是一些特殊数据分布场景可能会有一定偏差,请根据实际数据分布进行查询计划分析。

DBbrain for MongoDB

最后,本文中所介绍的优化原理即将集成到腾讯云DBbrain for MongoDB的智能索引推荐(规则+代价计算)功能中,届时可帮助用户一键优化索引,无需亲自反复推敲验证,欢迎体验。

腾讯云MongoDB当前服务于 游戏 、电商、社交、教育、新闻资讯、金融、物联网、软件服务等多个行业;MongoDB团队(简称CMongo)致力于对开源MongoDB内核进行深度研究及持续性优化(如百万库表、物理备份、免密、审计等),为用户提供高性能、低成本、高可用性的安全数据库存储服务。后续持续分享MongoDB在腾讯内部及外部的典型应用场景、踩坑案例、性能优化、内核模块化分析。

叮咚买菜自建MangoDB上腾讯云实践

《深入学习MongoDB图灵程序设计丛书:深入学习MongoDB》pdf下载在线阅读,求百度网盘云资源

《深入学习MongoDB》(Kristina Chodorow)电子书网盘下载免费在线阅读

资源链接:

链接: https://pan.baidu.com/s/19cx13O3W-PWcH3OUrMEWYA

提取码: 1d6g    

书名:深入学习MongoDB

作者:Kristina Chodorow

译者:巨成

豆瓣评分:7.3

出版社:人民邮电出版社

出版年份:2012-3

页数:121

内容简介:

《深入学习MongoDB》分两部分,分别对应O'Reilly公司出版的Scaling MongoDB 50Tips and Tricks for Mongo DB Developers两《深入学习MongoDB》的内容。第一部分全面讲解了有关建立和使用集群的内容,不仅从应用开发人员的角度讲解了MongoDB的使用,而且从运维方面介绍了集群的管理。其中内容包括通过分片设置MongoDB集群,分片的工作原理,查询和更新数据,操作、监控和备份集群,错误处理。第二部分依次从应用设计、实现、优化、数据安全和管理方面介绍了使用MongoDB构建应用的技巧,内容包括范式化与反范式化的利弊权衡,复制组的故障恢复等。

作者简介:

Kristina Chodorow  10gen公司的软件工程师,MongoDB项目的核心成员,从事与数据库服务器、PHP驱动、Perl驱动等相关的工作。她常在世界级技术大会上作报告,包括OSCON、LinuxCon、FOSDEM和Latinoware。

显示全文