前置要求 :
- RDBMS 关系型数据库
- Linux
- SpringBoot
MongoDB 是一个
基于分布式文件存储
的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储
解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间
的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
它支持的数据结构非常松散,是类似json
的bson
格式,因此可以存储比较复杂的数据类型。MongoDB 最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引
。
MongoDB服务端可运行在Linux、Windows平台,支持32位和64位应用,默认端口为27017
。
推荐运行在64位平台,因为MongoDB在32位模式运行时支持的最大文件尺寸为2GB。
面向集合存储,易存储对象类型的数据
支持查询,以及动态查询
支持 RUBY, PYTHON, JAVA, C++, PHP, C# 等多种语言
文件存储格式为 BSON (对 JSON 的扩展)
支持复制和故障恢复和分片
支持事务
索引 - 提高查询效率
聚合
关联查询
游戏应用
: 使用云数据库MongoDB作为游戏服务器的数据库存储用户信息。用户的游戏装备、积分等直接以内嵌文档的形式存储,方便进行查询与更新。物流应用
: 使用云数据库MongoDB存储订单信息,订单状态在运送过程中会不断更新,以云数据库MongoDB内嵌数组的形式来存储,-次查询就能将订单所有的变更读取出来,方便快捷且一目了然。社交应用
: 使用云数据库MongoDB存储用户信息以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能。并且,云数据库MongDB非常适合用来存储聊天记录,因为它提供了非常丰富的查询,并在写入和读取方面都相对较快。视频直播
: 使用云数据库MongoDB存储用户信息、礼物信息等。大数据应用
: 使用云数据库MongoDB作为大数据的云存储系统,随时进行数据提取分析,掌握行业动态。# 拉取镜像
docker pull mongo
# 创建容器
docker run --name mongodb -d -p 27017:27017 mongo:latest --auth
# 查看所有容器
docker ps -a
# 进入容器
docker exec -it d96de8c77c72(CONTAINER ID) bash
# 连接 mongo 客户端(前提: 在容器内)
mongo
# 设置 root 权限
use admin;
db.createUser({
user: 'nuo',
pwd: 'xiaonuo123..+',
roles: ['root']
});
# 认证
db.auth("nuo", "xiaonuo123..+");
# 查看数据库信息(前提: 进入 mongo 客户端)
show dbs;
# 退出 mongo 客户端
exit
# 再次 exit, 退出容器
exit
# 停止容器
docker stop d96de8c77c72(CONTAINER ID)
# 启动已有容器
docker start d96de8c77c72(CONTAINER ID)
mongodb 中的库就类似于传统关系型数据库中库的概念,用来通过不同库隔离不同应用数据
。mongodb中可以建立多个数据库, 每一个库都有自己的集合和权限,不同的数据库也放置在不同的文件中。
默认的数据库为 “test” , 数据库存储在启动指定的data目录中。
集合就是MongoDB 文档组, 类似于 RDBMS (关系数据库管理系统: Relational Database Management System)中的 '表' 的概念.
集合存在于数据库中,一个库中可以创建多个集合。每个集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
文档集合中一条条记录,是一组键值( key-value )对(即 BSON ).
MongoDB 的文档不需要设置
相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也
是 MongoDB 非常突出的特点。
# 清屏
cls;
保留库说明 :
admin
: 从权限的角度来看,这是"oot"数据库。要是将一 个用户添加到这个数据
库,这个用户自动继承所有数据库的权限。-些特定的服务器端命令也只能从这个数
据库运行,比如列出所有的数据库或者关闭服务器。local
: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合config
: 当Mongo用于分片设置时,config数据库 在内部使用,用于保存分片的相关
信息。
# 创建并使用
use xxx;
# 默认删除当前库, 但删除后其实还在当前库里边, 只是数据没了
db.dropDatabase();
没有直接的操作
# 方案一: 拷贝数据库后,删除原来的数据库
db.copyDatabase('old_name', 'new_name');
use old_name;
db.dropDatabase();
# 方案二: 利用renameCollection命令, 如果有多个集合,循环处理就好了
db.adminCommand({renameCollection: "db1.test1", to: "db2.test2"})
# 查看当前使用的数据库
db;
# 查看所有数据库(如果没有数据默认是不显示的)
show databases;
# or
show dbs;
# 1. 显式创建
db.createCollection('collection', [options]);
# 'collection': 集合名称
# [options] : 可选参数(不填则为默认)
# eg.
db.createCollection('user');
db.createCollection('user', {capped: true, size: 5000, max: 100});
2. 隐式创建-向不存在的集合插入数据
db.user.insert({"name": "nuo"});
# user: 集合名称
db.user.drop();
db.adminCommand({renameCollection: "demo.test", to: "demo.user"})
# 获取数据库中所有集合(表)
show collections;
# or
show tables;
# 1. 单条文档(数据)
db.user.insert({"name": "nuo"});
# bson 格式, key 可以不写 "",eg.
db.user.insert({name: "nuo"});
# 2. 多条文档(数据)批量插入
db.user.insertMany(
[
{name: "abc", _id:2},
{name: "123"}
],
writeConcern: 1, # 写入策略, 默认 1, 即要求确认写操作, 0 是不要求
ordered: true # 指定是否按顺序写入, 默认 true
);
db.user.insert(
[
{name: "abc", _id:1},
{name: "123"}
]
);
# 3. 脚本插入
for (let i = 0; i < 10; i ++) {
db.user.insert({userId: i});
}
注意 :在 mongodb 中,每个文档都有一个 ‘_id’ 作为唯一标识,‘_id’ 默认会自动生成,如果手动指定则将使用手动指定的指作为 ‘_id’ 的值。
db.collection.remove (
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
}
参数说明 :
- query : 可选删除的文档的条件。
- justOne : 可选如果设为 true 或 1 ,则只删除一个文档,如果不设置该参数,或使用默认值false,则删除所有匹配条件的文档。
- writeConcern : 可选抛出异常的级别,一般不用管。
eg.
# 删个别
db.user.remove({name:'nuo1'}, {justOne:false});
# 删除所有
db.collection.remove({});
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
);
参数说明:
- query : update的查询条件, 类似sql update查询内where后面的。
- update : update的对象和一些更新的操作符(她.,i.n…). 等,也可以理解为sql
update查询内set后面的- upsert: 可选,这个参数的意思是,如果不存在update的记录, 是否插入objNew,true
为插入,默认是false,不插入。- multi: 可选,mongodb默认是false,只更新找到的第一 条记录,如果这个参数为true,
就把按条件查出来多条记录全部更新。- writeConcern :可选,抛出异常的级别。
eg.
# 将符合条件的全部更新成后面的文档,相当于先删除在更新
db.user.update({name: 'nuo2'}, {name: 'nuo1', bir: new Date()});
# 保留原来数据更新,但是只更新符合条件的第一条数据
db.user.update({name: 'nuo1'}, {$set: {name: 'nuo3'});
# 保留原来数据更新,更新符合条件的所有数据
db.user.update({name: 'nuo1'}, {$set: {name: 'nuo3'},{multi:true});
# 保留原来数据更新,更新符合条件的所有数据没有条件符合时插入数据(saveOrUpdate)
db.user.update({name: 'nuo1'}, {$set: {name: 'nuo3'}, {multi: true, upsert: true});
db.collection.find(query, projection);
参数描述 :
query
: 可选,使用查询操作符指定查询条件projection
: 可选,使用投影操作符指定返回的键。查询时返回文档中所有键值,只需省略该参数即可(默认省略) 。
MongoDB 查询文档使用find() 方法。find() 方法以非结构化的方式来显示所有文档,如果你需要以易读的方式来读取数据,可以使用 pretty()
方法以格式化的方式来显示所有文档,语法格式如下 :
db.collection.find().pretty();
如果你熟悉常规的SQL数据,通过下表可以更好的理解MongoDB的条件语句查询:
db.collection.find({k1: v1, k2: v2, ...}).pretty();
类似于WHERE 语句: WHERE key1=value1 AND key2=value2
MongoDB OR条件语句使用了关键字$or,语法格式如下:
db.collection.find(
$or: [
{k1: v1}, {k2: v2}
]
).pretty();
类似于WHERE语句: WHERE k1=v1 or k2=v2
类似SQL语句为: ' where age < 18 AND (name = ' nuo1’ OR name = ' nuo2' )
db.user.find({
"age": {$lt: 18},
$or: [
{name: 'nuo1'},
{name: 'nuo2'}
]
}).pretty();
# 测试数据
db.user.insert({
"age": 3,
"like": [
"看电视",
"读书"
],
"name": "nuo11"
});
--执行数组查询
> db.user.find({like: "看电视"})
db.user.find( {name:/nuo/});
类似SQL中为'where name like '%name% '
1 : 升序
-1: 降序
db.collection.find().sort( {name:1, age:1}),
类似SQL语句为: 'order by name, age '
db.collection.find().sort( {条件}) .skip(start).limit( rows);
类似于SQL语句为: ' limit start, rows'
db.collection.count();
db.collection.find({"name": "nuo1"}).count();
类似于SQL 语句为: 'select count(id) from
db.collection.distinct('字段')
db.collection.find({query}, {name: 1, age: 1})
1 : 返回
0 : 不返回
注意:1和0不能同时使用
$type 操作符是基于 BSON 类型来检索集合中匹配的数据类型,并返回结果
db.col.find({title: {$type : 2}}). pretty();
# or
db.col. find({"title" : {$type: 'string' }}). pretty();
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中, 索引是对数据库表中一列或多列的值进行排序的一种结构。
从根本上说,MongoDB中的索引与 其他数据库系统中的索引类似。MongoDB在集合层面上定义了索引,并支持对MongoDB集合中的任何字段或文档的子字段进行索引。
# db.collection.createIndex(keys, options);
# 1 : 升序
# -1: 降序
db.test.createIndex( {"title" : 1, "description" : -1});
# 1. 查看集合索引
db.collection.getIndexes();
# 2. 查看集合索引大小
db.collection.totalIndexSize();
# 3. 删除集合所有索引
db.collection.dropIndexes();
# 4. 删除集合指定索引
db.collection.dropIndex("索引名称);
复合索引
- 说明 : 一个索引的值是由多个key进行维护的索引的称之为复合索引
- 注意 : mongoDB 中复合索引和传统关系型数据库一致都是左前缀原则
db.collection.createIndex( {"title": 1, "description": -1})
MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似 SQL 语句中的 count(*)。
{
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
by_user: 'runoob.com',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
},
{
title: 'NoSQL Overview',
description: 'No sql database is very fast',
by_user: 'runoob.com',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 10
},
{
title: 'Neo4j Overview',
description: 'Neo4j is no sql database',
by_user: 'Neo4j',
url: 'http://www.neo4j.com',
tags: ['neo4j', 'database', 'NoSQL'],
likes: 750
}
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
dependency>
spring:
data:
mongodb:
host: localhost
port: 27017
database: demo
username: nuo
password: xiaonuo123..+
@SpringBootTest
class MongodbApplicationTests {
@Autowired
private MongoTemplate mongoTemplate;
@Test
void contextLoads() {
}
/**
* TODO: 创建集合
*/
@Test
void creatCollection() {
System.out.println(mongoTemplate.createCollection("nuo"));
}
/**
* TODO: 删除集合
*/
@Test
void dropCollection() {
mongoTemplate.dropCollection("nuo");
}
/**
* TODO: 增
* - 插入重复数据时 :
* insert: 报 DuplicateKeyException 提示主键重复;
* save : 对已存在的数据进行更新.
* - 批处理操作时:
* insert: 可以一次性插入整个数据, 效率较高;
* save : 需遍历整个数据, 一次插入或更新, 效率较低.
*/
@Test
void add() {
mongoTemplate.save(new User(100, "nuo", "123"));
mongoTemplate.insert(new User(101, "nuo", "123"));
}
/**
* TODO: 删
*/
@Test
void del() {
mongoTemplate.remove(Query.query(Criteria.where("username").is("test")), User.class);
}
/**
* TODO: 改
*/
@Test
void put() {
mongoTemplate.updateMulti(Query.query(Criteria.where("username").is("nuo")), Update.update("username", "test"), User.class);
}
/**
* TODO: 查
*/
@Test
void query() {
}
}
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。
mongodb各个节点常见的搭配方式为:一主一从、一主多从。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
副本集特征:
- N 个节点的集群
- 任何节点可作为主节点
- 所有写入操作都在主节点上
- 自动故障转移
- 自动恢复
在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
为什么使用分片?
\
- 复制所有的写入操作到主节点
- 延迟的敏感数据会在主节点查询
- 单个副本集限制在12个节点
- 当请求量巨大时会出现内存不足。
- 本地磁盘不足
- 垂直扩展价格昂贵
Shard
:
- 用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障
Config Server
:
- mongod实例,存储了整个 ClusterMetadata,其中包括 chunk信息。
Query Routers
:
- 前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。