【MongoDB】(三)MongoDB 基本操作

494 阅读6分钟

文章目录

一、前言

MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。

在高负载的情况下,添加更多的节点,可以保证服务器性能。

MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。

如果对于 MongoDB的发展史感兴趣,可以参考下没有一个技术天生完美,MongoDB 十年发展全纪录这篇文章。
在这里插入图片描述

二、MongoDB的特点

  • 模式自由:可以把不同结构的文档存储在同一个数据库里
  • 面向集合的储存:适合储存JSON风格文件的形式
  • 完整的索引支持:对任何属性可索引
  • 复制和高可用性:支持服务器之间的数据复制吗,支持主从模式和服务器间的相互复制,目的是提供冗余及自动故障转移
  • 自动分片:支持水平的数据库集群,可动态添加额外的机器
  • 丰富的查询:丰富的查询表达式,查询指令使用JSON形式的标记和查询文档中内嵌的对象和数组
  • 快速就地更新:查询优化器会分析查询表达式,并生成一个高效的查询计划
  • 高效的传统储存方式:支持二进制数据集大型对象(如:图片或照片)

三、基本模型

  • MongoDB将数据储存为一个文档,数据结构由键值对组成(key=>value)
  • MongoDB文档类似于JSON对象,字段值可以包含其他文档、数组、文档数组
  • 安装管理mongodb环境、完成数据库、集合的管理
  • 数据的增加、修改、删除、查询
SQL属于/概念MongoDB术语/概念解释/说明
databasedatabase数据库
tablecollection数据库表/集合
rowdocument数据记录行/文档
columnfield数据字段/域
  • database 数据库,与SQL的数据库(database)概念相同,一个数据库包含多个集合(表)
  • collection 集合,相当于SQL中的表(table),一个集合可以存放多个文档(行)。 不同之处就在于集合的结构(schema)是动态的,不需要预先声明一个严格的表结构。更重要的是,默认情况下 MongoDB 并不会对写入的数据做任何schema的校验。
  • document 文档,相当于SQL中的行(row),一个文档由多个字段(列)组成,并采用bson(json)格式表示。
  • field 字段,相当于SQL中的列(column),相比普通column的差别在于field的类型可以更加灵活,比如支持嵌套的文档、数组。
    此外,MongoDB中字段的类型是固定的、区分大小写、并且文档中的字段也是有序的。

另外,SQL 还有一些其他的概念,对应关系如下:

SQL概念MongoDB概念
primary key_id
foreign keyreference
viewview
indexindex
join$lookup
transactiontrasaction
group byaggregation
  • _id 主键,MongoDB 默认使用一个_id 字段来保证文档的唯一性。
  • reference 引用,勉强可以对应于 外键(foreign key) 的概念,之所以是勉强是因为 reference 并没有实现任何外键的约束,而只是由客户端(driver)自动进行关联查询、转换的一个特殊类型。
  • view 视图,MongoDB 3.4 开始支持视图,和 SQL 的视图没有什么差异,视图是基于表/集合之上进行动态查询的一层对象,可以是虚拟的,也可以是物理的(物化视图)。
  • index 索引,与SQL 的索引相同。
  • $lookup,这是一个聚合操作符,可以用于实现类似 SQL-join 连接的功能
  • transaction 事务,从 MongoDB 4.0 版本开始,提供了对于事务的支持
  • aggregation 聚合,MongoDB 提供了强大的聚合计算框架,group by 是其中的一类聚合操作。

BSON 数据类型

MongoDB 文档可以使用 Javascript 对象表示,从格式上讲,是基于 JSON 的。

一个典型的文档如下:

{
  "_id": 1,
  "name" : { "first" : "John", "last" : "Backus" },
  "contribs" : [ "Fortran", "ALGOL", "Backus-Naur Form", "FP" ],
  "awards" : [
    {
      "award" : "W.W. McDowell Award",
      "year" : 1967,
      "by" : "IEEE Computer Society"
    }, {
      "award" : "Draper Prize",
      "year" : 1993,
      "by" : "National Academy of Engineering"
    }
  ]
}

曾经,JSON 的出现及流行让 Web 2.0 的数据传输变得非常简单,所以使用 JSON 语法是非常容易让开发者接受的。

但是 JSON 也有自己的短板,比如无法支持像日期这样的特定数据类型,因此 MongoDB 实际上使用的是一种扩展式的JSON,叫 BSON(Binary JSON)。

BSON 所支持的数据类型包括:

分布式ID

在单机时代,大多数应用可以使用数据库自增式ID 来作为主键。 传统的 RDBMS 也都支持这种方式,比如 mysql 可以通过声明 auto_increment来实现自增的主键。 但一旦数据实现了分布式存储,这种方式就不再适用了,原因就在于无法保证多个节点上的主键不出现重复。

为了实现分布式数据ID的唯一性保证,应用开发者提出了自己的方案,而大多数方案中都会将ID分段生成,如著名的 snowflake 算法中就同时使用了时间戳、机器号、进程号以及随机数来保证唯一性。

MongoDB 采用 ObjectId 来表示主键的类型,数据库中每个文档都拥有一个_id 字段表示主键。

_id 的生成规则如下:

在这里插入图片描述
其中包括:

  • 4-byte Unix 时间戳
  • 3-byte 机器 ID
  • 2-byte 进程 ID
  • 3-byte 计数器(初始化随机)

值得一提的是 _id 的生成实质上是由客户端(Driver)生成的,这样可以获得更好的随机性,同时降低服务端的负载。

当然服务端也会检测写入的文档是否包含_id 字段,如果没有就生成一个。

三、操作语法

除了文档模型本身,对于数据的操作命令也是基于JSON/BSON 格式的语法。

比如插入文档的操作:

db.book.insert(
{
  title: "My first blog post",
  published: new Date(),
  tags: [ "NoSQL", "MongoDB" ],
  type: "Work",
  author : "James",
  viewCount: 25,
  commentCount: 2
}
)

执行文档查找:

db.book.find({author : "James"})

更新文档的命令:

db.book.update(
   {"_id" : ObjectId("5c61301c15338f68639e6802")},
   {"$inc": {"viewCount": 3} }
)

删除文档的命令:

db.book.remove({"_id":
     ObjectId("5c612b2f15338f68639e67d5")})

在传统的SQL语法中,可以限定返回的字段,MongoDB可以使用Projection来表示:

db.book.find({"author": "James"}, 
    {"_id": 1, "title": 1, "author": 1})

实现简单的分页查询:

db.book.find({})
    .sort({"viewCount" : -1})
    .skip(10).limit(5)

关于文档操作与 SQL方式完整的对比,官方的文档描述得比较详细:
docs.mongodb.com/manual/refe…

MongoDB的好处挺多的,比如多列索引,查询时可以用一些统计函数,支持多条件查询,但是目前多表查询是不支持的,可以想办法通过数据冗余来解决多表查询的问题。

MongoDB对数据的操作很丰富,下面做一些举例说明,内容大部分来自官方文档。

查询colls所有数据

db.colls.find() 
//select * from colls        

通过指定条件查询

db.colls.find({‘last_name’: ‘Smith’});
//select * from colls where last_name=’Smith’

指定多条件查询

db.colls.find( { x : 3, y : “foo” } );
//select * from colls where x=3 and y=’foo’

指定条件范围查询

db.colls.find({j: {$ne: 3}, k: {$gt: 10} });
//select * from colls where j!=3 and k>10

查询不包括某内容

db.colls.find({}, {a:0});
//查询除a为0外的所有数据

支持 <, <=, >, >=查询,需用符号替代分别为$lt,$lte,$gt,$gte

db.colls.find({ “field” : { $gt: value } } ); 

db.colls.find({ “field” : { $lt: value } } ); 

db.colls.find({ “field” : { $gte: value } } );

db.colls.find({ “field” : { $lte: value } } );

也可对某一字段做范围查询

db.colls.find({ “field” : { $gt: value1, $lt: value2 } } );

不等于查询用字符$ne

db.colls.find( { x : { $ne : 3 } } );

in查询用字符$in

db.colls.find( { “field” : { $in : array } } );

db.colls.find({j:{$in: [2,4,6]}});

not in查询用字符$nin

db.colls.find({j:{$nin: [2,4,6]}});

取模查询用字符$mod

db.colls.find( { a : { $mod : [ 10 , 1 ] } } )
// where a % 10 == 1

$all查询

db.colls.find( { a: { $all: [ 2, 3 ] } } );
//指定a满足数组中任意值时

$size查询

db.colls.find( { a : { $size: 1 } } );
//对对象的数量查询,此查询查询a的子对象数目为1的记录

$exists查询

db.colls.find( { a : { $exists : true } } ); 
// 存在a对象的数据

db.colls.find( { a : { $exists : false } } ); 
// 不存在a对象的数据

$type查询$type值为bson数据的类型值

db.colls.find( { a : { $type : 2 } } ); 
// 匹配a为string类型数据

db.colls.find( { a : { $type : 16 } } ); 
// 匹配a为int类型数据

使用正则表达式匹配

db.colls.find( { name : /acme.*corp/i } );
//类似于SQL中like

内嵌对象查询

db.colls.find( { “author.name” : “joe” } );

1.3.3版本及更高版本包含$not查询

db.colls.find( { name : { $not : /acme.*corp/i } } );

db.colls.find( { a : { $not : { $mod : [ 10 , 1 ] } } } );

sort()排序

db.colls.find().sort( { ts : -1 } );
//1为升序2为降序

limit()对限制查询数据返回个数

db.colls.find().limit(10)

skip()跳过某些数据

db.colls.find().skip(10)

snapshot()快照保证没有重复数据返回或对象丢失

count()统计查询对象个数

db.students.find({‘address.state’ : ‘CA’}).count();
//效率较高

db.students.find({‘address.state’ : ‘CA’}).toArray().length;
//效率很低

group()对查询结果分组和SQL中group by函数类似

distinct()返回不重复值

四、索引

无疑,索引是一个数据库的关键能力,MongoDB 支持非常丰富的索引类型。利用这些索引,可以实现快速的数据查找,而索引的类型和特性则是针对不同的应用场景设计的。

索引的技术实现依赖于底层的存储引擎,在当前的版本中 MongoDB 使用 wiredTiger 作为默认的引擎。
在索引的实现上使用了 B+树的结构,这与其他的传统数据库并没有什么不同。

所以这是个好消息,大部分基于SQL数据库的一些索引调优技巧在 MongoDB 上仍然是可行的。

在这里插入图片描述
使用 ensureIndexes 可以为集合声明一个普通的索引:

db.book.ensureIndex({author: 1})

author后面的数字 1 代表升序,如果是降序则是 -1

实现复合式(compound)的索引,如下:

db.book.ensureIndex({type: 1, published: 1})

只有对于复合式索引时,索引键的顺序才变得有意义

如果索引的字段是数组类型,该索引就自动成为数组(multikey)索引:

db.book.ensureIndex({tags: 1})

MongoDB 可以在复合索引上包含数组的字段,但最多只能包含一个

索引特性

在声明索引时,还可以通过一些参数化选项来为索引赋予一定的特性,包括:

  • unique=true,表示一个唯一性索引
  • expireAfterSeconds=3600,表示这是一个TTL索引,并且数据将在1小时后老化
  • sparse=true,表示稀疏的索引,仅索引非空(non-null)字段的文档
  • partialFilterExpression: { rating: { $gt: 5 },条件式索引,即满足计算条件的文档才进行索引

索引分类

除了普通索引之外,MongoDB 支持的类型还包括:

  • 哈希(HASH)索引,哈希是另一种快速检索的数据结构,MongoDB 的 HASH 类型分片键会使用哈希索引。
  • 地理空间索引,用于支持快速的地理空间查询,如寻找附近1公里的商家。
  • 文本索引,用于支持快速的全文检索
  • 模糊索引(Wildcard Index),一种基于匹配规则的灵活式索引,在4.2版本开始引入。

索引评估、调优

使用 explain() 命令可以用于查询计划分析,进一步评估索引的效果。
如下:

> db.test.explain().find( { a : 5 } )

{
  "queryPlanner" : {
    ...
    "winningPlan" : {
      "stage" : "FETCH",
      "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "a" : 5
        },
        "indexName" : "a_1",
        "isMultiKey" : false,
        "direction" : "forward",
        "indexBounds" : {"a" : ["[5.0, 5.0]"]}
        }
    }},
   ...
}

从结果 winningPlan 中可以看出执行计划是否高效,比如:

  • 未能命中索引的结果,会显示COLLSCAN
  • 命中索引的结果,使用IXSCAN
  • 出现了内存排序,显示为 SORT

Reference

www.cnblogs.com/littleatp/p…