MongoDB
官方文档:https://docs.mongodb.com/
中文文档:https://www.mongodb.org.cn/
mongoDB的生态、理念非常先进而且成熟、但是mongoDB不但有开源版本,还有企业版本。以是有部门公司比力担心,哪天无法使用mongoDB了,以是也会产生一些替换产物。
- DynamoDB : AWS
- SequoiaDB : 巨杉数据库
复制代码 基本介绍
MongoDB 是由C++语言编写并基于分布式文件存储的开源数据库,属于NOSQL 。
MongoDB 是一款介于关系数据库和非关系数据库之间的产物,是非关系数据库当中功能最丰富,最像关系数据库的NOSQL数据库。它面向文档存储,而且安装和操纵起来都比力简单和容易,而且它支持各种盛行编程语言举行操纵,如Python,Node.js,Java,C++,PHP,C#等。
目前在大数据、内容管理、持续交付、移动应用、社交应用、用户数据管理、数据中心等领域皆有广泛被使用。
什么是mongodb?
MongoDB是一个文档数据库,提供好的性能,领先的非关系型数据库。采用BSON存储文档数据。
BSON()是一种类json的一种二进制形式的存储格式,全称:Binary JSON.
相对于json多了date范例和二进制数组。
为什么用MongoDB?
- 数据结构简单,没有固定的数据结构
- 没有复杂的连接,没有复杂的外键约束
- 深度查询本领,MongoDB支持动态查询,查询api是基于v8引擎(javascript)。
- 容易调试
- 容易扩展
- 不需要转化/映射应用对象到数据库对象
- 使用内部内存作为存储工作区,以便更快的存取数据。
mongodb的应用场景
- 大数据
- 内容管理系统
- 移动端App
- 数据管理
- 日记处理
- 游戏道具处理
MongoDB相对于RDBMS的上风
游戏道具:
[{“id”:1, “name”: “衣服”, “防御值”: 1000},
{“id”:2, “name”: “武器”, “攻击力”: 9999, “攻击间隔”: 50},
{“id”:3, “name”: “鞋子”, “移动速率”: 1.1, “颜色”: “蓝色”},]
idname防御值攻击力攻击间隔移动速率颜色2武器None999950NoneNone1衣服1000NoneNoneNoneNone3鞋子NoneNoneNone1.1蓝色
- 无固定结构 。面向文档,以 JSON 格式的文档保存数据,数据结构由键值(key=>value)对组成。MongoDB 的文档雷同于 JSON 对象。字段值可以包罗其他文档,数组及文档数组,单个对象的结构是清晰的。
- 没有复杂的表连接。不需要维护表与表之间的内在关联关系。
- 查询功能强大,丰富的查询语言。MongoDB的查询功能几乎与SQL一样强大,使用基于文档的查询语言,可以对文档举行动态查询,提供丰富的查询操纵和索引支持,也有事务操纵,可以更快地更稳固的访问数据。(mongoDB4.0以后才真正支持所谓的多文档事务操纵)
- 易于调优和扩展。具备高性能、高可用性及可伸缩性等特性
- 应用步伐对象与数据库对象天然对应。
- 可分片,对数据存储友爱,可以基于内存存储或者硬盘文件存储。
- 任何属性都可以建立索引,而且提供丰富多样的索引。
术语对比
RDBMSMongodb描述库(database)库(database)表(Table)集合(Collection)行/记录(Row/Record)文档(Document)Document就是json结构的一条数据记录列/字段(Column/Field)字段/键/域(Field)属性字段名主键(Primary Key)对象ID(ObjectId)_id: ObjectId(“10c191e8608f19729507deea”)索引(Index)索引(Index)都有普通索引, 唯一索引, 主键索引, 联合索引这么区分的 MySQL 与 MongoDB 的差别
差别在多方面,比方:数据的表示、查询、关系、事务、模式的计划和定义、速率和性能。
MongoDB 是由 C++语言编写的,是一个基于分布式文件存储的开源数据库系统。
在高负载的环境下,添加更多的节点,可以保证服务器性能。
MongoDB 旨在为 WEB 应用提供可扩展的高性能数据存储办理方案。
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。
MongoDB 文档雷同于 JSON 对象。字段值可以包罗其他文档,数组及文档数组。
MongoDB 是一个面向文档的数据库,目前由 10gen 开辟并维护,它的功能丰富齐全,以是完全可以替换 MySQL。
与 MySQL 等关系型数据库相比,MongoDB 的优点如下:
- 弱一致性,更能保证用户的访问速率。
- 文档结构的存储方式,能够更便捷的获取数据。
- 内置 GridFS,支持大容量的存储。
- 内置 Sharding。
- 第三方支持丰富。(这是与其他的 NoSQL 相比,MongoDB 也具有的上风)
- 性能良好
MongoDB 相对于MySQL,它还算比力年轻的一个产物,以是它的题目,就是成熟度肯定没有传统 MySQL那么成熟稳固。以是在使用的时候,第一,尽量使用稳固版,不要在线上使用开辟版,这是一个大原则;
另外一点,备份很重要,MongoDB 假如出现一些异常环境,备份肯定是要能跟上。除了通过传统的复制的方式来做备份,离线备份也还是要有,不管你是用什么方式,都要有一个完整的离线备份。每每最后出现了特殊环境,它能帮助到你;
另外,MongoDB 性能的一个关键点就是索引,索引是不是能有比力好的使用服从,索引是不是能够放在内存中,这样能够提升随机读写的性能。假如你的索引不能完全放在内存中,一旦出现随机读写比力高的时候,它就会频繁地举行磁盘交换,这个时候,MongoDB 的性能就会急剧降落,会出现波动。
另外,MongoDB 还有一个最大的缺点,就是它占用的空间很大,由于它属于典型空间换时间原则的范例。那么它的磁盘空间比普通数据库会浪费一些,而且到目前为止它还没有实现在线压缩功能,在 MongoDB 中频繁的举行数据增编削时,假如记录变了,比方数据大小发生了变革,这时候容易产生一些数据碎片,出现碎片引发的效果,一个是索引会出现性能题目,另外一个就是在肯定的时间后,所占空间会莫明其妙地增大,以是要定期把数据库做修复,定期重新做索引,这样会提升 MongoDB 的稳固性和服从。
成功运行了mongoDB以后,对于mongod和mongosh的区别
mongod是处理MongoDB系统的主要历程。主要负责处理数据哀求,管理数据存储和执行后台管理操纵。当我们运行mongod下令意味着正在启动MongoDB历程, 并且在后台运行。
mongosh是一个下令行实现的客户端操纵mongodb的工具,用于连接一个特定的mongod实例。当我们没有带参数运行mongosh下令它将使用默认的localhost:27017和mongod举行连接。
mongoDB的默认端口:27017
- # 查看交互终端的版本
- mongosh --version
- # 或者终端内部使用 version()
复制代码 sc stop MongoDB
sc delete MongoDB
mongod --config “D:\mongo\bin\mongod.cfg” --install
net start MongoDB
sc query MongoDB
基本操纵
通用操纵
查察帮助文档
终端效果:
- db 查看当前用户所在数据库的数据库对象
- use <db> set current database 切换操作的数据库
- show 'show databases'/'show dbs': Print a list of all available databases. 查看当前系统所有的数据库
- 'show collections'/'show tables': Print a list of all collections for current database. 查看当前数据库所有的数据集合
- 'show profile': Prints system.profile information. 查看当前系统的环境配置
- 'show users': Print a list of all users for current database. 查看当前数据库中所有的管理员用户
- 'show roles': Print a list of all roles for current database. 查看当前数据库中所有的管理员角色
- 'show log <type>': log for current connection, if type is not set uses 'global' 查看指定日志信息
- 'show logs': Print all logs. 查看全部日志
- it result of the last line evaluated; use to further iterate 查看更多的查询结果,相当于下一页
- exit quit the mongo shell 退出终端
- sleep Sleep for the specified number of milliseconds 睡眠函数,指定睡眠毫秒
- load Loads and runs a JavaScript file into the current shell environment 加载指定js文件的js代码到当前mongo shell交互终端下
- cls Clears the screen like console.clear() 清屏
- print Prints the contents of an object to the output 打印输出一个终端对象
复制代码 当前服务器状态
终端效果:
- {
- "host" : "ubuntu", # 主机名
- "version" : "6.0.2", # mongodb server 版本
- "process" : "mongod", # mongodb进程,主要有mongod和mongos(分片集群中)两种
- "pid" : NumberLong(1034), # mongod的pid进程号,可以在linux终端下使用命令 pidof mongod 验证
- "uptime" : 105063, # mongodb服务启动的秒数,时间短则表示近期有重启,时间长则表示在稳定运行
- "uptimeMillis" : NumberLong(105063193), # mongod服务启动的毫秒数
- "uptimeEstimate" : NumberLong(105063), # mongod内部自己计算的启动秒数
- "localTime" : ISODate("2020-12-08T16:01:08.230Z"), # 本地时间,相当于 python的 datetime.now()
- # 客户端连接数相关
- "connections" : {
- "current" : 8, # 当前连接数的编号
- "available" : 51192, # 可用最大连接数
- "totalCreated" : 1, # 截止目前为止总共历史创建的连接数
- "active" : 1, # 还在活跃的连接数,查看并发连接数主要参考这个值
- },
- # 全局锁相关信息
- "globalLock" : {
- "totalTime" : NumberLong("105063115000"), # mongod启动后到现在的总时间,单位微秒
- "currentQueue" : { # 当前等待锁队列
- "total" : 0, # 当前全局锁的等待个数
- "readers" : 0, # 当前全局读锁等待个数
- "writers" : 0 # 当前全局写锁等待个数
- },
- "activeClients" : {
- "total" : 0, # 当前活跃客户端的个数
- "readers" : 0, # 当前活跃客户端中进行读操作的个数
- "writers" : 0 # 当前活跃客户端中进行写操作的个数
- }
- },
- "network" : { # 网络相关
- "bytesIn" : NumberLong(1611), # 数据库接收到的网络传输字节数
- "bytesOut" : NumberLong(51269), # 从数据库发送出去的网络传输字节数
- "numRequests" : NumberLong(16), # mongod接收到的总请求次数
- },
-
- # 操作计数器
- "opcounters" : {
- "insert" : NumberLong(0), # 本次mongod实例启动至今收到的插入操作总数
- "query" : NumberLong(287), # 本次mongod实例启动至今收到的查询总数。
- "update" : NumberLong(0), # 本次mongod实例启动至今收到的更新操作总数 。
- "delete" : NumberLong(0), # 本次mongod实例启动至今的删除操作总数。
- "getmore" : NumberLong(0), # 本次mongod实例启动至今“getmore”操作的总数。
- "command" : NumberLong(588)# 本次mongod实例启动至今向数据库发出的命令总数 。
- },
- # 存储引擎,是MongoDB的核心组件, 负责管理数据如何存储在硬盘(Disk)和内存(Memory)上的组件
- # MongoDB 支持多种不同的存储引擎(Storage Engine),MongoDB支持的存储引擎有:WiredTiger,MMAPv1和In-Memory。
- # 1. WiredTiger,将数据持久化存储在硬盘文件中;从MongoDB 3.2 版本开始,成为MongDB默认存储引擎
- # 2. In-Memory,将数据存储在内存中
- # 3. MMAPv1,将数据持久化存储在硬盘文件中; MongoDB 3.2 版本以前的默认存储引擎,类似mysql里面的 MyISAM
-
- # WiredTiger 是比MMAPv1更好用,更强大的存储引擎,WiredTiger的写操作会先写入缓存(Cache)中,并持久化到WAL(Write ahead log,写日志),每60s或日志文件达到2GB时会做一次Checkpoint(检查点),将当前数据进行持久化,产生一个新的快照。Wiredtiger连接初始化时,首先将数据恢复至最新的快照状态,然后根据WAL恢复数据,以保证存储可靠性。
- # Checkpoint,检测点。将内存中的数据变更冲刷到磁盘中的数据文件中,并做一个标记点。
- # 表示此前的数据表示已经持久存储在了数据文件中,此后的数据变更存在于内存(CPU缓存)和WAL日志中.
- # 是一种让数据库redo(重做)和data(数据)文件保持一致的机制。这种机制 ,并非Mongodb独有的,mysql中的InnoDB也有。
- "storageEngine" : {
- "name" : "wiredTiger", # 当前mongoDB系统默认的存储引擎是 wiredTiger
- "supportsCommittedReads" : true,
- "oldestRequiredTimestampForCrashRecovery" : Timestamp(0, 0),
- "supportsPendingDrops" : true,
- "dropPendingIdents" : NumberLong(0),
- "supportsTwoPhaseIndexBuild" : true,
- "supportsSnapshotReadConcern" : true,
- "readOnly" : false,
- "persistent" : true,
- "backupCursorOpen" : false
- },
-
- # 多文档事务,mongodb4.0以后新增特性
- "transactions" : {
- "retriedCommandsCount" : NumberLong(0),
- "retriedStatementsCount" : NumberLong(0),
- "transactionsCollectionWriteCount" : NumberLong(0),
- "currentActive" : NumberLong(0),
- "currentInactive" : NumberLong(0),
- "currentOpen" : NumberLong(0),
- "totalAborted" : NumberLong(0),
- "totalCommitted" : NumberLong(0),
- "totalStarted" : NumberLong(0),
- "totalPrepared" : NumberLong(0),
- "totalPreparedThenCommitted" : NumberLong(0),
- "totalPreparedThenAborted" : NumberLong(0),
- "currentPrepared" : NumberLong(0)
- },
- "locks":{ # 锁相关
-
- },
- "mem" : { # 内存相关
- "bits" : 64, # 操作系统位数
- "resident" : 18, # 物理内存消耗,单位:M
- "virtual" : 1566, # 虚拟内存消耗,单位:M
- "supported" : true # 是否显示额外的内存信息
- },
- }
复制代码 查察当前db的连接呆板地址
查察日记
- show logs
- // global
- // startupWarnings
- // 如果要查看具体文件的日志。
- show log global
- // global是全局日志,默认保存的日志文件在 /var/log/mongodb/mongod.log
- // 如果mongoDB无法启动,查看错误就可以来到默认日志文件查看
复制代码 数据备份与恢复
MongdoDB一共提供了4个下令行工具给我们对数据举行备份与恢复以及导入与导出数据。
备份与恢复操纵的数据文件格式是bson格式,二进制文件,不需要进入mongo终端
导入与导出数据的数据文件格式是json格式,文本文档格式,不需要进入mongo终端
预备测试数据
mongoDB终端下举行以下操纵:
- // 进入mongo交互终端
- mongosh
- use test // mongoDB中可以使用use切换数据库,针对不存在的数据库会自动创建,可通过db在终端查看当前所在数据库
- // 声明了一个函数,生成一个指定随机整数,不足10,前面补0
- formatnumber = (start, end)=>{
- num = Math.round(Math.random() * (end-start)) + start
- if(num<10){
- return "0"+num;
- }else{
- return num;
- }
- }
- // 声明了一个函数,生成具有随机标题
- rand_title=()=>{num = Math.round( Math.random() * 10 );num1 = Math.round( Math.random() * 10 );return [
- "赠送礼品-"+num,
- "购物狂欢-"+num,
- "随便买买-"+num,
- "愉快购物-"+num,
- "赠送礼物-"+num,
- "商品购买-"+num,
- "买多送多-"+num,
- "买年货-"+num,
- "买买买买-"+num,
- "充值会员-"+num][num1];}
- // 创建一个函数,循环生成指定的数据
- function rand_data(size=200000)
- {for(var i=0; i<size; i++)
- {db.orders.insertOne({"order_number": ( "0000000000000000" + i ).substr( String(i).length ),"date": "20"+formatnumber(0,21)+"-"+formatnumber(1,12)+"-"+formatnumber(1,31),"title": rand_title(i),"user_id": parseInt(i/200)+1,"items" :[{"goods_id" : parseInt(i/200)+1,"goods_number" : formatnumber(2, 10),"price" : formatnumber(50, 1000)},{ "goods_id" : parseInt(i/200)+2,"goods_number" :formatnumber(2, 10),"price" : formatnumber(50, 1000)}]});if(i%10000==0){print("已经添加了"+Math.ceil(i/10000)+"万条数据!");}}}
- // 调用上面生成测试数据的函数
- rand_data()
- // 查看上面生成的数据
- db.orders.find()
- // 每次显示的数据,mongoDB交互终端默认只会显示20条,所以如果要查看更多,则根据提示输入it可以查看下一页数据。
复制代码 数据备份
下令格式:
- mongodump -h dbhost -d dbname -o dbdirectory
复制代码 参数阐明:
选项作用备注-hMongoDB服务端地址和端口的简写–host=MongoDB地址 --port=端口-d备份的数据库名称–db=数据库名称-o备份数据保存目录地点路径备份数据保存目录需要提前创建 比方:备份上面的test数据库
- mongodump -h 127.0.0.1:27017 -d test -o /home/moluo/Desktop/
- # 删除demo数据库
- mongosh
- use test
- db.dropDatabase()
- # 再次查看,可以发现没有任何数据了
- db.orders.find()
复制代码 数据恢复
下令格式:
- mongorestore -h dbhost -d dbname --dir dbdirectory --drop
复制代码 参数阐明:
选项作用备注-hMongoDB服务端地址和端口的简写–host=MongoDB地址 --port=端口-d恢复的数据库名称–db=数据库名称–dir备份数据地点目录–drop恢复数据前,先删除MongoDB中的旧数据 比方:恢复上面备份的test数据库
- mongorestore -h 127.0.0.1:27017 -d test --dir /home/moluo/Desktop/test --drop
- # 进入数据库查看
- use test
- # 统计当前数据库下orders集合里面所有的文档总数
- db.orders.countDocuments()
复制代码 数据导出
下令格式:
- mongoexport -h dbhost -d dbname -c collectionname -o file --type json/csv -f field
复制代码 参数阐明:
选项作用备注-hMongoDB服务端地址和端口的简写–host=MongoDB地址 --port=端口-d要导出的数据库名称–db=数据库名称-c要导出的集合名称–collection=集合名称-o导出数据保存的文件名-f导出数据的文档字段列表假如不执行-f,则默认导出文档的全部字段–type导出数据的文件格式默认是json,也可以是csv,当数据格式为csv时,另需加上-f “字段1,字段2,…” 比方:导出上面的test数据库的orders集合的所有数据
- mongoexport -h 127.0.0.1:27017 -d test -c orders -o /home/moluo/Desktop/test_orders.json --type json
复制代码 数据导入
下令格式:
- mongoimport -h dbhost -d dbname -c collectionname --file filename --type json/csv --headerline -f field
复制代码 参数阐明:
选项作用备注-hMongoDB服务端地址和端口的简写–host=MongoDB地址 --port=端口-d要导入的数据库名称–db=数据库名称-c要导入的集合名称–collection=集合名称–file导入数据保存的文件名–type导入数据的文件格式默认是json,
也可以是csv,当数据格式为csv时:
1. 需加上-f “字段1,字段2,…”
2. 可以选择加上–headerline,把首行视为导入字段行,不认为是实际数据 比方:把上面导出的json文件中的数据,导入到demo数据库的orders集合中
- mongoimport -h 127.0.0.1:27017 -d test -c orders --file /home/moluo/Desktop/test_orders.json --type json
复制代码 用户管理
创建用户
- db.createUser(user, pwd, writeConcern)
复制代码 创建一个数据库新用户用db.createUser()方法,假如用户存在则返回一个用户重复错误。
错误信息:uncaught exception: Error: couldn't add user: User "用户名@数据库" already exists
具体语法:
- db.createUser({
- user: "<用户名>",
- pwd: "<密码>",
- customData: { <any information> }, // 任意内容,主要是为了表示用户身份的相关介绍
- roles: [ // 角色和权限分配,一个用户可以绑定多个角色,也就是具备多个角色的权限
- { role: "<角色名>", db: "<数据库名>" }, // 也可以直接填写由mongoDB内置的角色,例如: "<role>"
- ...
- ]
- })
复制代码 留意:
与之前学习的redis或MySQL不同,mongoDB的用户是以数据库为单位来分别建立和管理用户访问控制权限的,每个数据库有属于本身的一个或多个管理员。
管理员可以管理本身的数据库,但是不能直接受理其他数据库,要先在内置数据库admin认证后才可以。
管理员的权限设置包罗了2块,分别是角色和权限,由创建用户时通过roles属性举行设置。
内置角色
也叫内建角色,是MongoDB安装以后默认创建提供给我们使用的。
- 数据库用户角色:read、readWrite
- 数据库管理角色:dbAdmin、dbOwner、userAdmin
- 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager
- 备份恢复角色:backup、restore
- 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
- 超级用户角色:root
- // 超级用户角色 提供了系统超级用户的访问权限
- // 有几个角色间接或直接提供了系统超级用户的访问权限(dbAdmin、dbOwner 、userAdmin、userAdminAnyDatabase、dbAdminAnyDatabase)
复制代码 内置权限
- Read:允许用户读取指定数据库
- readWrite:允许用户读写指定数据库
- dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
- userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
- clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。
- readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限
- readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限
- userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
- dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。
- root:只在admin数据库中可用。超级账号,擁有超级权限,对所有数据具有超级权限。
复制代码 给Admin数据库创建账户管理员(相称于给mpongoDB创建了一个HR)
当前账号只能用于管理admin数据库账号,不能举行其他数据库的操纵。
- // 进入/切换数据库到admin中
- use admin
- // 创建账户管理员
- // [用户名:admin,密码:123456,数据库:admin,角色userAdminAnyDatabase,表示admin用户在admin数据库中具有超级用户权限]
- db.createUser({
- user: "admin",
- pwd: "123456",
- roles: [
- {role: "userAdminAnyDatabase",db:"admin"},
- ]
- });
- // 终端效果如下:
- Successfully added user: {
- "user" : "admin",
- "roles" : [
- {
- "role" : "userAdminAnyDatabase",
- "db" : "admin"
- }
- ]
- }
复制代码 创建超级管理员
当前账号可以举行数据库相关操纵。
- // 进入/切换数据库到admin中
- use admin
- // 创建超级管理员账号
- db.createUser({
- user: "root",
- pwd: "123456",
- roles: [
- {role:"root", db:"admin"}, // 也可以这样简写:"root",
- ]
- })
- // 终端效果如下:
- Successfully added user: {
- "user" : "root",
- "roles" : [
- {
- "role" : "root",
- "db" : "admin"
- }
- ]
- }
复制代码 创建用户本身的数据库的角色
帐号是跟着数据库绑定的,以是是什么数据库的用户,就必须先到指定库里授权和验证!!!
一般开辟中,每每一个项目就分配一个数据库,并给这个数据库分配一个管理员!!!
- // 切换数据库,如果当前库不存在则自动创建
- use yingmingapp;
- // 创建管理员用户,为了保证不会报错,所以先删除同名管理员 db.system.users.remove({user:"yingmingapp"});
- db.createUser({
- user: "yingmingapp",
- pwd: "123456",
- roles: [
- { role: "dbOwner", db: "yingmingapp"}
- ]
- })
- // 终端下的效果
- /*
- Successfully added user: {
- "user" : "yingmingapp",
- "roles" : [
- {
- "role" : "dbOwner",
- "db" : "yingmingapp"
- }
- ]
- }
- */
- // 查看当前仓库下的用户
- show users;
- /* 效果:
- {
- "_id" : "yingmingapp.yingmingapp",
- "userId" : UUID("126f2bd2-cddc-450a-9814-3e8937827ab3"),
- "user" : "yingmingapp",
- "db" : "yingmingapp",
- "roles" : [
- {
- "role" : "dbOwner",
- "db" : "yingmingapp"
- }
- ],
- "mechanisms" : [
- "SCRAM-SHA-1",
- "SCRAM-SHA-256"
- ]
- }
- */
- // 也可以在admin库中全局显示所有用户
- use admin
- db.system.users.find()
复制代码 用户信息
查察当前数据库下的管理用户
只需要切换到对应的库中即可查察
- use yingmingapp
- show users
复制代码 查察系统中所有的用户
需要切换到admin中使用账号管理员的权限举行操纵
- use admin
- // db.auth("root","123456") // 如果开启了mongoDB的访问控制,就需要下用户认证密码才能继续往下操作
- db.system.users.find() // 只能在admin数据库中使用。
- // db.system.users.find().pretty() // 在mongoDB5.0以前的版本中,pretty表示在返回多个查询结果时,以结构化格式显示数据
复制代码 删除用户
语法格式:
- db.system.users.remove(json条件)
复制代码 比方,删除上面的用户名为yingmingapp的用户。
- // 有多种删除方式,下面是根据user用户名删除用户
- use admin
- db.system.users.deleteOne({user:"yingmingapp"});
- // 删除效果:
- WriteResult({ "nRemoved" : 1 }) // nRemoved 大于0表示成功删除管理员,等于0则表示没有删除。
复制代码 修改密码
必须切换到对应的数据库下才能给用户修改密码。
以是针对账户管理员或者超级管理员,需要在admin下修改,而其他数据库管理员则必须到对应数据库下才能修改。
- db.changeUserPassword("账户名", "新密码");
复制代码 mongo终端操纵:
- use yingmingapp
- // 添加测试账号:
- db.createUser({
- user: "yingmingapp",
- pwd: "123456",
- roles: [
- { role: "dbOwner", db: "yingmingapp"}
- ]
- })
- // 注册必须保证有这个管理员
- db.changeUserPassword("yingmingapp", "123");
复制代码 开启账户认证
开启账户认证功能,必须要修改配置文件,然后重启mongoDB才能见效。
- sudo vim /etc/mongod.conf// 找到31行附近的 security,去掉左边表明符号(#)security: authorization: enabled:wq// 重启mongdb,配置见效sudo systemctl restart mongod// 开启了账户认证机制以后,再次进入yingmingappmongoshuse yingmingapp
- show users
- // 此处会报错如:uncaught exception: Error: command usersInfo requires authenticationdb.auth("yingmingapp","123") // 此处认证时填写精确密码:// { ok: 1 }exitmongoshuse yingmingappdb.auth("yingmingapp","123456") // 此处认证时填写错误密码:// MongoServerError: Authentication failed.// 留意:假如实现以某个库的账户管理员登录数据库以后,要切换账号操纵其他数据库,则必须先退出当前登录状态。
复制代码 数据库管理
显示所有数据库列表【空数据库不会显示,或者说空数据库已经被mongoDB采取了。】
切换数据库,假如数据库不存在则创建数据库。
查察当前工作的数据库
删除当前数据库,假如数据库不存在,也会返回{"ok":1}
- use <db> // 先切换到要删除的数据库中,然后才能删除数据库
- db.dropDatabase()
复制代码 查察当前数据库状态
- test> db.stats()
- {
- db: 'test', // 当前数据库名
- collections: 1, // 当前数据库中的数据集合数量,相当于mysql的数据表
- views: 0, // 当前数据库中的视图数量
- objects: 200000, // 当前数据库中的文档数据,相当于mysql中的数据记录
- avgObjSize: 235.415035, // 当前数据库中的文档平均大小
- dataSize: 47083007, // 当前数据库中的数据总文件大小
- storageSize: 10096640, // 存储引擎占据的文件大小
- indexes: 1, // 当前数据库中的索引数量
- indexSize: 2756608, // 当前数据库中的索引文件大小
- totalSize: 12853248, // 数据库中总数据总文件大小
- scaleFactor: 1,
- fsUsedSize: 41576349696, // 文件系统空间占用大小
- fsTotalSize: 51989970944, // 文件系统的总占用空间大小
- ok: 1
- }
复制代码 在mongoDB中,最重要的焦点是文档,假如一个库或者一个库下的集合中的文档全部被删除了,则这个库和这个集合就意味着不存在,会被mongoDB采取删除。
集合管理
mongoDB中的集合有2种:固定集和动态集。
创建集合
一般工作中使用的是动态集,但是在mongoDB优化时,可以对部门数据转换成使用固定集来保存,性能更好,查询速率更快。
在mongodb中假如使用的动态集,其实不需要专门创建集合,直接添加文档,mongodb也会自动生成动态集合的。
而固定集需要设置存储的文档上限数量,以是需要提前创建集合。
- // name为必填参数,options为可选参数。capped若设置值为true,则size必须也一并设置
- db.createCollection(
- <集合名称>, // 同一数据库中,集合名是唯一的,不能重复创建,否则报错!
- {
- capped : <boolean>, // 当前创建的集合是否是固定集,固定集指限制固定数据大小的集合,当数据达到最大值会自动覆盖最早添加的文档内容
- size : <bytes_size>, // 如果capped值为True,则表示创建固定集,需要指定固定集合存储的最大字节数,单位:字节数.
- max : <collection_size> // 如果capped值为True,则表示创建固定集,需要指定固定集合中包含文档的最大数量,单位:字节数
- }
- );
- // db.createCollection不填写第二个参数则表示创建的是动态集
- // 添加文档到不存在的集合中,mongodb会自动创建动态集合,
- // db.<集合名称>.insert({"name":"python入门","price" : 31.4})
- db.courses.insert({"name":"python入门","price" : 31.4})
复制代码 固定集的使用操纵
固定集一般用于日记,历史记录中。固定集,会由于设置了max或size上限而出现文档被驱逐/出列的环境。后面新增超出部门的数据仍旧被添加到集合中,但是最早添加的划一数据数据会被删除掉。
使用场景:
记录访问足迹,记录日记(防止日记记录太多)
- // 创建固定集记录用户的访问足迹,集合名:history
- > db.createCollection("history", {capped:true, size: 1000000, max: 5, });
- { "ok" : 1 }
- // 可以通过show tables 直接查看当前数据库中所有的集合列表
- > show tables;
- // history
- // orders
- // 添加数据到固定集
- test > db.history.insert({"name":"python入门","price" : 31.4})
- // WriteResult({ "nInserted" : 1 })
- test > db.history.insert({"name":"python入门","price" : 32.4})
- // WriteResult({ "nInserted" : 1 })
- test > db.history.insert({"name":"python入门","price" : 33.4})
- // WriteResult({ "nInserted" : 1 })
- test > db.history.insert({"name":"python入门","price" : 34.4})
- // WriteResult({ "nInserted" : 1 })
- test > db.history.insert({"name":"python入门","price" : 35.4})
- // WriteResult({ "nInserted" : 1 })
- test > db.history.insert({"name":"python入门","price" : 36.4})
- // WriteResult({ "nInserted" : 1 })
- // 上面一共添加了6条数据到固定集,当时因为创建固定时设置了只允许5条数据,
- // 所以固定集中针对旧的数据已经删除,只保留最新的5数据。
- test > db.history.find()
- // [
- // {_id: ObjectId("634563f011b6d817d0188edd"), name: 'python入门', price: 32.4 },
- // {_id: ObjectId("634563f211b6d817d0188ede"), name: 'python入门', price: 33.4 },
- // {_id: ObjectId("634563f511b6d817d0188edf"), name: 'python入门', price: 34.4 },
- // {_id: ObjectId("634563f711b6d817d0188ee0"), name: 'python入门', price: 35.4 },
- // {_id: ObjectId("634563f911b6d817d0188ee1"), name: 'python入门', price: 36.4 }
- // ]
复制代码 集合列表
- show collections // 或 show tables 或 db.getCollectionNames()
复制代码 删除集合
删除集合,那么集合中原有的文档数据也被删除掉。
查察集合
- // 获取集合的对象
- // db.getCollection("集合名称") 或者 db.集合名称
- test> db.getCollection("orders")
- test.orders
- test> db.orders
- test.orders
复制代码 查察集合状态信息
- // db.集合名称.stats()
- db.orders.stats()
- // 打印效果:
- {
- ns: 'test.orders', // 当前集合的命名空间(namespace)
- size: 47083007, // 当前集合的数据总字节数
- count: 200000, // 当前集合的文档数量
- avgObjSize: 235, // 当前集合的每个文档平均字节数
- capped: false, // 当前集合是否是固定集
- },
- nindexes: 1, // 当前集合的索引数量,默认如果没有特定设置,默认mongoDB会给文档添加一个_id作为主键。
- totalIndexSize: 2756608, // 当前集合的索引的总字节数
- totalSize: 12853248, // 当前集合的数据总字节数
- indexSizes: { _id_: 2756608 }, // 当前集合的索引的数值上限
- }
复制代码 文档管理
mongodb中,文档也叫 object/document。对应的就是存储的bson数据记录,对应的就是python中的字典或者列表。
mongodb中,允许文档中有各种的自定义字段,每一个字段对应的值也存在不同数据格式,根据不同的格式产生不同的数据范例。
数据范例
Type描述ObjectID用于存储文档的ID,相称于主键,区分文档的唯一字段,mongoDB中就是一个ObjectID对象的返回值。一共由3部门组成:4个字节的时间戳、5个字节的客户端历程生成的随机数、3个字节的增量计数器,一共12字节长度的十六进制数,以此保证了每一个文档的唯一性。String字符串是最常用的数据范例,MongoDB中的字符串必须是UTF-8编码。Integer整数范例用于存储数值。整数可以是32位,也可以是64位,这取决于你的服务器。Double双精度范例,用于存储浮点值,mongodb中没有float浮点数这个说法Boolean布尔范例用于存储布尔值(true/ false),留意:是小写的!!!Arrays将数组、列表或多个值存储到一个键,[]Timestamp时间戳,用于记录文档何时被修改或创建。Date(),Timestamp(),ISODate() ,默认是ISODate()Date用于以UNIX时间格式存储当前日期或时间。Object用于嵌入文档, 相称于子属性是另一个json文档而已,这种方式就可以实现嵌套。{}Null空值,相称于 python的NoneSymbol与字符串用法相同,常用于某些使用特殊符号的语言,可以理解为一种二进制格式字符串Binary data二进制数据,常用于保存文件的内容,每每是图片、音频、视频等数据本身。Code用于将JavaScript代码存储到文档中Regular expression正则表达式 固然,MongoDB中提供了数据范例,但是mongoDB中对于文档的添加,是不需要预先约束字段值范例的,而是一种自动推断范例。因此,上面的这些范例,我们知道即可。
添加文档
mongodb中,文档的数据结构和 JSON 基本一样。所有存储在集合中的数据在内部存储的格式都是 BSON 格式。
BSON 是一种雷同 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。
- // 添加文档
- // 方式1
- db.<集合名称>.insertOne( // 如果文档存在_id主键为更新数据,否则就添加数据。
- <document>
- )
- // 方式2:
- // 一次性添加多个文档, 多次给同一个集合建议使用insertMany比insertOne效率更好
- db.<集合名称>.insertMany([
- <document>,
- <document>,
- ...
- ])
复制代码 操纵:
- use test;
- // 添加一条数据[insert是过去版本的MongoDB提供的添加数据方法]
- db.users.insertOne({
- "name": "laoli", // string
- "age": 33, // integer
- "sex": true, // boolean
- "child": { // Object
- "name":"xiaohuihui", // string
- "age":6 // integer
- }
- });
- // WriteResult({ "nInserted" : 1 })
- // mongoDB原则上内置了js解释引擎,所以支持js语法
- > db.users.findOne()._id
- // ObjectId("61552b913ccd8ec29dbf6512")
- > db.users.findOne().name
- // laoli
- // javascrit总可以通过typeof 来查看数据的类型
- > typeof db.users.findOne().name
- // string
- > typeof db.users.findOne()._id
- // object
- > typeof db.users.findOne().sex
- // boolean
- > typeof db.users.findOne().age
- // number
- > typeof db.users.findOne().child
- // object
- > typeof db.users.findOne().child.name
- // string
- // 添加一条数据
- db.users.insertOne({"name":"xiaozhang","age":18,"sex":true, "money": 300.50});
- // {
- // "acknowledged" : true,
- // "insertedId" : ObjectId("605021e6d5c7a55cc95c1cb7")
- // }
- // 添加多条数据
- db.users.insertMany([
- {"name":"xiaolan","age":16},
- {"name":"xiaoguang","age":16}
- ]);
- // {
- // "acknowledged" : true,
- // "insertedIds" : [
- // ObjectId("60502235d5c7a55cc95c1cba"),
- // ObjectId("60502235d5c7a55cc95c1cbb")
- // ]
- // }
- db.users.find()
复制代码 删除文档
- // 方式1: 删除一条数据
- db.<集合名称>.deleteOne(
- <filter>, // removed的条件表达式,一般写法:{"字段":{查询器函数:值}},如果不填写删除条件,删除所有文档
- )
- // 方式2:删除多条数据
- db.<集合名称>.deleteMany(
- <filter>, // removed的条件表达式,一般写法:{"属性":{条件:值}},如果不填写条件,删除所有文档
- )
复制代码 操纵:
- // 添加多条测试数据
- document1 = {"name":"xiaohei","age":16}
- document2 = {"name":"xiaobai","age":15}
- document3 = {"name":"xiaolan","age":18}
- document4 = {"name":"xiaohui","age":11}
- document5 = {"name":"xiaoming","age":13}
- db.users.insertMany([document1,document2,document3,document4,document5]);
- // 删除满足条件的第一条数据
- // mongoDB中的条件格式: {字段名:{$运算符:值}
- // 条件:{"age":{$eq:16}} 相当于SQL语句的age=16
- db.users.find({"age":{$eq:16}})
- db.users.deleteOne({"age":{$eq:16}})
- db.users.deleteOne({"age":16}); // 等于可以省略不写,相当于 db.users.remove({"age":{$eq:16}});
- // 再次 添加多条测试数据
- db.users.insertOne({"name": "laozhang", "age": 33, "sex": true, child: {name: "xiaozhang", "age": 4}})
- db.users.insertOne({"name": "laowang", "age": 32, "sex": true, child: {name: "xiaowang", "age": 2}})
- db.users.insertOne({"name": "laoli", "age": 33, "sex": true, child: {name: "xiaoli", "age": 1}})
- db.users.insertOne({"name": "laosun", "age": 34, "sex": true, child: {name: "xiaosun", "age": 7}})
- db.users.insertOne({"name": "laozhao", "age": 32, "sex": true, child: {name: "xiaozhao", "age": 6}})
- db.users.insertOne({"name": "laoyang", "age": 35, "sex": true, child: {name: "xiaoyang", "age": 4}})
- db.users.insertOne({"name": "laohuang", "age": 36, "sex": true, child: {name: "xiaohuang", "age": 3}})
- // 删除一条
- db.users.deleteOne({"child.age": {$lte: 2}}) // 删除1条 孩子年龄小于或等于2岁的家长数据,
- // 删除多条
- db.users.deleteMany({"child.age": {$lt: 5}}) // 把所有孩子年龄小于5岁的家长数据全部删除
复制代码 mongoDB中的条件,不但用于删除数据的条件,也可以是查询或者更新的过滤条件。
$eq $lte $gte
查询文档
- // 直接显示查询的所有,find和findOne的第二个参数,也是一个json对象,一般称之为字段投影,表示设置是否显示或隐藏指定数据字段。
- // 获取一条
- db.集合.findOne(
- <query>, // 查询条件,删除、查询、修改都需要设置条件、条件写法基本一样的。
- { // 查询结果的字段投影,用于指定查询结果以后,显示的字段列
- <field>: 0, // 隐藏指定字段,例如:"_id":0,
- <field>: 1, // 显示指定字段,例如:"title":1,
- ....
- }
- )
- // 获取多条
- db.集合.find(
- <query>, // 查询条件
- {
- <key>: 0, // 隐藏指定字段,例如:"_id":0,
- <key>: 1, // 显示指定字段,例如:"title":1,
- ....
- }
- )
- // 以易读的方式来格式化显示读取到的数据,只能在find方法后面使用。mongoDB6.0版本以后。默认以易读的方式来格式化显示
- db.集合.find().pretty()
复制代码 操纵:
- // 切换数据库
- use yingmingapp;
- // 查询整个集合的所有数据
- db.users.find()
- db.users.find({}) // 没有设置任何条件
- // 查询一条数据
- db.users.findOne() // 获取集合中第一条数据
- db.users.findOne({}) // 同上
- // 设置字段投影,显示结果中字段列
- db.users.find({}, {"child": 0}) // 获取集合中所有数据,并隐藏child属性的数据
- db.users.find({}, {"_id": 0, "child": 0}) // 获取集合中所有数据,并隐藏_id 与 child属性的数据
- db.users.find({}, {"name": 1}) // 获取集合中所有数据,并只显示name属性与_id主键的字段数据[注意:_id如果不明确隐藏,默认显示的]
- db.users.find({}, {"name": 1, "_id": 0}) // 获取集合中所有数据,并只显示name属性的字段数据
复制代码 条件运算符
mongoDB中,条件运算符也叫查询器(query selector)
比力运算
操纵格式语法例子SQL中的雷同语句等于写法1:{<key>:<val>}
写法2:{<key>:{$eq:<val>}}db.集合.find({"name":"xiaoming"})where name = 'xiaoming'小于{<key>:{$lt:<val>}}db.集合.find({"age":{$lt:17}})where age < 17小于或等于{<key>:{$lte:<val>}}db.集合.find({"age":{$lte:17}})where age <= 17大于{<key>:{$gt:<val>}}db.集合.find({"age":{$gt:17}})where age > 17大于或等于{<key>:{$gte:<val>}}db.集合.find({"age":{$gte:17}})where age >= 17不等于{<key>:{$ne:<val>}}db.集合.find({"age":{$ne:17}})where age != 17包罗{<key>:{$in:[<val>...]}}db.集合.find({"age":{$in:[1,2,3]}})where age in (1,2,3)不包罗{<key>:{$nin:[<val>...]}}db.集合.find({"age":{$nin:[1,2,3]}})where age not in (1,2,3) 终端运行
- // 添加测试数据
- db.users.insertOne({"name": "laozhang", "age": 33, "sex": true, child: {name: "xiaozhang", "age": 4}})
- db.users.insertOne({"name": "laowang", "age": 32, "sex": true, child: {name: "xiaowang", "age": 2}})
- db.users.insertOne({"name": "laoli", "age": 33, "sex": true, child: {name: "xiaoli", "age": 1}})
- db.users.insertOne({"name": "laosun", "age": 34, "sex": true, child: {name: "xiaosun", "age": 7}})
- db.users.insertOne({"name": "laozhao", "age": 32, "sex": true, child: {name: "xiaozhao", "age": 6}})
- db.users.insertOne({"name": "laoyang", "age": 35, "sex": true, child: {name: "xiaoyang", "age": 4}})
- db.users.insertOne({"name": "laohuang", "age": 36, "sex": true, child: {name: "xiaohuang", "age": 3}})
- db.users.insertOne({"name": "zhangsanfeng"})
- // 查询年龄大于33岁的用户信息
- db.users.find({"age":{$gt:33}})
- // 查询年龄小于33岁的用户信息
- db.users.find({"age":{$lt:33}})
- // 查询年龄在32,33,34岁范围里面用户信息
- db.users.find({"age":{$in:[32,33,34]}})
- // 注意:没有当前字段的文档是不会被通过大于、小于或等于查询出来,因为默认条件不成立
- // 所以依靠不存在的字段,是无法使用比较运算符查询出上面添加zhangsanfeng的
- db.users.find({"age":{$lt:33}})
- db.users.find({"age":{$gte:33}})
- db.users.find({"age":{$in:[33]}})
- db.users.find({"age":{$nin:[33]}}) // 使用排除范围的方式,可以查询出来。
- db.users.find({"age":{$ne: 33}}) // 使用不等于的方式,可以查询出来。
复制代码 逻辑运算
操纵语法语法例子$and写法1:{<key>:<val>,<key>:<val>,...}
写法2:{$and: [{key:{$运算符:<val>}},....]}db.集合.find({key1:value1, key2:value2})$or{$or: [{<key>: {$运算符:<val>}}, ....]}db.集合.find({$or: [{key1: value1}, {key2:value2}]})$and和$or组合使用写法1:{<key>:<val>, $or: [{<key>: {<$运算符>:<val>}},...]}
写法2:{$and:[{$or:[{<key>:{<$运算符>:<val>}},..]},$or:[{<key>:{<$运算符>:<val>}},..]}]}db.集合.find({key1:value1, $or: [{key1: value1}, {key2:value2}]})$not{<key>:{$not:{<$运算符>:<val>}}}db.集合.find({key1:{KaTeX parse error: Expected '}', got 'EOF' at end of input: not:{运算符: val1}}})- SQL:
- (class=301 and sex =true) or (class=302 and sex=false)
- mongo:
- {
- $or: [
- {$and: [{class:301}, {sex:true}]},
- {$and: [{class:302}, {sex:false}]},
- ]
- }
复制代码 $and,终端操纵:
- // 查询age>34 并且 child.age<5
- db.users.find({
- $and:[
- {"age":{$gt:34}},
- {"child.age":{$lt: 5}}
- ]
- })
- // 简写;
- db.users.find({"age": {$gt:34}, "child.age": {$lt:5}})
- // 查詢age=35,child.age=4
- db.users.find({
- $and:[
- {"age":{$eq:35}},
- {"child.age":{$eq: 4}}
- ]
- })
- // 简写
- db.users.find({
- $and:[
- {"age": 35},
- {"child.age": 4}
- ]
- })
- // 再次简写:
- db.users.find({
- "age": 35,
- "child.age": 4,
- })
复制代码 o r 、 or、 or、and与$or的组合使用,终端操纵:
- // 查询age=33或者age=36
- db.users.find({
- $or: [
- {"age":{$eq:33}},
- {"age":{$eq:36}}
- ]
- })
- // 简写:
- db.users.find({
- $or:[
- {"age":33},
- {"age":36}
- ]
- })
- // 查询age=33,child.age==5 或者 age=35,child.age==4
- db.users.find({
- $or: [
- {$and: [{age: {$eq: 33}}, {"child.age": {$eq: 5}}]},
- {$and: [{age: {$eq: 35}}, {"child.age": {$eq: 4}}]},
- ]
- })
- // 简写:
- db.users.find({
- $or: [
- {$and: [{age: 33}, {"child.age": 5}]},
- {$and: [{age: 35}, {"child.age": 4}]},
- ]
- })
- // 再次简写:
- db.users.find({
- $or: [
- {age: 33, "child.age": 5},
- {age: 35, "child.age": 4},
- ]
- })
复制代码 $not,终端操纵:
- // 查询年龄!=16的
- db.users.find({"age":{$not:{$eq:33}}})
- // 简写:
- db.users.find({"age":{$ne: 33}})
复制代码 其他运算符
操纵格式语法例子阐明$type{<key>:{$type: <datetype>}}db.集合.find({"name":{$type:'string'}})匹配指定键是指定数据范例的文档
number 数值型
string 字符串
bool 布尔范例
object json文档对象范例
array 数组范例$exists{<key>:{$exists:<bool>}db.集合.find({"title":{$exists:true}})匹配具有指定键的文档,存在指定字段的文档$regex{ <key>:/模式/<修正符>}
{<key>:{$regex:/模式/<修正符>}}db.集合.find({"name":{$regex:/张$/}})按正则匹配$mod{<key>: {$mod: [除数, 余数]}}db.集合.find({"age":{$mod:[10,0]}})算数运算,取模,语法中举例是age除以10==0 终端操纵:
- db.users.insert({"name":"xiaoming","sex":0,"age":"18", "mobile": "13313621234"});
- db.users.insert({"name":"xiaoming","sex":1,"age":18, "mobile": "13441351234"});
- db.users.insert({"name":"xiaoming","sex":1,"age":33, "mobile": "13851351234"});
- db.users.insert({"name":"xiaoming","sex":0,"age":"33", "mobile": "13881351234", child: {"age": 3, "sex": true}});
- db.users.insert({"name":"xiaoming","sex":1,"age":33, "mobile": "10086"});
- // $type的使用
- db.users.find({"sex":{$type:"number"}});
- db.users.find({"sex":{$type:"bool"}});
- db.users.find({"age":{$type:"string"}});
- // $exists
- db.users.find({"child":{$exists:true}}); // 查询出存在child字段的数据
- // $regex 正则匹配
- db.users.find({"mobile":{$regex: /^133/ }});
- // 不符合手机号码格式的
- db.users.find({"mobile":{$not:{$regex: /1[3-9]\d{9}/ }}});
- // $mod 取模,注意:仅针对数值类型,对于字符串是不识别的,所以$mod[3,0],对于 "18"来说,并非整除!
- db.users.find({"age":{$mod: [3,0] }});
复制代码 自定义条件查询函数
慎用!服从差,由于需要额外调用javascript执行引擎才过滤,相对服从差。
- // 用法1,逻辑比较复杂的情况,可以使用更多的javascript进行运算处理:结果函数结果为true,则当前数据被查询出来。
- db.<集合名称>.find({$where: function(){ // this代表的就是查询过程中,被循环的每一条文档数据
- return <this.字段> <运算符> <条件值>;
- }}});
- // 用法2,相对没那么复杂的,取函数的返回值作为条件值:
- db.集合.find({$where: "<this.字段> <运算符> <条件值>"});
- // db.集合.find({$where:"this.name=='xiaoming'"});
复制代码 操纵:
- db.users.find({$where: function(){
- return this.age>33 && this.child.age<4;
- }});
- // 把字符串作为代码条件执行,当结果为true,则返回当前符合的数据
- db.users.find({$where: "this.age>33 && this.child.age<4"});
复制代码 排序显示
- db.集合.find().sort({<key>:1}) // 升序,默认为升序
- db.集合.find().sort({<key>:-1}) // 倒序,
复制代码 终端操纵:
- db.users.find().sort({age:-1});
- db.users.find().sort({age:-1, sex:1});
复制代码 字段投影
find()方法默认将返回文档的所有数据,但是可以通过设置find()的第二个参数projection,设置值查询部门数据。
语法:
- // 获取一条
- db.集合.findOne(
- <query>, // 查询条件
- {
- <key>: 0, // 隐藏指定字段,例如:"_id":0,
- <key>: 1, // 显示指定字段,例如:"title":1,
- ....
- }
- )
- // 获取多条
- db.集合.find(
- <query>, // 查询条件
- {
- <key>: 0, // 隐藏指定字段,例如:"_id":0,
- <key>: 1, // 显示指定字段,例如:"title":1,
- ....
- }
- )
复制代码 操纵:
- > db.users.find({"mobile":{$regex:/^133\d{8}$/}},{"_id":0}).sort({"mobile":-1})
- { "name" : "xiaoming", "mobile" : "13333355678" }
- { "name" : "xiaoming", "mobile" : "13333345678" }
- { "name" : "xiaoming", "mobile" : "13312345678" }
- > db.users.find({"mobile":{$regex:/^133\d{8}$/}},{"_id":0,"name":0}).sort({"mobile":-1})
- { "mobile" : "13333355678" }
- { "mobile" : "13333345678" }
- { "mobile" : "13312345678" }
- > db.users.find({"mobile":{$regex:/^133\d{8}$/}},{"name":1}).sort({"mobile":-1})
- { "_id" : ObjectId("60502fb7d5c7a55cc95c1cc4"), "name" : "xiaoming" }
- { "_id" : ObjectId("60502fb4d5c7a55cc95c1cc3"), "name" : "xiaoming" }
- { "_id" : ObjectId("60502fb1d5c7a55cc95c1cc2"), "name" : "xiaoming" }
- > db.users.find({"mobile":{$regex:/^133\d{8}$/}},{"name":1,"_id":0}).sort({"mobile":-1})
- { "name" : "xiaoming" }
- { "name" : "xiaoming" }
- { "name" : "xiaoming" }
复制代码 限定与偏移
limit方法用于限定返回效果的数量
skip方法用于设置返回效果的开始位置
- db.集合.find(...).limit(结果数量).skip(开始下标)
复制代码 终端操纵:
- db.users.find({},{"_id":0,"name":1,"age":1}).sort({"age":1}).limit(5);
- db.users.find({},{"_id":0,"name":1,"age":1}).sort({"age":1}).limit(5).skip(0);
- db.users.find({},{"_id":0,"name":1,"age":1}).sort({"age":1}).limit(5).skip(5);
复制代码
更新文档
- // 更新数据
- db.集合.update(
- <query>,
- <update>,
- {
- upsert: <boolean>, // 可选参数,如果文档不存在,是否插入objNew, true为插入,默认是false,不插入
- multi: <boolean>, // 可选参数,是否把满足条件的所有数据全部更新,设置更新1条还是多条
- writeConcern: <document> // 可选参数,抛出异常的级别。
- }
- )
- // 更新一条
- db.集合.updateOne(
- <query>, // update的查询条件,一般写法:{"属性":{条件:值}}
- <update>, // update的更新数据,一般写法 { $set:{"属性":"值",....} } 或者 { $inc:{"属性":"值"} }
- {
- upsert: <boolean>, // 可选参数,如果文档不存在,是否插入objNew, true为插入,默认是false,不插入
- multi: <boolean>, // 可选参数,是否把满足条件的所有数据全部更新,设置更新1条还是多条
- writeConcern: <document> // 可选参数,抛出异常的级别。
- }
- )
- // 更 新多条
- db.集合.updateMany(
- <query>, // update的查询条件,一般写法:{"属性":{条件:值}}
- <update>, // update的对象,一般写法 { $set:{"属性":"值"} } 或者 { $inc:{"属性":"值"} }
- {
- upsert: <boolean>, // 可选参数,如果文档不存在,是否插入objNew, true为插入,默认是false,不插入
- multi: <boolean>, // 可选参数,是否把满足条件的所有数据全部更新
- writeConcern: <document> // 可选参数,抛出异常的级别。
- }
- )
复制代码 update更新运算符[修改器]
操纵语法$incdb.集合.update({<key1>:<val1>},{$inc:{<key2>:<val2>}})更新key1=val1的文档中key2的值为val2,雷同python的递增递减
递减,则{ $inc:{<key2>:-<val2>} }$setdb.集合.update({<key1>:<val>}, {$set:{<key2>:<val2>}})更新key1=val1的文档中key2的值为val2,假如key2不存在则新增对应键值对$unsetdb.集合.update({<key1>:<val>}, {$unset:{<key2>:<val2>}})移除key1=val1的文档中key2=val2这个键值对$pushdb.集合.update({<key1>:<val>}, {$push:{<key2>:<val2>}})给key1=val1的文档中key2列表增长1个数组成员val2。
key2必须是数组。$pulldb.集合.update({<key1>:<val>}, {$pull:{<key2>:<val2>}})与push相反,给key1=val1的文档中key2列表删除1个指定成员val2$pullAlldb.集合.update({:}, {$pullAll:{:[,]}})与$pull作用一样,用于删除多个指定成员$popdb.集合.update({<key1>:<val>}, {$pop:{<key2>:<val2>}})给key1=val1的文档中key2列表移除第一个或最后一个成员。
val2只能是1(最后面)或-1(最前面),与python相反 终端操纵:
- // $inc
- // 把laoli的年龄+10岁
- db.users.update({"name":"laoli"},{$inc:{"age":10}}); // 更新一条
- db.users.updateMany({"name":"xiaoming"},{$inc:{"age":10}}); // 更新多条
- // 把laoli的孩子年龄+10岁
- db.users.update({"name":"laoli"},{$inc:{"child.age":10}});
- // $set
- // 如果字段不存在,则新增字段的键值对,如果字段存在,则修改字段的值
- //更新laoli的手机号码
- db.users.update({"name":"laoli"},{$set:{"mobile":"18012312312"}}); // 更新一条
- // 更新laoli孩子的手机号码
- db.users.update({"name":"laoli"},{$set:{"child.mobile":"18012312312"}});
- // $unset
- // 移除laoli的性别键值对
- db.users.update({"name":"laoli"},{$unset:{"sex":true}});
- // $push
- db.users.update({"name":"laoli"},{$set:{"lve":["TV","game"]}});
- db.users.update({"name":"laoli"},{$push:{"lve":"code"}}); // 往列表属性中追加成员
- // $addToSet 结合 $each 把一个数组中每一个成员添加到数组中
- db.users.update({"name":"laoli"},{$addToSet:{"lve":{$each:["code","music","TV"]}}});
- // $pull
- db.users.update({"name":"laoli"},{$pull:{"lve":"TV"}});
- // $pullAll
- db.users.update({"name":"laoli"},{$pullAll:{"lve":["TV","game"]}});
- // $pop
- db.users.update({"name":"laoli"},{$pop:{"lve":-1}}); // 左边移除列表的第一个成员
- db.users.update({"name":"laoli"},{$pop:{"lve":1}}); // 右边移除列表的最后一个成员
- // $rename 字段名重命名
- db.users.update({"name":"laoli"},{$rename:{"lve":"love"}});
复制代码
索引操纵
前面学习过MySQL,我们知道数据库里给数据构建索引通常能够极大的提高数据查询的服从,收缩查询耗时,假如没有索引,数据库在查询数据时一定会扫描数据表中的每个记录并提取那些符合查询条件的记录。同理,在MongoDB中构建索引也可以提高数据的查询服从和收缩查询耗时,没有索引的环境也是一样,MongoDB也会再查询数据时扫描集合中的每个文档并提取符合查询条件的文档。这种扫描全集合的查询服从是无疑是非常低下的,特别在处理大量的集合数据时,查询时间大概会达到几十秒甚至几分钟,这对用户体验来说是非常致命的。
文档:
预备数据
- use demo
- fotmatnumber = (start, end)=>{
- num = Math.round(Math.random() * (end-start)) + start
- if(num<10){
- return "0"+num;
- }else{
- return num;
- }
- }
- rand_title = (i)=>{
- num = Math.round( Math.random() * 10 );
- num1 = Math.round( Math.random() * 10 );
- return [
- "赠送礼品-"+num,
- "购物狂欢-"+num,
- "随便买买-"+num,
- "愉快购物-"+num,
- "赠送礼物-"+num,
- "商品购买-"+num,
- "买多送多-"+num,
- "买年货-"+num,
- "买买买买-"+num,
- "充值会员-"+num
- ][num1];
- }
- for(var i=0; i<200000; i++){
- db.orders.insert({
- "onumber": ( "0000000000000000" + i ).substr( String(i).length ),
- "date": "20"+fotmatnumber(0,21)+"-"+fotmatnumber(1,12)+"-"+fotmatnumber(1,31),
- "title": rand_title(i),
- "user_id": parseInt(i/200)+1,
- "items" :[{
- "goods_id" : parseInt(i/200)+1,
- "goods_attr" : i,
- "price" : 100.0
- },{
- "goods_id" : parseInt(i/200)+2,
- "goods_attr" : i+1,
- "price" : 80.0
- }]
- })
- if(i%10000==0){
- print("已经添加了"+parseInt(i/10000)+"万条数据!");
- }
- }
复制代码 留意事项
- MongoDB的索引是存储在运行内存(RAM)中的,以是必须确保索引的大小不凌驾内存的限定。
假如索引的大小凌驾了运行内存的限定,MongoDB会删除一些索引(涉及到mongodb的驱逐机制,这将导致性能降落)
- MongoDB的索引在部门查询条件下是不会见效的。
- 正则表达式及非操纵符,如 $nin,$not , 等。
- 算术运算符,如 $mod, 等。
- $where自定义查询函数。
- …
- 索引会在写入数据(添加、更新和删除)时重排,假如项目假如是写多读少,则建议少使用或者不要使用索引。
- 一个集合中索引数量不能凌驾64个。
- 索引名的长度不能凌驾128个字符。
- 一个复合索引最多可以有31个字段。
- mongodb索引同一在system.indexes集合中管理。这个集合只能通过createIndex和dropIndexes来操纵。
查察索引
- // 获取当前集合中已经创建的所有索引信息
- db.集合.getIndexes()
- /*
- [{
- "v" : 2, // 索引版本
- "key" : { // 索引的字段及排序方向(1表示升序,-1表示降序)
- "_id" : 1 // 根据_id字段升序索引
- },
- "name" : "_id" // 索引的名称
- }]
- */
- // 获取当前集合中已经创建的索引总大小,以字节为单位返回结果
- db.集合.totalIndexSize()
- // 获取当前数据库中所有的索引【不会显示默认主键_id】
- db.system.indexes.find()
复制代码 MongoDB会为插入的文档默认生成_id字段(假如文档本身没有指定该字段),_id是文档唯一的主键,为了保证能根据文档id快速查询文档,MongoDB默认会为集合创建_id字段的主键索引。
查询分析
与SQL语句雷同,MongoDB也提供了一个explain,供开辟者举行查询分析,优化查询语句。
explain的使用有3个参数,分别是:queryPlanner、executionStats、allPlansExecution,默认是queryPlanner,开辟中常用的是executionStat s。
db.。。。.。。。.explain(“executionStats”);
- db.orders.find({"title":"愉快购物-6"}).explain("executionStats");
- /*
- {
- "queryPlanner" : { # 被查询优化器选择出来的查询计划
- "plannerVersion" : 1, # 查询计划版本
- "namespace" : "test.orders", # 要查询的集合
- "indexFilterSet" : false, # 是否了使用索引
- "parsedQuery" : { # 查询条件
- "title" : {
- "$eq" : "购买商品-19"
- }
- },
- "winningPlan" : { # 最佳执行计划
- "stage" : "COLLSCAN", # 扫描类型/扫描阶段
- "filter" : { # 过滤条件
- "title" : {
- "$eq" : "购买商品-19"
- }
- },
- "direction" : "forward" # 查询方向,forward为升序,backward表示倒序。
- },
- "rejectedPlans" : [ ] # 拒绝的执行计划
- },
- "executionStats" : { # 最佳执行计划的一些统计信息
- "executionSuccess" : true, # 是否执行成功
- "nReturned" : 1, # 返回的结果数
- "executionTimeMillis" : 346, # 执行耗时
- "totalKeysExamined" : 0, # 索引扫描次数
- "totalDocsExamined" : 1000000, # 文档扫描次数,所谓的优化无非是让totalDocsExamined和nReturned的值接近。
- "executionStages" : { # 执行状态
- "stage" : "COLLSCAN", # 扫描方式/扫描阶段
- "filter" : {
- "title" : {
- "$eq" : "购买商品-19"
- }
- },
- "nReturned" : 1, # 返回的结果数
- "executionTimeMillisEstimate" : 5, # 预估耗时
- "works" : 1000002, # 工作单元数
- "advanced" : 1, # 优先返回的结果数
- "needTime" : 1000000,
- "needYield" : 0,
- "saveState" : 1000,
- "restoreState" : 1000,
- "isEOF" : 1,
- "direction" : "forward",
- "docsExamined" : 1000000 # 文档检查数目,与totalDocsExamined一致
- }
- },
- "serverInfo" : { # 服务器信息
- "host" : "ubuntu",
- "port" : 27017,
- "version" : "4.4.2",
- "gitVersion" : "15e73dc5738d2278b688f8929aee605fe4279b0e"
- },
- "ok" : 1
- }
- */
复制代码 stage的扫描范例:
范例名称描述期望COLLSCAN全表扫描FalseIXSCAN索引扫描TrueFETCH根据索引去检索指定documentTrueIDHACK针对_id举行查询TrueCOUNTSCANcount不使用Index举行count时返回FalseCOUNT_SCANcount使用了Index举行count时返回TrueSUBPLA未使用到索引的$or查询时返回FalseTEXT使用全文索引举行查询时返回-SORT使用sort排序但是无index时返回FalseSKIP使用skip跳过但是无index时返回FalsePROJECTION使用limit限定效果但是无index时返回False 创建索引
MongoDB支持多种范例的索引,包括普通索引(单列索引)、复合索引、多列索引、全文索引、哈希索引、地理位置索引等,每种范例的索引有不同的使用场合。
ttl索引本质上就是普通索引,只是给索引添加一个过期时间而已。
另外,MongoDB的全文索引很弱智,假如真要用在开辟中,还是建议使用elasticsearch或者Sphinx。
复合索引留意点
- 创建的复合索引对单字段条件的查找没有帮助
- 靠左匹配原则:
假如创建复合索引时,顺序为,name,number,address
- db.集合.createIndex({
- "name": 1,
- "number": 1,
- "address": 1
- }
复制代码 那么在查询时:
- 全字段匹配(name,number,address三者都在查询时使用),走索引
- 单独name查询,也走索引
- name,number或者name,address都走索引
- number,addresshu或者单独number或者单独address:都不走索引

- // 创建索引
- db.集合.createIndex({
- // 单个字段,则为普通索引, // sort的值表示排序,值为1表示升序索引,-1表示降序索引
- "字段名1": <sort|type>, // type的值可以是text,表示创建全文索引。db.集合.find({$text:{$search:"字符串"}})
- "字段名2": <sort|type>, // 多个字段,则为复合索引
- "字段名3": [<值1>,<值2>,...], // 多列索引
- ....
- }, {
- background: <Boolean>, // 建索引过程会阻塞数据库的其它操作,background可指定以后台方式创建索引,默认为false
- unique: <Boolean>, // 是否建立唯一索引,默认值为false,也叫唯一索引
- name: <String>, // 索引的名称,不填写,则MongoDB会通过连接索引的字段名和排序顺序生成一个索引名称
- expireAfterSeconds: <integer>, // 设置索引的过期时间,类似redis的expire,也叫TTL索引
- sparse: <Boolean>, // 对文档中不存在的字段数据是否不启用索引,默认为False
- });
- // 单字段索引[普通索引]
- db.集合.createIndex({
- "字段名": <sort>, // sort的值表示排序,值为1表示升序索引,-1表示降序索引
- }, {
- ....
- })
- // 普通索引创建: db.orders.createIndex({"title":1})
- // 查询基本使用: db.orders.find({"title":"愉快购物-6"}).explain("executionStats");
- // 多字段索引,也叫复合索引。[类似mysql里面的联合索引]
- db.集合.createIndex({
- "字段名1": <sort>, // sort的值表示排序,值为1表示升序索引,-1表示降序索引
- "字段名2": <sort>, // sort的值表示排序,值为1表示升序索引,-1表示降序索引
- }, {
- ....
- })
- // 复合索引的使用对单字段条件的查找是没有帮助的,必须多字段[必须包含复合索引的字段]条件使用
- // 复合索引创建:db.orders.createIndex({"date":1,"title":1});
- // 查询基本使用:
- // db.orders.find({"date":"2014-06-12","title":"买年货-7"}).explain("executionStats");
- // db.orders.find({"date":"2014-06-12","onumber":"0000000000030014","title":"买年货-7"});
- // 全文索引
- db.集合.createIndex({
- "字段名1": "text", // type的值只能是text,表示创建全文索引。db.集合.find({$text:{$search:"字符串"}})
- }, {
- ....
- })
- // 全文索引创建: db.orders.createIndex({"title":"text"})
- // 查询基本使用: db.orders.find({$text:{$search:"商品-19"}}).explain("executionStats")
- // 多列索引[应用的地方是在列表属性]
- db.集合.createIndex({
- "字段名3": [<值1>,<值2>,...],
- }, {
- ....
- });
- // 创建测试数据
- db.doc.drop()
- db.doc.insert({"title":"标题1","tags":["python","django"]})
- db.doc.insert({"title":"标题1","tags":["python","django"]})
- db.doc.insert({"title":"标题1","tags":["python","django"]})
- db.doc.insert({"title":"标题2","tags":["java","mvp"]})
- db.doc.insert({"title":"标题3","tags":["java","mvp"]})
- db.doc.insert({"title":"标题2","tags":["java","mvp"]})
- db.doc.insert({"title":"标题3","tags":["python"]})
- db.doc.insert({"title":"标题4","tags":["python"]})
- db.doc.insert({"title":"标题2","tags":["python","flask"]})
- db.doc.insert({"title":"标题3","tags":["java"]})
- // 创建多列索引: db.doc.createIndex({"tags":1})
- // 查询数据: db.doc.find({"tags":["python"]}).explain("executionStats")
- // 唯一索引
- db.集合.createIndex({
- "字段名1": <sort>,
- }, {
- unique: true, // 是否建立唯一索引,默认值为false,也叫唯一索引
- })
- // 创建唯一索引: db.orders.createIndex({"onumber":1},{unique:true});
- // 查询数据: db.orders.find({"onumber":"0000000000001019"}).explain("executionStats")
- // ttl索引
- // 使用ttl索引,索引关键字段的值类型必须是Date类型,如果该字段不是date类型或者文档中不存在该字段,则文档不会进行过期处理
- // 数据过期的删除工作是在mongoDB中的独立线程内执行的,默认平均60s扫描一次,不会立即删除。
- // 例如:在文档创建10秒后删除文档
- db.orders.dropIndex("date_1")
- db.orders.createIndex({"date": 1},{expireAfterSeconds: 10});
- db.orders.insertOne({
- "date": new Date("2022-01-10 17:30:00"), // 在python中需要通过 utctime
- "user_id": 2,
- "username": "xiaohong"
- })
- // 在文档创建后,由索引字段值指定的时间删除文档
- // 创建索引:db.tasks.createIndex({"expire_time":1},{expireAfterSeconds:0})
- // 创建测试数据
- db.tasks.insert( {
- "expire_time": new Date('2025-01-10 17:32:05'), // 在python中需要通过 utctime
- "user_id": 2,
- "username": "xiaoming",
- "onumber": "200003"
- });
- db.tasks.insert( {
- "expire_time": new Date('2025-01-10 17:34:05'), // 在python中需要通过 utctime
- "user_id": 2,
- "username": "xiaoming"
- "onumber": "200004"
- });
- db.tasks.insert( {
- "expire_time": new Date('2025-01-10 17:35:10'), // 在python中需要通过 utctime
- "user_id": 2,
- "username": "xiaoming"
- "onumber": "200005"
- });
- db.tasks.insert( {
- "expire_time": new Date('2024-10-7 17:03:30'),
- "user_id": 20,
- "username": "xiaoduan",
- "onumber": "200006"
- });
- // 重建索引[一般是在长期项目运行下来,索引创建时间太久了,性能下降的时候使用。]
- // !!!!不能在高峰期时运行以下操作
- db.集合.reIndex();
复制代码 删除索引
MongoDB给文档主键_id默认创建单字段索引是无法删除的。
- // 删除单个索引
- db.集合.dropIndex("索引名称")
- // db.orders.dropIndex("date_1")
- // 删除所有索引,慎用
- db.集合.dropIndexes()
复制代码 若有错误与不敷请指出,关注DPT一起进步吧!!!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |