MongoDB是一种高性能、支持海量存储的NoSQL数据库
MongoDB的数据以类似于JSON格式的二进制文档存储
文档型的数据存储方式有几个重要好处:
MongoDB的适用场景:
MongoDB的具体应用
有一个符合就可以选择MongoDB,两个以上符合,选MongoDB绝对不后悔。
RDBMS(关系型DB):table(表),row(行),column(列),index(唯一索引、主键索引),join(主外键关联),primary key(指定1到N个列做主键)
MongoDB:collection(集合),document(文档),field(字段),index(支持地理位置索引、全文索引、哈希索引),embedded Document(嵌套文档),primary key(指定_id field做主键)
BSON是一种类似于JSON的二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型。BSON有三个特点:轻量性、可遍历性、高效性。
MondoDB中Document可以出现的数据类型有:String、Integer、Boolean、Double、ObjectId(对象id,用于创建文档的id)、Array、Timestamp(时间戳)、object(内嵌文档)、null、Date或者ISOdate、Code。
进入到/etc/yum.repos.d目录下,创建文件mongodb-org-5.0.repo并输入如下yum源地址:
[mongodb-org]
name=MongoDB Repository
baseurl=http://mirrors.aliyun.com/mongodb/yum/redhat/7Server/mongodb-org/5.0/x86_64/
gpgcheck=0
enabled=1
安装之前先更新yum:yum -y update
下载安装MongoDB:yum -y install mongodb-org
查看mongo的安装位置:whereis mongod
修改mongod.conf配置文件,修改如下:
bindIp: 0.0.0.0
即表示所有ip地址都可以进行连接
mongodb服务命令
systemctl start mongod.servicesystemctl stop mongod.servicesystemctl restart mongod.servicesystemctl status mongod.servicesystemctl enable mongod.service启动mongo服务:systemctl start mongod.service
进入到/usr/bin目录下,启动mongod 客户端:./mongo
查看mongodb默认的数据库:show dbs
yum -y install dockersystemctl start dockerhttps://hub.docker.com/,搜索mongodb:
docker pull mongo:5.0.11-focaldocker imagesmkdir -p /mnt/mongodb/datasystemctl stop mongod.servicedocker run -itd --privileged=true --name mongo5 -p 27017:27017 -v /mnt/mongodb/data:/data/db mongo:5.0.11-focaldocker ps -ahttps://www.mongobooster.com/downloads
与SQL数据库不同,在SQL数据库中,在插入数据之前必须确定和声明表的架构,默认情况下,MongoDB的集合不要求其文档具有相同的架构。
结构灵活
单个集合中的文档不需要具有相同的字段集合,并且集合中的文档之间的字段数据类型可以不同。
相当于RDB表的行数据中,每行的字段名都可以不相同;然后字段数据类型也都可以不相同。
可灵活更改集合中文档的结构,如添加新字段、删除现有字段或将字段值更改为新类型,将文档更新为新结构。
文档结构
选择数据模型
意向锁(Intent Lock),简单来说就是给更大一级别的空间示意里面是否已经上过锁。如果我们给某一行数据加上了排它锁,数据库会自动给更大一级的空间,比如数据页或数据表加上意向锁,告诉其他人这个数据页或数据表已经有人上过排它锁了,这样当其他人想要获取数据表排它锁的时候,只需要了解是否有人已经获取了这个数据表的意向排他锁即可。
意向锁,即通知其他线程该数据的上一级已经加过排它锁。
排它锁,即只有一个线程能获取该资源。
乐观锁(Optimistic Locking)认为对同一数据的并发操作不会总发生,属于小概率事件,不用每次都对数据上锁,也就是不采用数据库自身的锁机制,而是通过程序来实现。在程序上,我们可以采用版本号机制或者时间戳机制实现。
Snapshots and Checkpoints(快照和检查点)
WiredTiger使用多版本并发控制(MVCC)。在操作开始时,WiredTiger会向操作提供数据的point-in-time快照,快照显示了数据在内存中的一致性视图。
从3.6版本开始,MongoDB将WiredTiger配置为每隔60s创建检查点(将快照数据写入磁盘)。在早期版本中,MongoDB将检查点设置为在WiredTiger中每隔60s或写入2GB日志数据时(以先发生的为准)对用户数据进行检查。
journal(日志)
WiredTiger将日志和检查点结合使用,以确保数据的持久性。WiredTiger日志将保留检查点之间的所有数据修改。如果MongoDB在两个检查点之间退出,它将使用日志重播自上一个检查点以来修改的所有数据。
compression(压缩)
使用WiredTiger存储引擎,MongoDB会对所有集合和索引进行压缩,压缩以牺牲额外的cpu作为代价,最大限度的减少了磁盘的使用。默认情况下,WiredTiger对所有集合使用block compression(块压缩),并对索引使用prefix compression(前缀压缩)。
内存使用
对于WiredTiger,MongoDB利用WiredTiger内部缓存和文件系统缓存。从MongoDB 3.4开始,默认WiredTiger内存缓存大小为以下两者中的较大值:
50% of(RAM(主机物理内存)- 1GB)
256MB
从MongoDB企业版3.2.6开始,In-Memory存储引擎是64位版本中广泛使用(general availability GA)的一部分。除某些元数据和诊断数据外,In-Memory存储引擎不维护任何磁盘上的数据,包括配置数据,索引,用户凭据等。
In-Memory存储引擎设置
配置–storageEngine选项值为inMemory;如果使用配置文件,则配置storage.engine
配置–dbpath,如果使用配置文件则配置storage.dbPath。尽管In-Memory存储引擎不会将数据写入文件系统,但它会在–dbpath中维护小型元数据文件和诊断数据以及用于构建大型索引的临时文件。
并发(concurrency)
In-Memory存储引擎对于写入操作使用了document级别的并发控制。多个客户端可以同时修改集合的不同文档。即同时对同一个文档进行修改时时,才会进行阻塞。
内存使用
默认情况下,In-Memory存储引擎使用50%的物理RAM减去1GB。如果写操作的数据超过了指定的内存大小,则MongoDB返回内存溢出错误。
要指定新的内存大小,可使用YAML格式配置文件storage.inMemeory.engineConfig.inMemorySizeGB: < newSize>
事务
从MongoDB 4.2开始,在复制集和分片集群上支持事务,也就是支持集群事务,其中:主成员节点WiredTiger存储引擎,同时,辅助成员使用WiredTiger存储引擎或In-Memory存储引擎。
在MongoDB 4.0中,只有使用WiredTiger存储引擎的复制集才支持事务。
docker exec -it mongo5 bashmongoshow dbsuse databasedb.createCollection("集合名")show tables, show collectionsdb.集合名.drop()db.dropDatabase()db.collection.insertOne()db.inventory.insertOne({ item: "canvas", qty: 100, tags:
["cotton"],size: { h: 28, w: 35.5,uom: "cm" } })
db.collection.insertMany()db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank","red"], size: { h: 14, w: 21, uom: "cm" } },
{ item: "mat", qty: 85, tags: ["gray"],size: { h: 27.9, w: 35.5, uom: "cm" } },
{ item: "mousepad", qty: 25, tags: ["gel","blue"], size: { h: 19, w: 22.85, uom: "cm"}}
])
语法:db.collection.find()
db.inventory.find({})db.inventory.find({size:{h: 14, w: 21, uom: "cm"}})db.inventory.find({"size.h": 14})db.inventory.find({"size.h": {$lt:15}})db.inventory.updateOne()db.inventory.updateOne(
{ item: "journal" },
{$set: { "size.uom": "dm", qty: 30 },
$currentDate: { lastModified: true }}
)
db.inventory.updateMany()db.inventory.updateMany(
{ "qty":{$lt: 50} },
{$set: { "size.uom": "in" },
$currentDate: { lastModified: true }}
)
db.collection.replaceOne()db.inventory.replaceOne(
{ item: "journal" },
{ item: "journal", instock:[ { warehouse: "A", qty: 60 }, { warehouse:"B", qty: 40 } ] }
)
即第一个{}是条件,第二个{}是更新的内容。
db.inventory.deleteMany({})db.inventory.deleteMany({"qty":{$lt: 70}})db.inventory.deleteOne({"qty":{$lt: 101}})通过聚合操作可以处理多个文档,并返回计算后的结果。
聚合管道,即分别由多个阶段来处理文档,每个阶段的输出是下个阶段的输入,返回的是一组文档的处理结果,例如,total、average、maxmium、minmium。
插入用来聚合操作的数据:
db.orders.insertMany( [
{ _id: 0, name: "Pepperoni", size: "small",
price: 19, quantity: 10,date: ISODate( "2030-03-13T08:14:30Z" ) },
{ _id: 1, name: "Pepperoni", size: "medium",
price: 20,quantity: 20, date : ISODate( "2030-03-13T09:13:24Z" ) },
{ _id: 2, name: "Pepperoni", size: "large",
price: 21,quantity: 30, date : ISODate( "2030-03-17T09:22:12Z" ) },
{ _id: 3, name: "Cheese", size: "small",
price: 12,quantity: 15, date : ISODate( "2030-03-13T11:21:39.736Z" ) },
{ _id: 4, name: "Cheese",size: "medium",
price: 13,quantity:50, date : ISODate( "2031-01-12T21:23:13.331Z" ) },
{ _id: 5, name:"Cheese", size: "large",
price: 14,quantity: 10, date : ISODate( "2031-01-12T05:08:13Z" ) },
{ _id: 6, name: "Vegan", size: "small",
price: 17, quantity: 10, date : ISODate( "2030-01-13T05:08:13Z" ) },
{ _id: 7, name: "Vegan", size: "medium",
price: 18,quantity: 10, date : ISODate( "2030-01-13T05:10:13Z" ) }
] )
计算尺寸为medium的订单中,每种类型的订单数量
db.orders.aggregate([
{
$match:{size: "medium"}
},
{
$group:{_id:"$name",totalQuantity: {$sum: "$quantity"}}
}
])
下面一段,即根据name统计过滤后的文档,并把"quantity"值相加。
根据日期范围过滤,从2030-01-01到2030-01-30之间;对过滤后的文档以日期为条件进行分组并计算;按照订单价值倒序排列文档
db.orders.aggregate( [
{
$match:
{
"date": { $gte: new ISODate( "2030-01-01" ),
$lt: new ISODate( "2030-01-30" ) }
}
},
{
$group:
{
_id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } },
totalOrderValue: { $sum: { $multiply:[ "$price", "$quantity" ] } },
averageOrderQuantity: { $avg:"$quantity" }
}
},
{
$sort: { totalOrderValue: -1 }
}
] )
统计集合中的文档数量:db.orders.count()
根据指定字段进行过滤,去掉重复的文档:db.orders.distinct("name")
即拿到集合中所有name的值,并将重复的name去掉。
聚合管道顺序优化,即聚合管道在执行过程中有一个优化的阶段,以提高性能。
即先过滤再处理。
聚合操作的限制事项:
聚合操作的命令有:$sum、 $avg、 $min、 $max、 $push、 $first、 $last、 $group、 $project、 $match、 $limit
索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引目标是提高数据库的查询效率,没有索引的话,查询会进行全盘扫描(scan every document in a collection),数据量大时严重降低查询效率。默认情况下,MongoDB在一个集合(collection)创建时,自动地对集合的_id创建了唯一索引。
单键索引,即一个索引结构指向一个字段且非数组类型。
MongoDB默认所有集合在_id字段上有一个索引。即集合对文档的_id存储一个索引。
创建集合records并插入数据:
db.records.insertOne({
"score": 356,
"location": { province: "Hebei", city:"Tangshan" }
})
创建索引,1代表升序,-1代表降序
创建索引score:db.records.createIndex({score: 1})
在内嵌字段上建立索引province:db.records.createIndex({"location.province": 1})
在内嵌文档上建立索引:db.records.createIndex({location: 1})
db.products.insertOne({
"item": "Banana",
"category": ["food", "produce", "grocery"],
"location": "4th Street Store",
"stock": 4,
"type": "cases"
})
db.products.createIndex({"item": 1, "stock": 1})db.inventory.insertMany([
{ _id: 5, type: "food", item: "aaa", ratings:[5, 8, 9 ] },
{ _id: 6, type: "food", item: "bbb", ratings: [5, 9 ] },
{ _id: 7, type: "food", item: "ccc", ratings: [9, 5, 8 ] },
{ _id: 8, type: "food", item: "ddd", ratings: [9, 5 ] },
{ _id: 9, type: "food", item: "eee", ratings: [5, 9, 5 ] }
])
db.inventory.createIndex({ratings: 1})db.inventory.find({ratings: [5,9]})db.company.insert([
{
loc : { type: "Point", coordinates: [116.502451, 40.014176 ] },
name: "军博地铁",
category : "Parks"
},
{
loc : { type: "Point", coordinates: [116.492451, 39.924176 ] },
name: "苹果园地铁",
category : "Parks"
}
])
db.company.createIndex({loc: "2dsphere"})db.company.find({
"loc" : {
"$geoWithin" : {
"$center":[[116.482451,39.914176],0.05]
}
}
})
db.places.insert({"name":"aa","addr":[32,32]})
db.places.insert({"name":"bb","addr":[30,22]})
db.places.insert({"name":"cc","addr":[28,21]})
db.places.insert({"name":"dd","addr":[34,26]})
db.places.insert({"name":"ee","addr":[34,27]})
db.places.insert({"name":"ff","addr":[39,28]})
db.places.createIndex({"addr": "2d"})db.places.find({
"addr": {$within:
{"$box": [[0,0],[30,30]]}
}
})
MongoDB提供了针对string内容的文本查询,Text Index支持任意属性值为string或string数组元素的索引查询。
一个集合仅支持最多一个Text Index,中文分词不理想,推荐ES(Elasticsearch)
创建集合fullText并插入数据:
db.fullText.insert({name:"aa",description:"no pains,no gains"})
db.fullText.insert({name:"ab",description:"pay pains,get gains"})
db.fullText.insert(
{name:"ac",description:"a friend in need,a friend in deed"})
建立全文索引,在description字段上建立全文索引,并指定语言
db.fullText.createIndex(
{description: "text"},
{default_language: "english"}
)
通过全文索引text查找pains:db.fullText.find({"$text": {"$search": "pains"}})
创建多个字段的全文索引,并指定全文索引名称:
db.newText.createIndex(
{
content: "text",
"users.comments": "text",
"users.profiles": "text"
},
{
name: "MyTextIndex"
}
)
根据集合名查看全文索引名称:db.newText.getIndexes()
根据全文索引名称删除全文索引:db.newText.dropIndex("MyTextIndex")
hash索引,针对属性的hash值进行索引查询,当要使用Hashed Index时,MongoDB能够自动的计算hash值,无需程序计算hash值。
hash index仅支持等于查询,不支持范围查询。
创建hash索引:db.collection.createIndex({"field": "hashed"})
创建复合hash索引(4.4以后的版本):db.collection.createIndex({"fieldA": 1,"fieldB": "hashed"})
索引管理
获取针对某个集合的索引:db.collection.getIndexes()
获取索引的大小:db.collection.totalIndexSize()
索引的重建:db.collection.reIndex()
索引的删除(根据索引名):db.collection.dropIndex("索引名")
删除全部索引:db.collection.dropIndexes()
但是无法删除_id对应的索引。