【0】操作系统优化
参考:https://www.cnblogs.com/gered/p/12794878.html
【1】下载安装
参考官网手册:https://www.mongodb.com/docs/manual/installation/#std-label-tutorial-installation
(1.1)下载
官网下载:https://www.mongodb.com/try/download/community
脚本下载:
mkdir -p /data/dba/software cd /data/dba/software/ wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.4.13.tgz tar -zxf mongodb-linux-x86_64-rhel70-4.4.13.tgz
# 构建环境变量
mv mongodb-linux-x86_64-rhel70-4.4.13 /usr/local/
ln -s /usr/local/mongodb-linux-x86_64-rhel70-4.4.13 /usr/local/mongodb
echo "PATH=${PATH}:/usr/local/mongodb/bin" >> /etc/profile && source /etc/profile
【1.2】安装与启动
# 构建数据与日志目录
mkdir -p /data/mongodb/ mkdir -p /data/mongodb/{data,logs} useradd mongodb -s /sbin/false
# 配置文件,也可以使用yaml格式,但有些人说一定要是 yaml 格式,但我没用也成功了 cat <<eof >>/data/mongodb/mongodb.conf dbpath=/dba/mongodb/data logpath=/dba/mongodb/logs/mongodb.log port=27017 fork=true #是否后台守护进程 auth=true #noauth=true #verbose=true #打印更完整的详细信息 #vvvv=true journal=true #使用之后备份容灾比较容易保障,相当于mysql redolog maxConns=500 #最大连接数 logappend=true #是否开启日志追加 directoryperdb=true #数据目录存储模式,是不是每个数据库一个目录 bind_ip=0.0.0.0 pidfilepath=/data/mongodb/mongo.pid #进程文件
#cpu=true #为真的话,每4S报告一次CPU的使用情况,信息打出到日志 #nohttpinterface=false #是否打开http网页管理接口 #notablescan=false #不禁止表扫描 #profile=0 #数据库性能分析的 #slowms=200 #慢查询时间200ms #quiet=true #安静的日志输出 #syncdelay=60 #刷新日志的频率 #bind_ip=127.0.0.1,192.168.239.131 eof
# 目录权限
chown -R mongodb:mongodb /data/mongodb
chmod -R 775 /data/mongodb
# OS优化-文件修饰符,需要重启,ulimit -a 核验
cat <<eof >>/etc/security/limits.conf
mongodb hard nofile 65535
mongodb soft nofile 65535
mongodb hard nproc 65535
mongodb soft nproc 65535
eof
# 临时生效请使用 ulimit -n 65535
#开机自启
echo 'su - mongodb -c "mongod --config /data/mongodb/mongodb.conf &"'>>/etc/rc.local
yaml形式参考:
一般不支持tab键,所以要自己老老实实敲空格
启动:
su - mongodb -c "mongod --config /data/mongodb/mongodb.conf &"
# 如下,成功
[root@yj-proxysql-2 Wed Apr 06 19:32 mongodb]# su - mongodb -c "mongod --config /data/mongodb/mongodb.conf &"
about to fork child process, waiting until server is ready for connections.
forked process: 18092
child process started successfully, parent exiting
【1.3】启动与关闭 mongodb,基本查阅
#【1】启动 (1):mongod --config /mongodb/apps/mongodb/bin/mongodb.conf (2):nohup mongod --dbpath /mongod/data --config /mongodb/apps/mongodb/bin.mongodb.conf & #自动启动 Auto Start vim /etc/rc.d/rc.local su - mongodb -c "mongod --config /mongodb/apps/mongodb/bin/mongodb.conf &" #【2】停止 stop -2(在进程退出之前保存数据) (1)kill -2 `ps -ef|grep mongod|grep -v grep|awk '{print $2}'` (2)kill -2 pid (3)mongod --shutdown --config /mongodb/apps/mongodb/bin/mongodb.conf mongo -port 27107 use admin db.shutdownServer(); #【3】登录 本机:mongo 通用:mongo --host serverip:port 演示:mongo --host 192.168.239.131:27017
#【4】常用查询
(4.1)show databases / show dbs #查看所有数据库
(4.2)show tables / show collections #查看所有集合
(4.3)use db_name #切换到具体数据库
(4.4)db / select db #查看当前会话是连到哪个数据库
直接用 mongo 就是客户端登录
同时,db可以查看当前db,exit 是退出当前客户端;
那么我show dbs 它不显示
原因是我们没有用任何账户登录,而且我们的配置文件中的 auth 参数设置为了 true 所以没有权限;
我们登录后执行:
use admin db.createUser({ "user" : "admin", "pwd" : "admin888", "roles" : [{ role : "root", db : "admin" }] })
然后用它登录,如下所示,成功
mongo -uadmin -padmin888 > show dbs admin 0.000GB config 0.000GB local 0.000GB > show tables > ; > show collections > db test > show dbs admin 0.000GB config 0.000GB local 0.000GB >
test库: 登录时默认切换到的库(虚拟库)
系统库:
《1》admin库:系统预留库,MongoDB 系统管理库
《2》local库:本地预留库,存储关键日志(oplog,类似于mysqlbinlog)
《3》config库:MongoDB 配置信息库
(1.4)显示创建和隐式创建的问题
但这里有一个问题,我们在(1.3)中明明 show dbs 没有 test库,为什么可以切换到 test库呢?
是这样的MongoDB属于隐式创建,就是说 你可以use 进去一个不存在的库名,进去之后呢 如果你没有创建集合 他就会认定为 没有这个库,如果你插入了数据 或者创建了集合,这个库 自动就出现了;
这是优点,也是缺点:
比如:如果有一天 你use错了库 创建了集合插入了数据 结果发现库错了;他不会报错 说库不存在,也不会报错说集合不存在
区别:
集合可以显示创建
库一定只能隐式创建 库是不可以 显示创建的
还有就是 显示创建和隐式创建集合 有一个地方 不一样
如果你是隐式创建的集合 当你的集合里面的数据全部清空之后 你的集合会自动删除
显示创建的集合 在你没有数据的时候 也会存在;
【2】mongo shell 客户端及命令
(2.0)命令种类
{1}: db 对象相关命令
db.[TAB][TAB]:按2下 TAB,会弹出库级可选的命令
db.help() :查看库下所有命令用法描述
db.collections.[TAB][TAB]:会弹出表级(集合)可选的命令
db.collections.help():会弹出表级命令的所有用法描述
{2}:rs 复制集有关(replication set)
rs.[TAB][TAB]
rs.help()
{3}:sh 分片集群相关(sharding cluster)
sh.[TAB][TAB]
sh.help()
(2.1)mongo 命令介绍
mongo 是 MongoDB 的交互式 JavaScript Shell 界面,它为系统管理员提供了强大的界面,并为开发人员提供了直接测试数据库查询和操作的方法;
如下图:还会显示相关的 MongoDB shell 的版本
mongo -uadmin -padmin888 --port=27017
(2.2)常用 mongo shell 命令
(2.3)集合操作
# 查看当前数据库下所有集合 show collections # 删除集合 db.emp.drop() # 显示创建集合 db.createCollection("emp",options) # 创建集合的 options 参数 (1)capped:bool 类型,选填。如果为 True,则会创建固定大小的集合,当集合大小达到最大值时,会循环覆盖最早的文档记录; (2)size:数字类型,选填。为固定集合指定一个最大值(字节),如果capped 为 true,同时也需要指定该字段 (3)max:数字类型,选填。指固定集合中包含文档的最大数量限制; 注意:当集合不存在时,直接向不存在的集合插入文档,也会创建集合;但同时如果非显示创建的集合中的文档被删除完后该集合也会被删除;
db.emp.find() 查看集合中的所有文档(默认只显示前20行)
db.emp.count() 查询数据行数db.emp.totalSize() 查看集合压缩后的总大小
(2.4)显示创建和隐式创建的问题
但这里有一个问题,我们在(1.3)中明明 show dbs 没有 test库,为什么可以切换到 test库呢?
是这样的MongoDB属于隐式创建,就是说 你可以use 进去一个不存在的库名,进去之后呢 如果你没有创建集合 他就会认定为 没有这个库,如果你插入了数据 或者创建了集合,这个库 自动就出现了;
这是优点,也是缺点:
比如:如果有一天 你use错了库 创建了集合插入了数据 结果发现库错了;他不会报错 说库不存在,也不会报错说集合不存在
区别:
集合可以显示创建
库一定只能隐式创建 库是不可以 显示创建的
还有就是 显示创建和隐式创建集合 有一个地方 不一样
如果你是隐式创建的集合 当你的集合里面的数据全部清空之后 你的集合会自动删除
显示创建的集合 在你没有数据的时候 也会存在;
【3】用户及权限管理
【注意事项】
必须在配置文件里配置 auth=true ,才能有效的做权限控制
验证库:建立用户时,use切换到的库,在使用用户时,需要加上验证库才能登录
如:mongo -u appdb -p appdb --authenticationDatabase=appdb
也可以:mongo -uadmin -padmin888 127.0.0.1/admin -- 如果是本机,连 host信息都可以不用 mongo -uadmin -padmin888 admin
管理员用户:必须在 admin 下创建
常规用户:
1、建用户时,当前 use 的库,就是此用户的验证库
2、登录时,必须明确指定验证库才能登录
3、通常,管理员的验证库是 admin ,普通用户的验证库一般是所管理的库设置为验证库
4、如果直接登录到数据库吗,不进行use,默认登录到的验证库是 test,生产不建议这样玩
5、从3.6版本开始,不添加 BindIp参数,默认不让远程登录,只能本地管理员登录
(3.0)常用权限
如下图,越往下权限越大,然后下面的还包含上面的
(3.1)admin 库创建管理员账号
对应的权限是这部分:
命令演示如下:
# 必须切换库到 admin
use admin
# 创建用户 sa db.createUser({ "user" : "sa", "pwd" : "123456", "roles" : [{ role : "root", db : "admin" }] })
# 验证账户密码 是否正确存在,存在且正确返回1
db.auth('sa','123456')
# 查看当前库下所有用户信息
show users
# 删除用户
db.dropUser("sa")
(3.2)创建应用数据库用户(登录到指定数据库)
对应的权限信息如下:
use appdb db.createUser({user:"appdb",pwd:"appdb",roles:["dbOwner"]}) db.createCollection("userinfo") # 需要Mongodb以 --auth 参数启动,本文已使用
# 登录,如果是非admin账户,必须制定 --authenticationDatabase 参数,否则会登录失败
mongo -u appdb -p appdb --authenticationDatabase=appdb
登录情况:
【4】MongoDB 文档操作 CRUD
(4.0)使用for 构造测试数据
use test
for(i=0;i<100;i++){db.tmp.insert({uid:i,name:'mongodb',age:6,date:new Date()})}
// 相关类似参考
var arr= [] ;
for( var i=1; i<20000000; i++){
var uid = i; var name="name"+i; arr.push({"id":uid,"name":name});
}
db.users.insertMany(arr);
这个 _id:其实就是它的聚集索引,默认的自动生成自增的主键值(也可以手动指定),也可以叫做隐藏列;
(4.1)插入文档
推荐使用:3.2版本之后新增了,db.collection.insertOne() 和 db.collection.insertMany()。
新增单个文档:
(1)insertOne:支持 writeConcern
db.collection.insertOne( <document>, { writeConcern:<document> }
)
writeConcern 默认为1:(有复制集的情况下使用该参数)决定一个写操作落在多少个节点上才算成功。writeConcern 的取值包括
- 0:发起写操作,不关心是否成功;
- 1~N,N为集群最大节点数:写操作需要被复制到指定的节点个数才算成功;
- majority:写操作需要被复制到大多数节点上才算成功;
(2)insert:若插入的数据主键已经存在,则抛出异常 DuplicateKeyException 异常,提示主键重复,不保存当前数据。
(3)save:若_id 主键存在则更新数据,不存在则插入
(4)insertMany:
db.collection.insertMany( [ <document 1>,<document 2>,...], { writeConcern: <document>, ordered: <boolean> } )
writeConcern 默认为1; ordered:指定是否按顺序写入,默认为 true,按书序写入;
案例演示:这里要注意看不同的API 的返回值也是不一样的
> use appdb switched to db appdb > db.emp.insert({name:"张三",age:20}) WriteResult({ "nInserted" : 1 }) > db.emp.find() { "_id" : ObjectId("624ef1c1a77fc161ae69f8ee"), "name" : "张三", "age" : 20 } > db.emp.insertMany([{name:"李四",age:21},{name:"王五",age:22}]) { "acknowledged" : true, "insertedIds" : [ ObjectId("624ef243a77fc161ae69f8ef"), ObjectId("624ef243a77fc161ae69f8f0") ] } > db.emp.insertOne({name:"赵六",age:23}) { "acknowledged" : true, "insertedId" : ObjectId("624ef2a3a77fc161ae69f8f1") } >
同时 insert / save 也可以批量写入
(4.2)查询文档
一般形式:
db.collection.find(query,projection)
query:可选,使用查询操作符指定查询条件(比如大于等于小于等于)
projection:可选,使用投影操作符返回指定的键值(列)
db.collection.findOne:只会返回查询到的第一行
# 构造测试数据:重复执行多次,超过20行 db.user.insertMany([{username:"张三",age:20,hobby:["打游戏","看电影"]},{username:"李四",age:21,hobby:["唱歌","跳舞"]},{username:"王五",age:22,hobby:["游泳","健身"]}]) # 默认显示前20行(根据_id 从小到大,先插入先查出),然后输入it继续往下显示20行,以此类推 db.user.find()
DBQuery.shellBatchSize=50 #把默认的分页行数20行,改成一次显示50行
db.tmp.find().pretty() # 以标准json格式显示结果集 # 统计集合中文档的数量 db.user.find().count()
db.emp.totalSize() # 查看集合中的索引 + 数据压缩存储之后的大小
# 写query条件,查询 usernamme为张三的
db.user.find({username:"张三"})
db.user.find({username:"张三",age:20})
db.user.find({username:"张三",age:{$gt:20}}) # username为张三,且年龄大于等于20
db.user.find({age:{$in:[20,21,22]}}) # age in (20,21,22)
db.user.find({_id:ObjectId("62522c2b849acd5782fee534")}) # 查某个具体ObjectId
# 写projection 返回指定列,username:1,1表示显示,0表示不显示(默认的 _id 也是会返回的)
db.user.find({username:"张三",age:20},{username:1})
# 排序,负数为降序
db.user.find({age:{$in:[20,21,22]}}).sort({age:-1})
# 分页查询 skip 与 limit ,这个就和像mysql中的 Limit skip,limit
db.user.find({age:{$in:[20,21,22]}}).sort({age:-1}).skip(2).limit(1)
# 正则表达式匹配查询,使用 $regex 操作符来设置匹配正则
db.user.find({username:{$regex:"^张"}}) # 查询 username 姓张的文档
db.user.find({hobby:{$regex:"身"}}) # 查询 hobby 列中有包含 身 这个字的文档
# 模糊查询,和上面的正则匹配也是类似,功能和mmysql中 like '%%' 相同
db.user.find({hobby:/身/}) # 查询 hobby 列中有包含 身 这个字的文档
查询条件对照表:
逻辑对照表:
查询逻辑运算:
(4.3)更新文档
一般形式:
db.collection.update(query,update,options)
query:描述更新的查询条件
update:描述更新的动作及新的内容,比如更新某一个字段
options:描述更新的选项
upsert:可选,匹配条件对应行不存在则则插入。默认 false,不插入
multi:可选,是否按条件查询出的多条记录全部更新。默认false,只更新找到的第一条记录
writeConcern:可选,决定一个写操作落到多少个节点上才算成功,和前面insert一样
# 简化 API , 但注意,用不同的API,返回的结果反馈是不一样的
updateOne:更新单个文档
updateMany:更新多个文档
replaceOne:替换单个文档
findAndModify:兼容了查询和修改文档的功能,但只能操作单行
db.books.findAndModify({
query:{_id:ObjectId("62522c29849acd5782fee526")},
update:{$inc:{age:1}},
new:true # 查看更新之后的值
})
# findAndModify 案例
db.user.findAndModify({query:{_id:ObjectId("62522c29849acd5782fee526")},update:{$inc:{age:1}},new:true})
案例:
# 更新文档,age字段+1
db.user.update({_id:ObjectId("62522c2b849acd5782fee534")},{$inc:{age:1}})
# 更新操作后 返回结果,是否匹配到,Upsert情况影响结果,修改影响行数
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
# 添加字段(但这里只会影响第一行)
db.user.update({username:"张三"},{$set:{publishDate:new Date()}})
# 添加字段(影响所有满足条件的行)
db.user.update({username:"张三"},{$set:{publishDate:new Date()}},{multi:true})
# 存在则更新,不存在则插入
db.user.update({username:"赵六"},{$set:{address:"杭州市"}},{upsert:true})
# replace 彻底覆盖,直接把整个文档给覆盖掉了
> db.user.replaceOne({username:"赵六"},{a:1})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.user.find({"_id" : ObjectId("625259b86319813881f48f91")})
{ "_id" : ObjectId("625259b86319813881f48f91"), "a" : 1 }
>
# 不写更新操作符,即为整个文档级别的彻底替换
> db.user.update({a:1},{b:2})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({"_id" : ObjectId("625259b86319813881f48f91")})
{ "_id" : ObjectId("625259b86319813881f48f91"), "b" : 2 }
>
更新操作符:
(4.4)删除文档
可以使用 Remove、delete、findOneAndDelete
remove:(不推荐使用)
remove命令需要配合查询条件使用;
匹配查询条件的文档会全部被删除;
指定一个空文档条件会删除所有文档;
演示:
db.user.remove({age:24}) // 删除 age 等于 24 的记录 db.user.remove({age:{$gt:24}}) // 删除 age 大于 24 的记录 db.user.remove({}) // 删除所有记录,不推荐使用,还不如直接删除集合效率会更高 db.user.remove() // 报错 # 默认的 remove 会删除匹配到的所有文档,如果希望只限定删除一个文档,则需要加 justOne 参数,情况如下: db.user.remove({age:23},{justOne:true}) db.user.remove({age:23},true)
delete:
推荐使用 deleteOne() 和 deleteMany() 方法删除
db.user.deleteMany({}) // 删除集合下所有文档,推荐使用db.drop("user") 直接删除集合 db.user.deleteMany({age:24}) // 删除 age 为 24 的全部文档 db.user.deleteOne({age:24}) // 删除 gae 为 24 的查询结果中的第一个文档
findOneAndDelete:
remove,deleteOne 等命令在删除文档后只会返回确认性的信息,如果希望获得被删除的文档,可以使用 findOneAndDelete;
且可以很方便的排序等情况(否则默认一般是根据_id 大小的升序);
remove、deleteOne等命令只能按默认顺序删除,但findOneAndDelete 可以自己调控顺序;利用这个特性可以实现 队列的先进先出;
# 如下,查询条件为 age=22 且 username字段升序排序结果集,并删除其第1行 db.user.findOneAndDelete({age:22},{sort:{username:1}})