关于mongodb的学习与探索一

前言

什么是mongodb?MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档相似于 JSON 对象。字段值能够包含其余文档,数组及文档数组。python

mongodb的基本用法

连接mongodb数据库

在windows下,MongoDB 提供了可用于 32 位和 64 位系统的预编译二进制包,你能够从MongoDB官网下载安装,MongoDB 预编译二进制包下载地址:https://www.mongodb.com/download-center#community,安装后自行搜索配置环境变量,其余操做系统下也自行搜索安装方法,如下操做默认已经配置好环境变量。git

  • 打开命令行工具,执行mongod --dbpath c:\data\db,这里的路径跟的是你本身设置的存储mongodb数据的路径。
  • 另开一个命令行工具,执行mongo,即进入mongodb命令窗口。

数据库的基本操做经常使用命令

  • show dbs --显示全部数据库
  • use test --使用test数据库
  • db.dropDatabase() --删除当前所use的数据库
  • show tables --显示当前数据库下全部集合
  • db.foo.insert({}) --向foo集合下插入数据
  • db.foo.find() --查询foo集合下全部数据
  • db.foo.update({}) --更新数据
  • db.foo.remove({}) --删除数据

find查询

db.collection.find(query, projection)github

    • query :可选,使用查询操做符指定查询条件
    • projection:可选,使用投影操做符指定返回的键。查询时返回文档中全部键值, 只需省略该参数便可(默认省略)。

pretty() 方法以格式化的方式来显示全部文档。sql

db.collection.find(query, projection).pretty()mongodb

> db.col.find().pretty()
{
        "_id" : ObjectId("56063f17ade2f21f36b03133"),
        "title" : "MongoDB 教程"
}
复制代码

MongoDB一样还支持条件语句查询,如:数据库

    • 等于 : { key : value }
    • 小于 : { key : { $lt : value } }
    • 小于或等于 : { key : { $lte : value } }
    • 大于 :{ key : { $gt : value } }
    • 大于或等于 : { key : { $gte : value } }
    • 不等于 : { key : { $ne : value } }

下面这个小例子表示查询sort不等于50的数据,代码以下所示:windows

> db.col.find({"sort":{$ne:50}}).pretty()
{
        "_id" : ObjectId("56063f17ade2f21f36b03133"),
        "title" : "MongoDB 教程",
        "sort" : 20
}
复制代码

find的条件中支持and和or查询,也就是“并”与“或”的关系, find() 方法中传入多个键(key),每一个键(key)以逗号隔开,表示and,or则是用数组包裹,下面直接上代码。数组

> db.col.find({"likes": {$gt:50}, $or: [{"by": "MongoDB"},{"title": "MongoDB 教程"}]}).pretty()
{
        "_id" : ObjectId("56063f17ade2f21f36b03133"),
        "title" : "MongoDB 教程",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "MongoDB",
        "likes" : 100
}
复制代码

下面咱们继续讨论MongoDB中条件操做符 $type,若是想获取 "col" 集合中 title 为 String 的数据,你可使用如下命令缓存

db.col.find({"title" : {$type : 2}})网络

> db.col.find({"title" : {$type : 2}})
{ "_id" : ObjectId("56066542ade2f21f36b0313a"), "title" : "PHP 教程", "likes" : 200 }
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java 教程", "likes" : 150 }
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB 教程", "likes" : 100 }
复制代码

若是你须要在MongoDB中读取指定数量的数据记录,可使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。

> db.col.find({},{"title":1,_id:0}).limit(2)
{ "title" : "PHP 教程" }
{ "title" : "Java 教程" }
复制代码

咱们除了可使用limit()方法来读取指定数量的数据外,还可使用skip()方法来跳过指定数量的数据,skip方法一样接受一个数字参数做为跳过的记录条数。

>db.col.find({},{"title":1,_id:0}).limit(1).skip(1)
{ "title" : "Java 教程" }
复制代码

在MongoDB中使用使用sort()方法对数据进行排序,sort()方法能够经过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而-1是用于降序排列。

>db.col.find({},{"title":1,_id:0}).sort({"likes":-1})
{ "title" : "PHP 教程" }
{ "title" : "Java 教程" }
{ "title" : "MongoDB 教程" }
复制代码

注意:当查询时同时使用sort,skip,limit,不管位置前后,最早执行顺序 sort再skip再limit。

MongoDB 索引

索引一般可以极大的提升查询的效率,若是没有索引,MongoDB在读取数据时必须扫描集合中的每一个文件并选取那些符合查询条件的记录。

这种扫描全集合的查询效率是很是低的,特别在处理大量的数据时,查询能够要花费几十秒甚至几分钟,这对网站的性能是很是致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。

索引优势:加快索引相关的查询;

索引缺点:增长磁盘空间消耗,下降写入性能。

索引的基本操做

getIndexes() =>查看索引

ensureIndex() =>建立索引

dropIndex("name_1") =>删除索引

_id索引

_id索引是绝大多数集合默认创建的索引。对于每一个插入的数据,mongodb都会自动生成一条惟一的_id字段。如

>db.col.insert({"title":1})
{ "title" : "1","_id" : ObjectId("5a2de799328eaf1eb0af7849") }
复制代码
单键索引

单键索引是最普通的索引,与_id索引不一样,单键索引不会自动建立

>db.col.ensureIndex({"title":1})
复制代码
多键索引

多键索引与单键索引建立形式相同,区别在于字段的值。单键索引,值为一个单一的值,例如字符串、数字或者日期;多键索引的值具备多个记录,例如数组。

>db.col.ensureIndex({"title":1})
复制代码
复合索引

对多个键建立的索引,好比,须要对Mail的user与folderId建立降序索引:

>db.col.ensureIndex({"title":1."name": -1})
复制代码
过时索引

过时索引是在一段时间后会过时的索引,在索引过时后,相应的数据会被删除,这适合存储一些在一段时间后会失效的数据好比用户的登陆信息、存储的日志等。

>db.col.ensureIndex({"title":1}, { expireAfterSeconds:60 })
复制代码

须要注意的是,过时索引会存在偏差,大概60s刷新一次;存储在过时索引字段的值必须是指定的时间类型,不能是时间戳;若是指定了Date数组,则按照最小的时间进行删除; 过时索引不能是复合索引。

全文索引

全文索引是对字符串与字符串数组建立全文可搜索的索引,使用状况以下所示

{author: '', title: '', article: ''}

全文索引最经常使用的创建方法有三种,其中value必须是"text",对单个字段创建全文索引可选第一种,对多个字段可选第二种,对全部字段可选最后一种,其中要注意的是,在mongodb中只能存在一种全文索引。

db.col.ensureIndex({"key":"text"})

db.col.ensureIndex({"key1":"text","key2":"text"})

db.col.ensureIndex({"$**":"text"})

> db.col.ensureIndex({name:"text"})
> db.col.ensureIndex({name:"text",title:"text"})
> db.col.ensureIndex({"$**":"text"})
复制代码

那么如何使用全文索引查询呢?

db.col.find({text:{search:"aaa"}}) =>查询包含aaa

db.col.find({text:{search:"aaa bb"}}) =>查询包含aaa或者包含bb

db.col.find({text:{search:"aaa bb -cc"}}) =>查询包含aaa或者bb,可是不包含cc

db.col.find({text:{search:""aaa""bb""}}) =>查询包含aaa和bb

> db.test.find({$text:{$search:"aa"}})
{ "_id" : ObjectId("5a321f7f071884f56c1baba4"), "name" : "aa bb cc" }
{ "_id" : ObjectId("5a321f83071884f56c1baba5"), "name" : "aa bb" } 
复制代码

全文索引的另外一个黑科技,那就是全文索引类似度了,全文索引类似度指的是meta操做符:{score:{meta:"textScore"}}写在查询条件后面能够返回结果的类似度,与sort一块儿使用,能够达到很好的效果。

> db.test.find({$text:{$search:"aa"}},{score:{$meta:"textScore"}})
{ "_id" : ObjectId("5a321f7f071884f56c1baba4"), "name" : "aa bb cc", "score" : 0.6666666666666666 }
{ "_id" : ObjectId("5a321f83071884f56c1baba5"), "name" : "aa bb", "score" : 0.75 }
复制代码

全文索引虽然很是强大,可是一样存在着一些限制,例如:每次查询,只能指定一个text查询;text查询不能出如今nor查询中;查询中若是包含了text,hint再也不起做用。

地理位置索引

地理位置索引指的是将一些点的位置存储在mongodb中,建立索引后,能够按照位置来查找其余的点。

地理位置索引分为两类,一种是2d索引表示平面地理位置索引,即用于存储和查找平面上的点,另外一种是2dsphere索引表示球面地理位置索引,用于存储和查找球面上的点。

查找方式也可大体分为两种:

1.查找距离某个点必定距离内的点;

2.查找包含在某区域内的点

下面咱们先来讲说2d索引,建立方式以下所示:

db.col.ensureIndex({ w : "2d"})

位置表示方式:经纬度[经度,纬度]

取值范围:经度[-180,180]纬度[-90,90]

查询方式:

(1)near查询:查询距离某个点最近的点,默认返回一百个离我最近的点,可以使用maxDistance来限制最大范围

> db.col.find({w:{$near:[1,1],$maxDistance:20}})
{ "_id" : ObjectId("5a32297d071884f56c1baba8"), "w" : [ 1, 1 ] }
{ "_id" : ObjectId("5a322981071884f56c1baba9"), "w" : [ 1, 11 ] }
{ "_id" : ObjectId("5a322986071884f56c1babaa"), "w" : [ 1, 21 ] }
复制代码

(2)$geoWithin查询:查询某个形状内的点,其中形状的表示有如下几种:

box:矩形,使用{box : [ [ x1, y1 ] , [ x2, y2 ] ] }

> db.col.find({ w:{$geoWithin:{$box:[ [1,1],[2,3] ]}} })
{ "_id" : ObjectId("5a32297d071884f56c1baba8"), "w" : [ 1, 1 ] }
复制代码

center:圆形,使用{center: [[ x1, y1 ], r] }表示

> db.col.find({ w:{$geoWithin:{$center:[ [1,1],10 ]}} })
{ "_id" : ObjectId("5a32297d071884f56c1baba8"), "w" : [ 1, 1 ] }
{ "_id" : ObjectId("5a322981071884f56c1baba9"), "w" : [ 1, 11 ] }
复制代码

polygon: 多边形,使用{polygon: [ [ x1, y1 ], [ x2, y2 ], [ x3, y3 ] ] }表示

> db.col.find({ w:{$geoWithin:{$polygon:[ [1,1],[20,10],[6,20] ]}} })
{ "_id" : ObjectId("5a32297d071884f56c1baba8"), "w" : [ 1, 1 ] }
复制代码

(3)geoNear查询:geoNear使用runCommand命令进行使用,经常使用使用以下

db.runCommand( { geoNear: col, near: [x, y], minDistance: (对2d索引无效), maxDistancen: , num: 返回条数 } )

> db.runCommand({ geoNear: "col", near:[1,2], maxDistance: 10, num: 1 })
{
        "results" : [
                {
                        "dis" : 1,
                        "obj" : {
                                "_id" : ObjectId("5a32297d071884f56c1baba8"),
                                "w" : [
                                        1,
                                        1
                                ]
                        }
                }
        ],
        "stats" : {
                "nscanned" : 4,
                "objectsLoaded" : 1,
                "avgDistance" : 1,
                "maxDistance" : 1,
                "time" : 0
        },
        "ok" : 1
}
复制代码

接着咱们来聊聊2dsphere索引,2dsphere表示球面地理位置索引,建立方式以下所示:

db.col.ensureIndex({ w: 2dsphere })

位置表示方式:GeoJSON:描述一个点,一条直线,多边形等形状。格式以下所示:

{ type: '', coordinates: [ coordinates ] }

查询方式与2d索引查询方式相似,而且支持$minDistance,这一块的比较复杂,有兴趣的小伙伴可自行搜索。

索引的经常使用属性
Parameter Type Description
background Boolean 建索引过程会阻塞其它数据库操做,background可指定之后台方式建立索引,即增长 "background" 可选参数。 "background" 默认值为false。
unique Boolean 创建的索引是否惟一。指定为true建立惟一索引。默认值为false.
name string 索引的名称。若是未指定,MongoDB的经过链接索引的字段名和排序顺序生成一个索引名称。
dropDups Boolean 在创建惟一索引时是否删除重复记录,指定 true 建立惟一索引。默认值为 false.
sparse Boolean 对文档中不存在的字段数据不启用索引;这个参数须要特别注意,若是设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间
v index version 索引的版本号。默认的索引版本取决于mongod建立索引时运行的版本。
weights document 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其余索引字段的得分权重。
索引构建状况分析

索引的好处:加快索引相关的查询。

索引的弊端:增长磁盘空间消耗,下降写入性能。 可经过如下四种途径来评判当前索引构建状况:

(1)mongostat工具

此工具是mongodb自带的一个检测工具,提供了关于mongod和mongos的当前运行状态概览,启动命令格式为:

mongostat -h 127.0.0.1:27017 -u root -p 123456

若是没有设置用户名和密码可省略-u及之后代码

F:\mongodb\bin>mongostat -h 127.0.0.1:27017
insert query update delete getmore command dirty used flushes vsize   res qrw arw net_in net_out conn                time
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0  269M 14.0M 0|0 0|0   157b   45.1k    2 Dec 14 16:14:29.989
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0  269M 14.0M 0|0 0|0   157b   45.2k    2 Dec 14 16:14:30.990
    *0    *0     *0     *0       0     2|0  0.0% 0.0%       0  269M 14.0M 0|0 0|0   158b   45.5k    2 Dec 14 16:14:31.986
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0  269M 14.0M 0|0 0|0   157b   45.2k    2 Dec 14 16:14:32.987
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0  269M 14.0M 0|0 0|0   157b   45.2k    2 Dec 14 16:14:33.989
    *0    *0     *0     *0       0     2|0  0.0% 0.0%       0  269M 14.0M 0|0 0|0   158b   45.3k    2 Dec 14 16:14:34.988
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0  269M 14.0M 0|0 0|0   157b   45.3k    2 Dec 14 16:14:35.988

复制代码

字段解释:

名字段 说明
insert/s 官方解释是每秒插入数据库的对象数量,若是是slave,则数值前有*,则表示复制集操做
query/s 每秒的查询操做次数
update/s 每秒的更新操做次数
delete/s 每秒的删除操做次数
getmore/s 每秒查询cursor(游标)时的getmore操做数
command 每秒执行的命令数,在主从系统中会显示两个值(例如 3|0),分表表明 本地|复制 命令。注: 一秒内执行的命令数好比批量插入,只认为是一条命令(因此意义应该不大)
dirty 仅仅针对WiredTiger引擎,官网解释是脏数据字节的缓存百分比
used 仅仅针对WiredTiger引擎,官网解释是正在使用中的缓存百分比
flushes For WiredTiger引擎:指checkpoint的触发次数在一个轮询间隔期间;For MMAPv1 引擎:每秒执行fsync将数据写入硬盘的次数。注:通常都是0,间断性会是1, 经过计算两个1之间的间隔时间,能够大体了解多长时间flush一次。flush开销是很大的,若是频繁的flush,可能就要找找缘由了
vsize 虚拟内存使用量,单位MB (这是 在mongostat 最后一次调用的总数据)
res 物理内存使用量,单位MB (这是 在mongostat 最后一次调用的总数据)。注:这个和你用top看到的同样, vsize通常不会有大的变更, res会慢慢的上升,若是res常常忽然降低,去查查是否有别的程序狂吃内存。
qr 客户端等待从MongoDB实例读数据的队列长度
qw 客户端等待从MongoDB实例写入数据的队列长度。
ar 执行读操做的活跃客户端数量
aw 执行写操做的活客户端数量。注:若是这两个数值很大,那么就是DB被堵住了,DB的处理速度不及请求速度。看看是否有开销很大的慢查询。若是查询一切正常,确实是负载很大,就须要加机器了
netIn MongoDB实例的网络进流量
netOut MongoDB实例的网络出流量。注:此两项字段表名网络带宽压力,通常状况下,不会成为瓶颈
conn 打开链接的总数,是qr,qw,ar,aw的总和

注:MongoDB为每个链接建立一个线程,线程的建立与释放也会有开销,因此尽可能要适当配置链接数的启动参数,maxIncomingConnections,阿里工程师建议在5000如下,基本知足多数场景。

profile集合

> db.system.profile.find()
复制代码

这种方式是将全部日志新建了一个数据库存储进去的,对性能有较大影响,不推荐这种方式。

日志

咱们也能够在mongodb的日志中查看操做的详细信息,而且能够经过conf/mongod.conf中的verbose参数进行配置,verbose的有v-vvvvv五个等级,等级越高,日志越详细,能够经过log/mongod.log文件查看mongodb的日志信息。

explain

explain()可以提供大量与查询相关的信息。对于速度比较慢的查询来讲,这是最重要的诊断工具之一。经过查看一个查询的explain()输出信息,能够知道查询使用了哪一个索引,以及是如何使用的。

最多见的explain()的输出有两种类型:使用索引的查询和没有使用索引的查询。

  1. 没有使用索引时
{
    "cursor" : "BasicCursor",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1001,
    "nscanned" : 1001,
    "nscannedObjectsAllPlans" : 1001,
    "nscannedAllPlans" : 1001,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 7,
    "nChunkSkips" : 0,
    "millis" : 1,
    "server" : "user:27017",
    "filterSet" : false
}
复制代码
  1. 使用索引时
{
    "cursor" : "BtreeCursor username_1",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1,
    "nscanned" : 1,
    "nscannedObjectsAllPlans" : 1,
    "nscannedAllPlans" : 1,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {
        "username" : [
            [
                "user1000",
                "user1000"
            ]
        ]
    },
    "server" : "user:27017",
    "filterSet" : false
}
复制代码

下面咱们以有索引的结果为例,来依次看一下这些字段表明的意思。

  • "cursor" : "BtreeCursor username_1" BtreeCursor表示本次查询使用了索引,具体来讲,是使用了“username”上的索引{“username”:1}。若是查询要对结果进行逆序遍历,或者使用了多键索引,就能够在这个字段中看到“reverse”和“multi”这样的值。

  • "isMultiKey" : false 用于说明本次是否使用了多键索引。

  • "n" : 1 本次查询返回的文档数量

  • "nscannedObjects" : 1 这是MongoDB按照索引指针去磁盘上查找实际文档的次数。若是查询包含的查询条件不是索引的一部分,或者说要求返回不在索引内的字段,MongoDB就必须依次查找每一个索引条目指向的文档。

  • "nscanned" : 1 若是有使用索引,那么这个数字就是查找过的索引条目数量,若是本次查询是一次全表扫描,那么这个数字就表明检查过的文档数目。

  • "scanAndOrder" : false MongoDB是否在内存中对结果集进行了排序

  • "indexOnly" : false MongoDB是否只使用索引就能完成这次查询。在本例中,MongoDB只使用索引就找到了所有的匹配文档,从“nscanned”和“n”相等就能够看出来。然而,本次查询要就返回匹配文档中的全部字段,而索引只包含“username”这个字段,若是就本次查询修改成{"_id":0, "username":1},那么本次查询就能够被索引覆盖了,"indexOnly"的值就会是true。

  • "nYields" : 0 为了让写入请求可以顺利执行,本次查询暂停暂停的次数。若是有写入请求需求处理,查询会周期性的释放他们的锁,以便写入可以顺利执行。然而,在本次查询中,没有写入请求,所以查询没有暂停过。

  • "millis" : 0 数据库执行本次查询所耗费的毫秒数。这个数字越小,说明效率越高。

  • "indexBounds" : {...} 这个字段描述了索引的使用状况,给出了索引的遍历范围。因为这次查询是精确匹配,因此因此只要查“user1000”这个值就能够了。

欢迎各路大佬对文章错误的地方进行指正,万分感谢,共同窗习进步。博主的GitHub地址,能够在GitHub提Issues或者直接在文章下面评论区留言。
相关文章
相关标签/搜索