文档地址:https://docs.mongodb.com/manual/
MongoDB 是一个基于分布式文件存储的数据库
。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案
。
MongoDB 是一个介于关系数据库和非关系数据库之间
的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型 。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引
。
总结: mongoDB 是一个非关系型文档数据库
安装部署地址:https://blog.csdn.net/qq_37242720/article/details/122680686
不管我们学习什么数据库都应该学习其中的基础概念,在mongodb中基本的概念是文档、集合、数据库。
mongodb中的库就类似于传统关系型数据库中库的概念,用来通过不同库隔离不同应用数据`。mongodb中可以建立多个数据库。每一个库都有自己的集合和权限,不同的数据库也放置在不同的文件中。默认的数据库为"test",数据库存储在启动指定的data目录中。
集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表的概念。
集合存在于数据库中,一个库中可以创建多个集合。每个集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
文档集合中一条条记录,是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB不支持 | |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
## 查看有权限查看的所有的数据库命令 # 方法一 show databases; # 方法二 show dbs; ## 选择和创建数据库的语法格式,use代表创建并使用,当库中没有数据时默认不显示这个库 use 数据库名称 ## 查看当前正在使用的数据库命令 db ## 删除数据库,主要用来删除已经持久化的数据库,默认删除当前选中的库 db.dropDatabase()
MongoDB 中默认的数据库为 test,如果你没有创建新的数据库,集合将存放在 test 数据库中。
数据库名可以是满足以下条件的任意UTF-8字符串。
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
注意: 在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。
集合,类似关系型数据库中的表。可以显示的创建,也可以隐式的创建。
## 查看当前库中的集合(表):show tables命令 # 方式一 show collections # 方式二 show tables ## 集合的显式创建 db.createCollection(name, options) # 参数说明: # name: 要创建的集合名称 # options: 可选参数, 指定有关内存大小及索引的选项 ## 集合的隐式创建 db.集合名称.insert({"name":"tom"}) ## 删除集合 如果成功删除选定集合 则 drop() 方法返回 true 否则返回 false 语法格式: # 方式一 db.collection.drop() # 方式二 db.集合名称.drop()
集合的命名规范:
参考文档:https://docs.mongodb.com/manual/reference/method/
## 插入单条文档 db.集合名称.insert({"name":"xuxiansheng","age":18,"sex":"男"}); ## 插入多条文档 db.集合名称.insertMany( [ <document 1> , <document 2>, ... ], { writeConcern: 1,//写入策略,默认为 1,即要求确认写操作,0 是不要求。 ordered: true //指定是否按顺序写入,默认 true,按顺序写入。 } ) db.集合名称.insert([ {"name":"java","age":18,"sex":"男"}, {"name":"go","age":16,"sex":"女"} ]); ## 脚本方式 for(let i=; i<10;i++){ db.users.insert({"_id":i,"name":"java_"+i,"age":i}) } ## 查询所有 db.集合名称.find(); ## 删除文档 # 参数说明: - query:可选`删除的文档的条件。 - justOne:可选`如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。 - writeConcern :可选抛出异常的级别 db.集合名称.remove( <query>, { justOne: <boolean>, writeConcern: <document> } ) ## 更新文档 # 参数说明: - query:update的查询条件,类似sql update查询内where后面的。 - updat:update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的 - upsert:可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。 - multi:可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。 - writeConcern:可选,抛出异常的级别。 db.集合名称.update( <query>, <update>, { upsert: <boolean>, multi: <boolean>, writeConcern: <document> } ); # 这个更新是将符合条件的全部更新成后面的文档,相当于先删除在更新 db.集合名称.update({"name":"zhangsan"},{name:"11",bir:new date()}) # 保留原来数据更新,但是只更新符合条件的第一条数据 db.集合名称.update({"name":"xiaohei"},{$set:{name:"mingming"}}) # 保留原来数据更新,更新符合条件的所有数据 - db.集合名称.update({name:”小黑”},{$set:{name:”小明”}},{multi:true}) # 保留原来数据更新,更新符合条件的所有数据 没有条件符合时插入数据 - db.集合名称.update({name:”小黑”},{$set:{name:”小明”}},{multi:true,upsert:true})
注意:在 mongodb 中每个文档都会有一个_id作为唯一标识,_id默认会自动生成如果手动指定将使用手动指定的值作为_id 的值。
MongoDB 查询文档使用 find() 方法。find() 方法以非结构化的方式来显示所有文档。
## find() 方法以非结构化的方式来显示所有文档 # 参数: # - query:可选,使用查询操作符指定查询条件 # - projection:可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。 db.集合名称.find(query, projection) ## pretty() 方法以格式化的方式来显示所有文档。 db.集合名称.find().pretty()
如果你熟悉常规的 SQL 数据,通过下表可以更好的理解 MongoDB 的条件语句查询:
操作 | 格式 | 范例 | RDBMS中的类似语句 |
---|---|---|---|
等于 | {<key>:<value> } | db.col.find({“by”:“菜鸟教程”}).pretty() | where by = ‘徐先生’ |
小于 | {<key>:{$lt:<value>}} | db.col.find({“likes”:{$lt:50}}).pretty() | where likes < 50 |
小于或等于 | {<key>:{$lte:<value>}} | db.col.find({“likes”:{$lte:50}}).pretty() | where likes <= 50 |
大于 | {<key>:{$gt:<value>}} | db.col.find({“likes”:{$gt:50}}).pretty() | where likes > 50 |
大于或等于 | {<key>:{$gte:<value>}} | db.col.find({“likes”:{$gte:50}}).pretty() | where likes >= 50 |
不等于 | {<key>:{$ne:<value>}} | db.col.find({“likes”:{$ne:50}}).pretty() | where likes != 50 |
db.集合名称.find({key1:value1, key2:value2,...}).pretty()
类似于 WHERE 语句:WHERE key1=value1 AND key2=value2
MongoDB OR 条件语句使用了关键字 $or
,语法格式如下:
db.集合名称.find( { $or: [ {key1: value1}, {key2:value2} ] } ).pretty()
类似于 WHERE 语句:WHERE key1=value1 or key2=value2
db.集合名称.find( {"age": {$gt:50}, $or: [{"name": "java"},{"name": "MongoDB"}]} ).pretty();
-- 测试数据 db.users.insert({"_id":100, "name":"java_100", "age": 100, "likes":["抽烟", "喝酒", "烫头"]}) -- 执行数组查询 db.users.find({likes:"抽烟"}) -- $size 按照数组长度查询 > db.users.find({likes:{$size:3}});
db.users.find({likes:/烟/});
类似SQL中 where name like “%name%''
注意:在mongoDB中使用正则表达式可以是实现近似模糊查询功能
## sort 排序 1:升序 -1:降序 db.集合名称.find().sort({name:1,age:1}), db.users.find().sort({"name": 1, "age":-1})
类似 SQL 语句为:order by name asc, age desc
db.集合名称.find().sort({条件}).skip(start).limit(rows);
类似于 SQL 语句为:limit start, rows
db.集合名称.count(); db.users.find({"name":/java/}).count();
类似于 SQL 语句为:select count(id) from ....
db.集合名称.distinct('字段')
类似于 SQL 语句为:select distinct name from ....
db.集合名称.find({条件},{name:1,age:1}) - 参数2: 1返回 0不返回 1和0不能同时使用 db.users.find({"name":/java/}, {name:1,age:1}
$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。
MongoDB 中可以使用的类型如下表所示:
类型 | 数字 | 别名 | 备注 |
---|---|---|---|
Double | 1 | “double” | |
String | 2 | “string” | |
Object | 3 | “object” | |
Array | 4 | “array” | |
Binary data | 5 | “binData” | |
Undefined | 6 | “undefined” | 不推荐。 |
Object id | 7 | “objectId” | |
Boolean | 8 | “bool” | |
Date | 9 | “date” | |
Null | 10 | “null” | |
Regular Expression | 11 | “regex” | |
DBPointer | 12 | “dbPointer” | 不推荐。 |
JavaScript | 13 | “javascript” | |
Symbol | 14 | “symbol” | 不推荐。 |
JavaScript code with scope | 15 | “javascriptWithScope” | MongoDB 4.4中已弃用。 |
32-bit integer | 16 | “int” | |
Timestamp | 17 | “timestamp” | |
64-bit integer | 18 | “timestamp” | |
Decimal128 | 19 | “timestamp” | |
Min key | -1 | “timestamp” | Query with -1 . |
Max key | 127 | “timestamp” |
>db.col.insert({ title: 'PHP 教程', description: 'PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。', by: '徐先生', url: 'http://www.java521.com', tags: ['php'], likes: 200 }) >db.col.insert({ title: 'Java 教程', description: 'Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。', by: '徐先生', url: 'http://www.runoob.com', tags: ['java'], likes: 150 }) >db.col.insert({ title: 'MongoDB 教程', description: 'MongoDB 是一个 Nosql 数据库', by: '徐先生', url: 'http://www.java521.com', tags: ['mongodb'], likes: 100 }) >db.col.insert({ title: 666, description: 'MySQL 是一个 关系型 数据库', by: '徐先生', url: 'http://www.java521.com', tags: ['mongodb'], likes: 100 }) -- 使用find()命令查看数据 > db.col.find().pretty(); ## 如果想获取 "col" 集合中 title 为 String 的数据,你可以使用以下命令: # 方式一 db.col.find({"title" : {$type : 2}}).pretty(); # 方式二 db.col.find({"title" : {$type : 'string'}}).pretty();
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
文档地址:https://docs.mongodb.com/manual/indexes/
下图说明了使用索引选择和排序匹配文档的查询:
从根本上说,MongoDB 中的索引与其他数据库系统中的索引类似。MongoDB在集合级别定义索引,并支持 MongoDB集合中文档的任何字段或子字段的索引。
## 创建索引 # Key 值为你要创建的索引字段 # 1 按升序创建索引,-1按降序来创建索引 db.集合名称.createIndex(keys, options) db.集合名称.createIndex({"title":1,"description":-1})
createIndex()
接收可选参数,可选参数列表如下:
Parameter | Type | Description |
---|---|---|
background | Boolean | 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false。 |
unique | Boolean | 建立的索引是否唯一。指定为true创建唯一索引。默认值为false. |
name | string | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。 |
dropDups | Boolean | **3.0+版本已废弃。**在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false. |
sparse | Boolean | 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false. |
expireAfterSeconds | integer | 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
v | index version | 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。 |
weights | document | 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。 |
default_language | string | 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语 |
language_override | string | 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language. |
## 查看集合索引 db.集合名称.getIndexes() ## 查看集合索引大小 db.集合名称.totalIndexSize() ## 删除集合所有索引 db.集合名称.dropIndexes() ## 删除集合指定索引 db.集合名称.dropIndex("索引名称")
说明:一个索引的值是由多个 key 进行维护的索引的称之为复合索引。
db.集合名称.createIndex({"title":1,"description":-1})
注意:mongoDB 中复合索引和传统关系型数据库一致都是最左前缀原则
MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。类似 SQL 语句中的 count(*)。
MongoDB中聚合的方法使用aggregate(),aggregate() 方法的基本语法格式:
db.集合名称.aggregate(AGGREGATE_OPERATION);
db.mycol.insertMany([ { _id: 1, title: 'MongoDB Overview', description: 'MongoDB is no sql database', by_user: 'runoob.com', url: 'http://www.java521.com', tags: ['mongodb', 'database', 'NoSQL'], likes: 100 }, { _id: 2, title: 'NoSQL Overview', description: 'No sql database is very fast', by_user: 'runoob.com', url: 'http://www.java521.com', tags: ['mongodb', 'database', 'NoSQL'], likes: 10 }, { _id: 3, title: 'Neo4j Overview', description: 'Neo4j is no sql database', by_user: 'Neo4j', url: 'http://www.baidu.com', tags: ['neo4j', 'database', 'NoSQL'], likes: 750 } ]) ## 通过以上集合计算每个作者所写的文章数,使用aggregate()计算 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
以上实例类似sql语句:
select by_user, count(*) from mycol group by by_user
表达式 | 描述 | 实例 |
---|---|---|
$sum | 计算总和。 | db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { s u m : " sum : " sum:"likes"}}}]) |
$avg | 计算平均值 | db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { a v g : " avg : " avg:"likes"}}}]) |
$min | 获取集合中所有文档对应值得最小值。 | db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { m i n : " min : " min:"likes"}}}]) |
$max | 获取集合中所有文档对应值得最大值。 | db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { m a x : " max : " max:"likes"}}}]) |
$push | 将值加入一个数组中,不会判断是否有重复的值。 | db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", url : { p u s h : " push: " push:"url"}}}]) |
$addToSet | 将值加入一个数组中,会判断是否有重复的值,若相同的值在数组中已经存在了,则不加入。 | db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", url : { a d d T o S e t : " addToSet : " addToSet:"url"}}}]) |
$first | 根据资源文档的排序获取第一个文档数据。 | db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", first_url : { f i r s t : " first : " first:"url"}}}]) |
$last | 根据资源文档的排序获取最后一个文档数据 | db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", last_url : { l a s t : " last : " last:"url"}}}]) |
创建spring boot 项目springboot-mongodb
添加依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> <!--lombok插件--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
配置
# 应用名称 spring.application.name=springboot-mongodb # 应用服务 WEB 访问端口 server.port=8080 # mongodb 没有开启任何安全协议 # mongodb(协议)://192.168.200.128(主机):27017(端口)/users(库名) spring.data.mongodb.uri=mongodb://192.168.200.128:27017/users # mongodb 存在密码 #spring.data.mongodb.host=192.168.200.128 #spring.data.mongodb.port=27017 #spring.data.mongodb.database=users #spring.data.mongodb.username=root #spring.data.mongodb.password=root
···java
@SpringBootTest
public class CollectionTest {
@Autowired private MongoTemplate mongoTemplate; //创建集合 @Test public void createCollection() { mongoTemplate.createCollection("users"); } //删除集合 @Test public void dropCollection() { mongoTemplate.dropCollection("users"); }
}
···
@Data @Document("users") public class User { @Id private Integer id; @Field private String name; @Field private Integer age; @Field private Date bir; }
@Document
value 、collection
)用来指定操作的集合名称@Id
@Field
name,value
)用来指定在文档中 key 的名称,默认为成员变量名@Transient
添加一条文档
@Test public void insertOrSave() { User user = new User(); user.setId(1); user.setAge(20); user.setName("徐先生"); user.setBir(new Date()); //insert不存在时添加, 存在时报错 //User saveUser = mongoTemplate.insert(user); //save不存在时添加, 存在时更新 User saveUser = mongoTemplate.save(user); System.out.println(saveUser); }
添加多条文档
@Test public void batchInsertOrSave() { List<User> list = new ArrayList<>(); for (int i = 2; i <= 10; i++) { User user = new User(); user.setId(i); user.setAge(18 + i); user.setName("徐先生" + i); user.setBir(new Date()); list.add(user); } /* insert不存在时添加, 存在时报错 参数一:批量数据 参数二:放入哪个集合 */ Collection<User> insert = mongoTemplate.insert(list, User.class); System.out.println(insert); }
insert
报DuplicateKeyException
提示主键重复;save
对已存在的数据进行更新。insert
可以一次性插入整个数据,效率较高;save
需遍历整个数据,一次插入或更新,效率较低。//文档查询 @Test public void findAll() { //1.查询所有 //List<User> list = mongoTemplate.findAll(User.class); List<User> list = mongoTemplate.findAll(User.class, "users"); list.forEach(System.out::println); //2.基于id 查询一条文档 User user = mongoTemplate.findById(1, User.class); System.out.println(user); //3.添加查询 //参数一:查询条件 参数二:查询类型 List<User> userList = mongoTemplate.find(new Query(), User.class); userList.forEach(System.out::println); //4.等值查询 List<User> userList1 = mongoTemplate.find(Query.query(Criteria.where("name").is("徐先生")), User.class); userList1.forEach(System.out::println); System.out.println("========比较查询========="); //5.比较查询 > gt < lt >= gte <= lte List<User> userList2 = mongoTemplate.find(Query.query(Criteria.where("age").lt(25)), User.class); userList2.forEach(System.out::println); System.out.println("================="); List<User> userList3 = mongoTemplate.find(Query.query(Criteria.where("age").gt(25)), User.class); userList3.forEach(System.out::println); System.out.println("================="); List<User> userList4 = mongoTemplate.find(Query.query(Criteria.where("age").lte(25)), User.class); userList4.forEach(System.out::println); System.out.println("================="); List<User> userList5 = mongoTemplate.find(Query.query(Criteria.where("age").gte(25)), User.class); userList5.forEach(System.out::println); System.out.println("========and========="); List<User> userList6 = mongoTemplate.find(Query.query(Criteria.where("name").is("徐先生4").and("age").is(22)), User.class); userList6.forEach(System.out::println); System.out.println("========or========="); Criteria criteria = new Criteria().orOperator( Criteria.where("name").is("徐先生4"), Criteria.where("name").is("徐先生5") ); List<User> userList7 = mongoTemplate.find(Query.query(criteria), User.class); userList7.forEach(System.out::println); System.out.println("=========and or========"); Criteria criteria1 = new Criteria().and("age").is(20).orOperator( Criteria.where("name").is("徐先生3"), Criteria.where("name").is("徐先生4")); List<User> userList8 = mongoTemplate.find(Query.query(criteria1), User.class); userList8.forEach(System.out::println); System.out.println("=========sort 排序========"); Query query = new Query(); query.with(Sort.by(Sort.Order.desc("age")));//desc 降序 asc 升序 List<User> userList9 = mongoTemplate.find(query, User.class); userList9.forEach(System.out::println); System.out.println("========skip limit 分页========="); Query queryPage = new Query(); queryPage.with(Sort.by(Sort.Order.desc("age")))//desc 降序 asc 升序 .skip(0) //起始条数 .limit(4); //每页显示记录数 List<User> userList10 = mongoTemplate.find(queryPage, User.class); userList10.forEach(System.out::println); System.out.println("========count 总条数========="); long count = mongoTemplate.count(new Query(), User.class); System.out.println(count); System.out.println("========distinct 去重========="); //参数 1:查询条件 参数 2: 去重字段 参数 3: 操作集合 参数 4: 返回类型 List<Integer> age = mongoTemplate.findDistinct(new Query(), "age", User.class, Integer.class); age.forEach(System.out::println); System.out.println("========使用 json 字符串方式查询========="); Query query2 = new BasicQuery("{$or:[{name:'徐先生8'},{name:'徐先生9'}]}", "{name:1}"); List<User> userList11 = mongoTemplate.find(query2, User.class); userList11.forEach(System.out::println); }
@Test public void update(){ //1.更新条件 Query query = Query.query(Criteria.where("age").is(22)); //2.更新内容 Update update = new Update(); update.set("name","徐先生, 您好啊~"); //单条更新 UpdateResult updateResult = mongoTemplate.updateFirst(query, update, User.class); System.out.println("匹配条数:" + updateResult.getMatchedCount()); //多条更新 //UpdateResult updateResult = mongoTemplate.updateMulti(query, update, User.class); //System.out.println("修改条数:" + updateResult.getModifiedCount()); //更新插入 //UpdateResult updateResult = mongoTemplate.upsert(query, update, User.class); //System.out.println("插入id_:" + updateResult.getUpsertedId()); }
@Test public void testDelete() { //条件删除 DeleteResult result = mongoTemplate.remove(Query.query(Criteria.where("name").is("徐先生")), User.class); System.out.println(result); //删除所有 DeleteResult deleteResult = mongoTemplate.remove(new Query(), User.class); System.out.println(deleteResult); }
文档地址:https://docs.mongodb.com/manual/replication/
MongoDB复制是将数据同步在多个服务器的过程。
复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。
复制还允许您从硬件故障和服务中断中恢复数据。
mongodb的复制至少需要两个节点。其中一个是主节点(Primary
),负责处理客户端请求,其余的都是从节点(Secondary
),负责复制主节点上的数据。
mongodb各个节点常见的搭配方式为:一主一从、一主多从。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
MongoDB复制结构图如下所示:
以上结构图中,客户端从主节点读取数据,在客户端写入数据到主节点时, 主节点与从节点进行数据交互保障数据的一致性。
当主节点与集合中的其他成员通信的时间超过配置的electionTimeoutMillis 时间段(默认为 10 秒)时,符合条件的辅助节点会调用选举来提名自己为新的主节点。集群尝试完成新主节点的选举并恢复正常操作。
在选举成功完成之前,副本集无法处理写入操作。如果将此类查询配置为在主节点离线时在辅助节点上运行,则副本集可以继续为读取查询提供服务 。
文档地址:https://docs.mongodb.com/manual/sharding/
分片(sharding)
是指将数据拆分,将其分散存在不同机器的过程
,有时也用分区(partitioning)
来表示这个概念,将数据分散在不同的机器上,不需要功能强大的大型计算机就能存储更多的数据,处理更大的负载。
分片目的是通过分片能够增加更多机器来应对不断的增加负载和数据,还不影响应用运行。
MongoDB支持自动分片
,可以摆脱手动分片的管理困扰,集群自动切分数据做负载均衡。MongoDB分片的基本思想就是将集合拆分成多个块,这些快分散在若干个片里,每个片只负责总数据的一部分,应用程序不必知道哪些片对应哪些数据,甚至不需要知道数据拆分了,所以在分片之前会运行一个路由进程,mongos进程,这个路由器知道所有的数据存放位置,应用只需要直接与mongos交互即可。mongos自动将请求转到相应的片上获取数据,从应用角度看分不分片没有什么区别。