mongoDB学习笔记

官网: https://www.mongodb.org

mogodb是文档数据库,存储的是文档(Bson,json的二进制数据),内部执行引擎为JS解析器(谷歌v8),把文档储存成bson结构,在查询是转换为JS对象,可以通过熟悉的JS语法来操作。

mogodb和传统数据库区别:

传统数据库:结构化数据,设计了表结构后,每一行内容的列的类型是固定的。

mogodb文档数据库:表下的每篇文档都可以有自己独立的数据结构(json对象都可以有自己独特的属性和值)

1 安装mongodb

1.1 在主机安装和启动mongodb

(1) 安装mongodb

# 下载地址https://www.mongodb.org/dl/linux/x86_64
# 下载双数版本,例如3.6,而单数3.5是开发版本

#下载
wget http://downloads.mongodb.org/linux/mongodb-linux-x86_64-3.6.3.tgz?_ga=2.249742406.1729791217.1523023903-593615807.1523023903

#解压并安装到/usr/local目录下
tar xvf mongodb-linux-x86_64-3.6.3.tgz
mv mongodb-linux-x86_64-3.6.3 mongodb
mv mongodb /usr/local

# 把bin目录添加到环境变量
echo 'export PATH="$PATH:/usr/local/mongodb/bin"' >> /etc/bashrc

# 使当前终端生效
source /etc/bashrc

# 创建存放数据和日志文件夹
mkdir -p /data/mongodb/db
mkdir -p /data/mongodb/log

mongodb安装bin目录下程序说明:

  • bsondump:导出bson结构
  • mongo:客户端
  • mongod:服务端
  • mongodump:整体数据库导出(二进制)
  • mongoexport:导出易识别的json文档或csv文档
  • mongorestore:数据库整体导入
  • mongos:路由器(分片时用)
  • mongostat:观察状态
  • mongotop:类似linux的top命令


(2) 启动mongodb

以参数方式启动

mongod --dbpath /data/mongodb/db --logpath /data/mongodb/log/mongodb.log --port 27017 --fork
# --dbpath:数据库储存目录,--logpath日志储存目录,--fork后台运行,--port运行端口
# 使用mongod --help查看有哪些参数:
    # --dbpath 数据库路径(数据文件)
    # --logpath 日志文件路径
    # --master 指定为主机器
    # --slave 指定为从机器
    # --source 指定主机器的IP地址
    # --pologSize 指定日志文件大小不超过64M.因为resync是非常操作量大且耗时,最好通过设置一个足够大的oplogSize来避免resync(默认的 oplog大小是空闲磁盘大小的5%)。
    # --logappend 日志文件末尾添加,即使用追加的方式写日志
    # --journal 启用日志
    # --port 启用端口号
    # --fork 在后台运行
    # --only 指定只复制哪一个数据库
    # --slavedelay 指从复制检测的时间间隔
    # --auth 是否需要验证权限登录(用户名和密码)
    # --syncdelay 数据写入硬盘的时间(秒),0是不等待,直接写入
    # --notablescan 不允许表扫描
    # --maxConns 最大的并发连接数,默认2000  
    # --pidfilepath 指定进程文件,不指定则不产生进程文件
    # --bind_ip 绑定IP,绑定后只能绑定的IP访问服务


自定义mongodb配置文件方式启动

新建配置文件:

vim /usr/local/mongodb/mongodb.conf

填写下面内容如下:

fork=true   # 允许程序在后台运行
#auth=true  # 开启认证
logpath=/var/log/mongo/mongo.log
logappend=true      # 写日志的模式:设置为true为追加。默认是覆盖
dbpath=/data/mongo/    # 数据存放目录
pidfilepath=/tmp/mongo/mongo.pid    # 进程ID,没有指定则启动时候就没有PID文件。默认缺省。
port=27017
bind_ip=127.0.0.1   # 绑定地址。默认127.0.0.1,只能通过本地连接

# 设置为true,修改数据目录存储模式,每个数据库的文件存储在DBPATH指定目录的不同的文件夹中。
# 使用此选项,可以配置的MongoDB将数据存储在不同的磁盘设备上,以提高写入吞吐量或磁盘容量。默认为false。
# 建议一开始就配置次选项
directoryperdb=true

# 禁止日志 
# 对应 journal 启用操作日志,以确保写入持久性和数据的一致性,会在dbpath目录下创建journal目录
nojournal=true

## max connections
# 最大连接数。默认值:取决于系统(即的ulimit和文件描述符)限制。
# MongoDB中不会限制其自身的连接。当设置大于系统的限制,则无效,以系统限制为准。
# 设置该值的高于连接池和总连接数的大小,以防止尖峰时候的连接。
# 注意:不能设置该值大于20000。
maxConns=1024

启动mongodb:

mongod -f /usr/local/mongodb/mongodb.conf

关闭mongodb服务:

mongod –shutdown –dbpath /data/mongodb/db


(3) 连接mongodb

mongodb客户端连接:

mongo –host 192.168.8.200 –port 27017

有密码连接:

mongo -u “vison” -p “123456” –host 192.168.8.200 –port 27017 –authenticationDatabase “crawler”


(3) 允许外网访问mongodb

如果使用命令行方式启动,则添加参数–bind_ip 0.0.0.0;如果使用配置文件方式启动,则修改配置文件字段为bind_ip=0.0.0.0。


(4) 查看mongodb连接数

netstat -nat | grep -i 27017 | wc -l


1.2 在docker安装mongodb

(1) 默认配置启动

docker run -d –name demo-mongo mongo:latest

docker run -d --name demo-mongo \
    -e MONGO_INITDB_ROOT_USERNAME=root \
    -e MONGO_INITDB_ROOT_PASSWORD=123456 \
    mongo


(2) 自定义配置文件启动

docker run -d –name demo-mongo -p 27017:27017 -v /data/mongo:/etc/mongo mongo –config /etc/mongo/mongod.conf

mongod.conf配置文件内容如下:

fork=true   # 允许程序在后台运行
#auth=true  # 开启认证
logpath=/var/log/mongo/mongo.log
logappend=true      # 写日志的模式:设置为true为追加。默认是覆盖
dbpath=/etc/mongo    # 数据存放目录
pidfilepath=/tmp/mongo/mongodb.pid    # 进程ID,没有指定则启动时候就没有PID文件。默认缺省。
port=27017
bind_ip=127.0.0.1   # 绑定地址。默认127.0.0.1,只能通过本地
directoryperdb=true
nojournal=true
maxConns=1024


(3) docker-compose命令启动,同时启动mongodb管理界面服务

version: '3.1'
services:
  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: 123456
    ports:
    - 27017:27017
    volumes:
    - /data/mongo:/etc/mongo

  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: 123456



2 mongo账号管理

在mongodb中有一个admin数据库,牵涉到服务器配置层面的操作,需要先切换到admin数据库,use admin命令相当于进入超级用户管理模式。

mongo的用户是以数据库为单位来建立的,每个数据库有自己的管理员。

我们在设置用户时,需要先在admin数据库下建立管理员,这个管理员登陆后,相当于超级管理员,然后可以切换到其他库,添加普通用户。

注意: mongodb服务器启动时,默认不是需要认证的,要让用户生效,需要启动服务器时指定–auth选项。添加用户后,我们再次退出并登陆,认证才有效。

2.1 创建管理员

# (1)创建用户管理员(在管理身份验证数据库)。
use admin
db.createUser(
  {
    user: "admin",
    pwd: "xxx",
    roles: [{role: "userAdminAnyDatabase", db: "admin"}]
  }
)

# (2)重新启动MongoDB实例与访问控制。
mongod --dbpath /data/mongodb/database --logpath /data/mongodb/log/mongodb.log --port 27017 --fork --auth

# (3)连接和用户管理员进行身份验证。
mongo --port 27017 -u "vison" -p "xxx" --authenticationDatabase "admin"


2.2 创建自定义用户

# (1) 给其他数据库test配置一个读写权限的用户
use test
db.createUser(
  {
    user: "myTester",
    pwd: "xyz123",
    roles: [ { role: "readWrite", db: "test" },
             { role: "read", db: "reporting" } ]
  }
)

# (2)mytest连接和验证。
mongo --port 27017 -u "myTester" -p "xyz123" --authenticationDatabase "test"
# 或登录后执行命令验证
use test
db.auth("myTester","xyz123")


2.3 查看已经创建的用户

# 查看创建的用户需要在管理员数据库上执行
use admin
db.system.users.find()

# 查看当前数据库用户
show users


2.4 删除用户

# 删除一个用户
use dbname
db.system.users.remove({user:"username"})

# 删除管理员
use admin
db.system.users.remove({user:"admin"})



3 mongodb增删改查操作

3.1 库和集合的基础命令

(1) 库级命令

# 查看所有数据库
show dbs # 或使用mysql命令show databases;

# 切换到指定数据库
use 库名

# 创建数据库(mongodb不提供命令)
# mongodb的数据库是隐式创建的(创建表时自动创建),就算没有指定的库,也可以使用use命令。

# 删除数据库
db.dropDatabase()


(2) 表(集合)级命令

# 显示所有表
show collections; # 或mysql命令show tables;

# 创建表
db.createCollection('user')

# 删除表
db.user.drop()


3.2 insert 插入数据

(1) 增加一个文档

# 不指定id
db.user.insert({name:'张三',age:22})

# 指定id
db.user.insert({_id:10001,name:'李四',age:23})


(2) 一次增加多个文档

db.user.insert([{name:'张三',age:24},{_id:10002,name:'李四',age:25},{name:'王五',age:26}])


3.3 remove 删除数据

语法: > db.collection.remove(查询表达式,选项)

查询表达式: 类似mysql的where条件,不加条件会删除表所有数据。 选项: 是否删除一行,{justOne:true/false},默认是false

# 示例
db.user.remove({_id:10001})
db.user.remove({age:24},{ justOne:true})

注:实际应用中一般不使用删除,使用一个字段来标记删除。


3.4 update 修改数据

语法:

db.collection.updae(查询表达式,新值,选项)

错误示例:

db.user.update({name:‘张三’},{age:33})

注意:这种方式是新文档直接替换了旧文档,必须指定赋值表达式,常用表达式有:

表达式 说明
$set 设置字段新的值
$unset 删除指定的列
$inc 增长
$rename 重命名该列
$setOnInsert 当upsert时,设置字段的值


(1) 赋值表达式

# $set设置字段新值(如果字段不存在则新增加该列)
db.user.update({name:'张三'},{$set:{age:33}})

# $inc自增字段值
db.user.update({name:'张三'},{$inc:{age:1}})

# $rename修改字段名
db.user.update({name:'张三'},{$rename:{sex:'gender'}})

# $unset删除指定列
db.user.update({name:'张三'},{$unset:{phone:1}})

# setOnInsert当upsert为true时,添加附加字段
db.user.update({name:'赵六'},{$setOnInsert:{age:25,gender:'male'}},{upsert:true})

# 多种赋值表达可以一次执行
db.collection.updae(查询表达式,{
{$set:{...}},
{$unset:{...}},
{$inc:{...}},
{$rename:{...}}
})


(2) 修改选项

选项包括upsert和multi

  • upsert:没有匹配的行,是否直接插入该行,默认为false。
  • multi:查询表达式匹配中多行,是否修改多行,默认false(只修改一行)
# 示例
db.user.update({name:'李四'},{$set:{age:44}},{upsert:true})
db.user.update({gender:'male'},{$set:{age:55}},{multi:true})


3.5 query 查询

# 语法:db.collection.find(查询表达式,查询的列)
db.user.find() # 查询所有

# 查询匹配条件的所有列
db.user.find({name:'张三'})

# 查询匹配条件的指定列({列名:1,列名:1,...})
db.user.find({name:'张三'},{age:1})

# 不查询id列
db.user.find({name:'张三'},{_id:0,age:1})


(1) 字段值查询

# 查询主键为32的商品
db.goods.find({goods_id:32})

# 子文档查询(指向子文档的key是字符串)
db.stu.find({'score.yuwen':75})

# 数组元素查询(指向子文档的key是字符串)
db.stu.find({'hobby.2':'football'})


(2) 范围查询

# $ne 不等于
# 查询不属第3栏目的所有商品
db.goods.find({cat_id:{$ne:3}})

# $gt 大于
# 查询高于3000元的商品
db.goods.find({shop_price:{$gt:3000}})

# $gte 大于等于
# 查询高于或等于3000元的商品
db.goods.find({shop_price:{$gte: 3000}})

# $lt 小于
# 查询低于500元的商品
db.goods.find({shop_price:{$lt:500}})

# $lt 小于等于
# 查询低于500元的商品
db.goods.find({shop_price:{$lte:500}})


(3) 集合查询

# $in 在集合,(当查询字段是数组时,不需完全匹配也会命中该行)
# 查询栏目为4和11的商品
db.goods.find({cat_id:{$in:[4,11]}})

# $nin 不在集合
# 查询栏目不是3和4的商品
db.goods.find({cat_id:{$nin:[3,4]}})

# $all完全匹配,(当查询字段是数组时,必须完全匹配才命中该行)
db.goods.find({cat_id:{$all:[4,11]}})


(4) 逻辑查询

# $and 逻辑与,必须都满足条件
# 查询价格为100到500的商品
db.goods.find({$and:[{shop_price:{$gt:100}},{shop_price:{$lt:500}}]})

# $or 逻辑或,至少满足一个条件
# 查询价格小于100或大于3000的商品
db.goods.find({$or:[{shop_price:{$lt:100}},{shop_price:{$gt:3000}}]})

# $nor 与非逻辑
# 查询不在栏目3,并且价格不小1000的商品
db.goods.find({$nor:[{cat_id:3},{shop_price:{$lt:1000}}]})


(5) 元素运算符查询

# $mod 求余
# 查询年龄对10求余为0的用户
db.goods.find({age:{$mod:[10,0]}})

# $exist 查询列是否存在
# 查询有电话属性的用户
db.user.find({phone:{$exists:1}})

# $type 查询属性为指定类型的文档
# 查询为浮点型的文档
db.user.find({phone:{$type:1}})

# 查询为字符串型的文档
db.user.find({phone:{$type:2}})


(6) 其他常用查询

# 统计行数
db.goods.count()
db.goods.find({cat_id:3}).count()

# 查询每页数据
    # 按指定属性排序,1表示升序,-1表示降序
    sort({属性:1|-1})
    # 跳过行数
    skip(num)
    # 限制取多少行
    limit(num)

# 页码page,每页行数n,则skip数=(page-1)*n
db.goods.find({cat_id:3}).sort({shop_price:-1}).skip(0).limit(10)


4 索引

索引提高查询速度,降低写入速度,权衡常用的查询字段,不建议在太多列上建索引。在mongodb中,索引可以按字段升序/降序来创建,便于排序。默认是用btree来组织索引文件,也允许建立hash索引。

# 在test库下stu下创建10000行数据的成绩表
for (var i=1;i<=10000;i++){db.stu.insert({sn:i,name:'stu'+i,email:'stu'+i+'@126.com',score:{yuwen:i%80,shuxue:i%90,yingyu:i%100}})}


4.1 普通索引

(1) 单列索引

在表stu创建sn列索引

db.stu.ensureIndex({sn:1})

1表示升序,-1表示降序


(2) 多列索引

在表stu创建sn列和name列共同索引

db.stu.ensureIndex({sn:1,name:1})

1表示升序,-1表示降序


(3) 子文档索引

在表stu的score列下的yuwen字段创建索引

db.stu.ensureIndex({‘score.yuwen’:1})

1表示升序,-1表示降序


4.2 唯一索引

创建唯一索引后字段值都是唯一的

在表stu创建email列索引

db.stu.ensureIndex({email:1},{unique:true})


4.3 稀疏索引

稀疏索引的特点:如果针对field做索引,针对不含field列的文档,将不建立索引。与之相对的普通索引会把该文档的field列的值认为NULL,并建索引。

使用场景:小部分文档含有某列时。

在表stu创建phone列稀疏索引

db.stu.ensureIndex({age:1},{sparse:true})


4.4 哈希索引

哈希索引速度比普通索引快,缺点是不能对范围查询进行优化。使用场景:随机性强的散列

在表stu创建email列哈希索引

db.stu.ensureIndex({email:‘hashed’})


4.5 重建索引

一个表经过很多次修改后,导致表的文件产生空洞,索引文件也如此。可以通过索引的重建,减少索引文件碎片,并提高索引的效率,类似mysql中的optimize table

在表stu重建索引

db.stu.reIndex()


4.6 删除索引

# 语法
db.collection.dropIndex({filed:1/-1});

# 示例
db.stu.dropIndex({sn:1})
db.stu.dropIndex ({email:'hashed'})


4.7 查看索引和执行计划

# 查看表索引
db.stu.getIndexes()

# 查看执行计划
db.stu.find({sn:5555}).explain() # 默认只输出queryPlanner

# 其中explain()参数有三个,分别是'queryPlanner'、'executionStats'、'allPlansExecution'
db.stu.find({sn:5555}).explain('executionStats')

# explain分析结果的几个重要字段,通过结果分析可以判断是否需要优化执行语句

executionStats属性下的字段:

  • executionTimeMillis:查询耗时,单位(ms)
  • totalDocsExamined:扫描文档数
  • executionStages.stage:”COLLSCAN”表示全表扫描,”FETCH”表示索引扫描
  • executionStages. executionTimeMillisEstimate:索引扫描耗时,单位(ms)

winningPlan.inputStage属性下的字段:

  • indexName:索引名字



5 数据备份与恢复

5.1 mongoexport导出json/csv结构化数据

mongoexport命令导出的只有数据,不包括索引,mongoexport参数说明:

  • -h:主机ip或域名 (默认localhost)
  • –port:mongodb使用端口 (默认27107)
  • -u:认证用户名 (当需要认证时用)
  • -p:认证密码 (当需要认证时用)
  • -d:指定导出的库名
  • -c:指定导出的表名
  • -f:指定导出的列名
  • -q:查询条件,例如:’{sn:{“$lte”:100}}’
  • -o:保存导出数据文件位置
  • –csv:指定导出csv格式 (便于和传统数据库交换数据),默认导出的json格式
# 导出json数据
mongoexport -h 192.168.8.200 --port 27017 -u vison -p 123456 -d test -c stu -f sn,name,email -q '{sn:{"$lte":100}}' -o /home/vison/src/test.stu.json

# 导出csv数据
mongoexport -h 192.168.8.200 --port 27017 -u vison -p 123456 -d test -c stu -f sn,name,email -q '{sn:{"$lte":100}}' --csv -o /home/vison/src/test.stu.csv


5.2 mongoimport导入json/csv结构化数据

mongoimport命令导出的只有数据,不包括索引,mongoimport参数说明:

  • -h:主机ip或域名 (默认localhost)
  • –port:mongodb使用端口 (默认27107)
  • -u:认证用户名 (当需要认证时用)
  • -p:认证密码 (当需要认证时用)
  • -d:指定导入的库名
  • -c:指定导入的表名(不存在会自己创建)
  • –type:csv/json(默认json)
  • –headline:当导入csv文件时,需要跳过第一行列名
  • –file:导入数据文件的位置
# 导入json数据
mongoimport -h 192.168.8.200 --port 27017 -u vison -p 123456 -d test -c stu_json --type json --file /home/vison/src/test.stu.json

# 导入csv数据
mongoimport -h 192.168.8.200 --port 27017 -u vison -p 123456 -d test -c stu_csv --type csv --headerline --file /home/vison/src/test.stu.csv

# 注:老版本需要指定-fields参数


5.3 mongodump导出二进制数据

mongodump导出数据是包括索引的,mongodump的参数说明:

  • -h:主机ip或域名 (默认localhost)
  • –port:mongodb使用端口 (默认27107)
  • -u:认证用户名 (当需要认证时用)
  • -p:认证密码 (当需要认证时用)
  • -d:指定导出的库名
  • -c:指定导出的表名 (可选)
  • -q:查询条件(可选),例如:’{sn:{“$lte”:100}}’
  • -o:保存导出数据文件位置(默认是导出到mongo下的dump目录)
  • –gzip:导出并压缩

示例:

mongodump -h 192.168.8.200 –port 27017 -u vison -p 123456 -d test –gzip -o /home/vison/src/mongoDump

注:可以写脚本每天凌晨访问少的时候备份一次数据


5.4 mongorestore导入二进制数据

mongorestore导出数据是包括索引的,mongorestore的参数说明:

  • -h:主机ip或域名 (默认localhost)
  • –port:mongodb使用端口 (默认27107)
  • -u:认证用户名 (当需要认证时用)
  • -p:认证密码 (当需要认证时用)
  • -d:指定导出的库名
  • -c:指定导出的表名 (可选)
  • –dir:保存导入数据文件位置
  • –gzip:导出并压缩

示例:

mongorestore -h 192.168.8.200 –port 27017 -u vison -p 123456 -d test –gzip –dir /home/vison/src/mongoDump/test



获取mongodb状态信息

首先在admin下创建一个新能够获取mongodb状态权限的用户

use admin
db.createUser(
  {
    user: "stat",
    pwd: "123456",
    roles: [{role: "mongostatRole", db: "admin"}]
  }
)

每隔2秒采集一次信息保存到文件stat.log

mongostat -u stat -p 123456 –authenticationDatabase admin -n 2 –json >> stat.log

每一条统计信息如下:

{
    "localhost:27017":{
        "arw":"1|0",
        "command":"2|0",
        "conn":"4",
        "delete":"*0",
        "dirty":"0.0%",
        "flushes":"0",
        "getmore":"0",
        "insert":"*0",
        "net_in":"18.9k",
        "net_out":"79.0k",
        "qrw":"0|0",
        "query":"94",
        "res":"49.0M",
        "time":"14:41:32",
        "update":"*0",
        "used":"0.1%",
        "vsize":"938M"
    }
}

更多mongostat命令说明看官网 https://docs.mongodb.com/manual/reference/program/mongostat/



非正常关闭mongodb导致无法启动的解决方法

非正常关闭包括断电或强制关闭,造成文件mongod.lock锁住了,所以无法正常启动,解决方法:

(1) 删除mongod.lock文件,文件存放一般在数据库文件夹里
rm /data/mongodb/db/mongod.lock

(2) repair的模式启动
mongod -f /usr/local/mongodb/mongodb.conf –repair

(3) 启动mongodb
mongod -f /usr/local/mongodb/mongodb.conf


专题「数据库」的其它文章 »