MongoDB是一个开源、高性能、无模式的文档型数据库,当初的设计就是用于简化开发和方便扩展,是NoSQL数据库产品中的一种.
它支持的数据结构非常松散,是一种类似于]JSON的格式叫BSON,所以它既可以存储比较复杂的数据类型,又相当的灵活。
MongoDB中的记录是一个文档,它是一个由字段和值对(field:value)组成的数据结构。MongoDB文档类似于JSON对象,即一个文档认为就是一个对象。字段的数据类型是字符型,它的值除了使用基本的一些类型外,还可以包括其他文档、普通数组和文档数组。
业务应用场景:
应对数据操作的"三高"需求:
MongoDB和mysql数据库的对比:
<1>命令行参数方式启动
再bin目录下打开命令行,输入如下命令(指定db路径):
mongod --dbpath=. . \data\db
<2>配置文件启动
现在bin目录同级目录下创建conf文件夹,在文件夹下建立mongod.conf的配置文件,写入如下配置:
storage:
#the directory where the mongod instance stores its data.Default value is "\data\db" onwindows .
dbPath: F:\MongDB\mongodb-win32-x86_64-windows-5.0.21\data\db
在bin目录打开命令行,通过命令行形式输入如下命令:
mongod -f ..\config\mongod.conf //conf文件的目录
mongod --config ..\config\mongod.conf //conf文件的目录
在启动MongDB的情况下:
<1>shell连接
在bin命令打开命令行,输入如下命令:
mongo
或
mongo --host=127.0.0.1 --port=27017
即可通过Shell连接MongDB
<1>在mongdb官网上下载好tgz的压缩包,上传到linux上.
<2>通过tar命令解压压缩包
<3>创建数据存储目录和日志存储目录:
mkdir -p single/data/db
mkdir -p single/log
<4>在single文件夹下编写配置文件mongo.conf:
systemLog:
#MongoDB发送所有日志输出的目标指定为文件
#The path of the log file to which mongod or mongos should send all diagnostic logginginformation
destination: file
#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径
path: "/mongodb/single/log/mongod.log"
#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。
logAppend: true
storage:
#mongod实例存储其数据的目录。storage. dbPath设置仅适用于mongod。
#打The directory where the mongod instance stores its data.Default value is "/data/db".
dbPath: "/mongodb/single/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
net:
#服务实例绑定的IP,默认是loca7host
bindIp: localhost,192.168.0.2
#bindIp
绑定的端口,默认是27017
port: 27017
<5>通过命令启动mongodb
[root@bobohost single]#/home/zc/mongodb/bin/mongod -f /mongodb/single/mongod.conf
about to fork child process,waiting until server is ready for connections.
forked process : 90384
child process started successfu11y,parent exiting
检验mongodb是否启动成功可以使用ps -e 命令或者compass连接mongodb进行测试
<6>停止和杀死mongodb进程
停止服务的方式有两种:快速关闭和标准关闭,下面依次说明:
(一)快速关闭方法(快速,简单,数据可能会出错)
目标:通过系统的kill命令直接杀死进程:
杀完要检查—下,避免有的没有杀掉。
#通过进程编号关闭节点
kill -2 -75412
注意-2 是不会丢失数据的(会先写入),-9会
(1)展示当前数据库:
show dbs
(2)获取当前数据库信息:
db
(3)使用数据库(当数据库未存在时,则自动创建数据库)
use articledb (要使用的数据库名称)
注意:在创建数据库时,这个数据库信息是存放在内存中的,所以使用show dbs
命令时是不会看到创建的数据库的(该命令是扫描磁盘上的数据)
数据库名可以是满足以下条件的任意UTF-8字符串。
有—些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
(4)删除当前数据库
db.dropDatabase()
(1)展示当前所有集合:
show collections
(2)创建集合:
语法格式为:
db.createCollection(name, options)
db.createCollections("name")
name表示要创建的集合的名称
option选项包含:
字段 | 类型 | 描述 |
---|---|---|
capped | 布尔 | (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数。 |
autoIndexId | 布尔 | 3.2 之后不再支持该参数。(可选)如为 true,自动在 _id 字段创建索引。默认为 false。 |
size | 数值 | (可选)为固定集合指定一个最大值,即字节数。 如果 capped 为 true,也需要指定该字段。 |
max | 数值 | (可选)指定固定集合中包含文档的最大数量。 |
在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。
例如:
创建固定集合 mycol,整个集合空间大小 6142800 B, 文档最大个数为 10000 个。
> db.createCollection("mycol", { capped : true, autoIndexId : true, size :
6142800, max : 10000 } )
{ "ok" : 1 }
>
(3)集合的隐式创建
当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合。
详见文档的插入章节。
提示:通常我们使用隐式创建文档即可。
(3)删除集合
db.collection.drop()
或
db.集合.drop()
返回值
如果成功删除选定集合,则drop()方法返回true,否则返回false。
文档的数据格式与JSON类似,
数据存储在MongDB中为BSON格式
使用insert()或save()方法向集合中插入文档(类比mysql向表中插入数据),语法如下:
db.collection.insert(
,
{
writeConcern:
ordered:
}
)
实例:
向comment表(未存在)中插入一条测试数据:
db.comment.insert( {
"articleid":"100000" ,
"content":"今天天气真好,阳光明媚",
"userid":"1001",
"nickname":"Rose",
"createdatetime":new Date(),
"likenum":NumberInt(10),
"state":null
}
)
最后显示:WriteResult({ "nInserted" : 1 })
表示插入成功
db.collection.insert(
,
{
writeConcern:
ordered:
}
)
也就是多写几个文档
实例:
db.comment.insert([
{
"articleid":"1000" ,
"content":"阳光太耀眼了",
"userid":"1050",
"nickname":"RSSS",
"createdatetime":new Date(),
"likenum":NumberInt(1050),
"state":null
},
{
"articleid":"10" ,
"content":"今天天气真差,乌云密布",
"userid":"1002",
"nickname":"R",
"createdatetime":new Date(),
"likenum":NumberInt(100),
"state":null
}
]
)
返回值为:
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
提示:
插入时指定了_id,则主键就是该值。
如果某条数据插入失败,将会终止插入,但已经插入成功的数据不会回滚掉。
因为批量插入由于数据较多容易出现失败,因此,可以使用try catch进行异常捕捉处理,测试的时候可以不处理。如(了解)∶
try{
db.comment.insert([
{
"_id":"1",
"articleid":"1000" ,
"content":"阳光太耀眼了",
"userid":"1050",
"nickname":"RSSS",
"createdatetime":new Date(),
"likenum":NumberInt(1050),
"state":null
},
{
"_id":"2",
"articleid":"10" ,
"content":"今天天气真差,乌云密布",
"userid":"1002",
"nickname":"R",
"createdatetime":new Date(),
"likenum":NumberInt(100),
"state":null
}
]
)
}catch(e){
print(e);
}
db.collection.find() #查看集合中的所有数据
db.collection.find({_id:"1"}) #查看id=1的数据
db.collection.findOne({likenum:"1"}} #查看likenum=1的第一条数据
如果要查询结果返回部分字段,则需要使用投影查询(不显示所有字段,只显示指定的字段)。
db.collection.find({},{字段1:1,字段2:1})
如:查询结果只显示_id、userid、nickname :
db.comment.find({userid: "1003"},{userid:1,nickname:1})
查询结果:
{ "_id" :"4","userid" : "1003","nickname":"凯撒"}
{ "_id" :"5","userid" : "1003","nickname :"凯撒”}
更新语法:
db.collection.update(query,update,options)//或
db.collection.update(
,
,
{
upsert: ,
multi: ,
writeConcern: ,
collation: ,
arrayFilters : [ ,... ],
hint:
//Avai1able starting in MongoDB 4.2
}
)
<1>覆盖的更新:
如果我们想修改_id为1的记录,点赞量为1001,输入以下语句:
db.comment.update(i_id: "1"},{likenum : NumberInt(1001})
但是修改后,其他的数据都消失了,只剩下likenum这一个字段了
<2>局部修改:
为了解决这个问题,我们需要使用修改器**$set**来实现,命令如下:
我们想修改_id为2的记录,浏览量为889,输入以下语句:_
db.comment.update({_id: "2"},{$set:{likenum:NumberInt(889)}})
这样就只修改了likenum字段了
<3>批量修改
使用的update默认修改第一条数据
使用updateMany或配置multi:true可以修改所有复合条件的数据
db.comment.updateMany({_id:"2"},{$set:{nickname:"Rose"}})
或
db.comment.updateMany({_id:"2"},{$set:{nickname:"Rose"}},{multi:true})
<4>列值增长的修改:
如果我们想实现对某列值在原有值的基础上进行增加或减少,可以使用**$inc**运算符来实现。
需求:对3号数据的点赞数,每次递增1
db.comment.update({_id: "3"},{$inc:{likenum : NumberInt(1)}})
删除文档的语法结构:
db.collection.remove(条件)
使用:
db.collection.remove({})
会使所有数据都被删除
统计使用count()方法,语法如下:
db.collection.count(uery,options)
参数如下:
参数 | 种类 | 描述 |
---|---|---|
query | document | 查询选择条件 |
options | document | 可选.用于修改计数的额外选项 |
示例:
使用以下语句查询所有数据
db.collection.count()
使用以下语句查询_id=1的数据
db.collection.count({_id:"1"})
可以使用**limit()方法来读取指定数量的数据,使用skip()**方法来跳过指定数量的数据。基本语法如下所示:
db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
如果你想返回指定条数的记录,可以在find方法后调用limit来返回结果(TopN),默认值20,例如:
db.comment.find().limit(3)
选择skip方法同样接受─个数字参数作为跳过的记录条数。(前N个不要),默认值是0
db.comment.find().skip(3)
6.排序查询
sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序排列,而-1是用于降序排列。
语法如下所示:
db.COLLECTION_NAME.find().sort({KEY:1})
或
db.集合名称.find().sort(排序方式)
选择语言
例如:
对userid降序排列,并对访问量进行升序排列
db.comment.find().sort({userid:-1,likenum:1})
skip()., limilt),sort()三个放在一起执行的时候,执行的顺序是先 sort(),然后是skip(),最后是显示的limit(),和命令编写顺序无关。
MongoDB的模糊查询是通过正则表达式的方式实现的。格式为:
db.co1lection.find({field:/正则表达式/})
或db.集合.find({字段:/正则表达式/)
提示:正则表达式是js的语法,直接量的写法。
例如,我要查询评论内容包含“开水”的所有文档,代码如下:
db.comment.find(icontent:/开水/)
如果要查询评论的内容中以“专家"开头的,代码如下:
db.comment.find(icontent:/^专家/)
3.6.2比较查询
<,<=,>,>=这个操作符也是很常用的,格式如下:
db.集合名称.find({"field":{ $gt:value }}) //大于:field > value
db.集合名称.find({"field":{ $lt:value }}) // 小于: field < value
db.集合名称.find({"field":{ $gte:value }}) //大于等于:field >= value
db.集合名称.find({"field":{ $lte:value }}) //小于等于: field <= value
db.集合名称.find({"field":{ $ne:value }}) //不等于: field != value
示例:查询评论点赞数量大于700的记录
db.comment.find({likenum:{$gt:NumberInt(700)}})
包含使用**$in**操作符。
示例:查询评论的集合中userid字段包含1003或1004的文档
db.comment.find({userid:{$in:["1003" , "1004"]}})
不包含使用**$nin**操作符。
示例:查询评论集合中userid字段不包含1003和1004的文档
db.comment.find({userid:{$nin: ["1003","1004"]}})
<4>多条件查询
我们如果需要查询同时满足两个以上条件,需要使用**$and**操作符将条件进行关联。(相当于SQL的and)
格式为:
$and:[{},{},{}]
选择语言
示例:查询评论集合中likenum大于等于100并且小于2000的文档:
db.comment.find({$and: [{likenum:{$gte:100}},{likenum:{$lt:2000}}]})
如果两个以上条件之间是或者的关系,我们使用**$or**操作符进行关联,与前面and的使用方式相同
格式为:
$or :[ {},{},{} ]
示例:查询评论集合中userid为1003,或者点赞数小于1000的文档记录
db.comment.find({$or:[ {userid: "1003"},{likenum:{$1t:1000}}]})
选择切换数据库: use articledb
插入数据: db.comment.insert({bson数据})
查询所有数据: db.comment.find();
条件查询数据: db.comment.find({条件})
查询符合条件的第一条记录: db.comment.findone({条件})
查询符合条件的前几条记录: db.comment.find({条件}).limit(条数)
查询符合条件的跳过的记录: db.comment.find({条件}).skip(条数)
修改数据: db.comment.update({条件}, {修改后的数据})或 db.comment.update({条件},{$set:{要修改部分的字段:数据})
修改数据并自增某字段值: db.comment.update({条件},{$inc:{自增的字段:步进值}})
删除数据: db.comment.remove({条件})
统计查询: db.comment.count({条件})
模糊查询: db.comment.find({字段名:/正则表达式/})
条件比较运算: db.comment.find({字段名:{$gt:值}})
包含查询: db.comment.find({字段名:{$in:[值1,值2]}})或 db.comment.find({字段名:{$nin:[值1,值2]})
条件连接查询: db.comment.find({$and:[{条件1}, {条件2]]})或 db.comment.find({$or :[{条件1},{条件2}]})
具体可以看:MongoDB多条件,正则表达式查询
索引是特殊的数据结构,它以易于遍历的形式存储集合数据集的一小部分。索引存储特定字段或一组字段的值,按字段值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作。此外,MongoDB还可以使用索引中的排序返回排序结果。
具体请看:mysql索引
(1)单字段索引
MongoDB支持在文档的单个字段上创建用户定义的升序/降序索引,称为单字段索引(Single Field lndex)。对于单个字段索引和排序操作,索引键的排序顺序〈即升序或降序)并不重要,因为MongoDB可以在任何方向上遍历索引。
(2)复合索引
MongoDB还支持多个字段的用户定义索引,即复合索引(Compound Index)。复合索引中列出的字段顺序具有重要意义。例如,如果复合索引由{userid: 1,score:-1}组成,则索引首先按userid正序排序,然后在每个userid的值内,再在按score倒序排序。
返回—个集合中的所有索引的数组。
语法:
db.colleetion.getIndexes()
查询结果为:
[
{ "v" : 2,
"key" :
{
"_id" : 1
},
"name" : "_id_"
}
]
在集合上创建索引。
语法:
db.collection.createIndex(keys,options)
其中的参数:
parameter | Type | Description |
---|---|---|
keys | document | 包含字段和值对的文档,其中字段是索引键,值描述该字段的索引类型。对于字段上的升序索引,请指定值1;对于降序索引,请指定值-1。比如:{字段:1或-1},其中1为指定按升序创建索引,如果你想按降序来创建索引指定为-1即可。另外,MongoDB支持几种不同的索引类型,包括文本、地理空间和哈希索引。 |
options | document | 可选。包含—组控制索引创建的选项的文档。有关详细信息,请参见选项详情看列表。 |
option选项有:
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 | 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。默认为英语 |
(1)单字段索引的创建:
例如,给userid加上升序索引
db.comment.createIndex({userid:1})
查看一下索引:
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_"
},
{
"v" : 2,
"key" : {
"likenum" : 1
},
"name" : "likenum_1"
}
]
(2)复合索引的创建:
在大括号中多加几个字段,例如同时给userid和likenum加上索引:
db.comment.createIndex({userid:1,likenum:-1})
查看一下索引:
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_"
},
{
"v" : 2,
"key" : {
"likenum" : 1
},
"name" : "likenum_1"
},
//复合索引:
{
"v" : 2,
"key" : {
"userid" : 1,
"likenum" : -1
},
"name" : "userid_1_likenum_-1"
}
]
说明:可以移除指定的索引,或移除所有索引、指定索引的移除
语法:
db.collection.dropIndex(index)
Parameter | Type | Description |
---|---|---|
document | string or index | 指定要删除的索引。可以通过索引名称或索引规范文档指定索引。若要删除文本索引,请指定索引名称。 |
例如:删除comment集合中userid字段上的升序索引:
db.comment.dropIndex({userid:1})
{ "nIndexeswas" : 3,"ok" : 1 }
删除所有的除_id以外的索引:
db.collection.dropIndexes()
(1)执行计划:
分析查询性能(Analyze Query Performance)通常使用执行计划〈解释计划、Explain Plan)来查看查询的情况,如**查询耗费的时间、是否基于索引查询等。**语法:
db.collection.find(query,options).explain(options)
返回值中:
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"likenum" : 1
},
"indexName" : "likenum_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"likenum" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"likenum" : [
"[5.0, 5.0]"
]
}
}
},
winningPlan记录了最后的测试结果,stage:"FETCH"表示使用到了索引,如果是"COLSCAN"表示依旧是扫描整个集合,没有用到索引
同样可以通过compass图形化界面来实现执行计划:
(2)涵盖的查询(Covered Queries)
当查询条件和查询的投影仅包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存。这些覆盖的查询可以非常有效。
例如建立了likenum的索引,并且也只查询likenum,那就是覆盖的查询
mongodb-driver (了解)
mongodb-driver是mongo官方推出的java连接mongoDB的驱动包,相当于JDBC驱动。
官方驱动说明和下载:http://mongodb.github.io/mongo-java-driver/
官方驱动示例文档: http://mongodb.github.io/mongo-java-driver/3.8/driver/getting-started/quick-start
SpringDataMongoDB
SpringData家族成员之一,用于操作MongoDB的持久层框架,封装了底层的mongodb-driver。
官网主页: https://projects.spring.io/spring-data-mongodb/
1.新建spring-boot项目
2.导入maven坐标:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
dependency>
3.编写配置:
spring:
data:
mongodb:
#主机号
host: 192.168.37.136
#连接数据库
database: articledb
port: 27017
#也可以用url连接:
#url: mongodb://192.168.37.136/articledb
导包后就可以通过idea连接mongodb数据库了
package com.example.article.pojo;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 文章评论实体类
*/
//把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。
//@Document(collection="mongodb 对应 collection 名")
// 若未加 @Document ,该 bean save 到 mongo 的 comment collection
// 若添加 @Document ,则 save 到 comment collection
@Document(collection="comment")//可以省略,如果省略,则默认使用类名小写映射集合
@Data
//复合索引
// @CompoundIndex( def = "{'userid': 1, 'nickname': -1}")
public class Comment implements Serializable {
//主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫“id”,则该注解可以省略,否则必须写
@Id
private String id;//主键
//该属性对应mongodb的字段的名字,如果一致,则无需该注解
@Field("content")
private String content;//吐槽内容
private LocalDateTime publishtime;//发布日期
//添加了一个单字段的索引
@Indexed
private String userid;//发布人ID
private String nickname;//昵称
private LocalDateTime createdatetime;//评论的日期时间
private Integer likenum;//点赞数
private Integer replynum;//回复数
private String state;//状态
private String parentid;//上级ID
private String articleid;
}
1)单字段索引注解
@Indexed
声明该字段需要索引,建索引可以大大的提高查询效率。 Mongo命令参考:
db.comment.createIndex({"userid":1})
2)复合索引注解
@CompoundIndex
复合索引的声明,建复合索引可以有效地提高多字段的查询效率。 Mongo命令参考:
db.comment.createIndex({"userid":1,"nickname":-1})
java代码参考:
@CompoundIndex( def = "{'userid': 1, 'nickname': -1}")
3)指定主键注解
@Id
主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫“id”,则该注解可以省略,否则必须写
4)添加单字段索引注解
@Indexed
在对应的属性上添加,添加了一个单字段的索引
创建数据访问接口 cn.itcast.article包下创建dao包,包下创建接口CommentRespository
public interface CommentRepsitory extends MongoRepository<Comment, String> {
}
通过继承mongodb提供的MongoRespository(I:表示要映射的对象, ID: 表示id主键的数据类型 ) 接口**(类似于mybatis-plus提供的方法)**
建立service包后,建立实现类CommentService:
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
/**
* 保存一个评论
*
* @param comment
*/
public void saveComment(Comment comment) {
//如果需要自定义主键,可以在这里指定主键;如果不指定主键,MongoDB会自动生成主键
//设置一些默认初始值。。。
//调用dao
commentRepository.save(comment);
}
/**
* 更新评论
*
* @param comment
*/
public void updateComment(Comment comment) {
//调用dao
commentRepository.save(comment);
}
/**
* 根据id删除评论
*
* @param id
*/
public void deleteCommentById(String id) {
//调用dao
commentRepository.deleteById(id);
}
/**
* 查询所有评论
*
* @return
*/
public List<Comment> findCommentList() {
//调用dao
return commentRepository.findAll();
}
/**
* 根据id查询评论
*
* @param id
* @return
*/
public Comment findCommentById(String id) {
//调用dao
return commentRepository.findById(id).get();
}
}
MongoRepository接口提供的方法:
@SpringBootTest
public class TestCommentService {
@Autowired
private CommentService commentService;
@Test
public void TestfindCommentList(){
List<Comment> commentList =commentService.findCommentList();
for (Comment comment : commentList) {
System.out.println(comment);
}
}
}
如果输出正确结果并且没有报错,那么就是可行的
dao层:
Page<Comment> findCommentByParentid(String parentid, Pageable pageable);
findCommenByParentid是MongoRepository为我们提供的方法,我们只需要注意命名规范,就可以使用这些方法(类似于Mybatis-Plus)
其中的Pageable是spring框架提供的分页查询接口
service层:
public Page<Comment> findCommentListByParentId(String parentId,int page,int size) {
//调用dao层的分页查询方法,注意:page-1是因为我们的第一页在查询结果中应为第零页
return commentRepository.findCommentByParentid(parentId, PageRequest.of(page-1, size));
}
通过PageRequest类可以通过of方法创建一个PageRequset对象,而PageRequset实行了Pageable接口,传入的是当前页码(page)和当前展示总数量(size).
返回的Page对象中,可以通过以下方法获得对应值:
@Test
public void TestfindCommentListByParentId() {
String parentid="1";
int size=1;
int page=1;
Page<Comment> commentPage = commentService.findCommentListByParentId(parentid, page, size);
System.out.println("总页数:"+commentPage.getTotalPages());
System.out.println("总数量:"+commentPage.getTotalElements());
//getContent表示当前这一页的结果
for (Comment comment : commentPage.getContent()) {
System.out.println(comment);
}
}
编写service中的方法:
根据传入id进行点赞操作(将likenum+1)
这里使用到了mongoTemplate模板。
他提供了很多的方法:具体方法举例可以看这篇文章:MongoTemplate的基本使用方法
public void updateCommnetById(String id){
//query为查询条件,通过Criteria(标准)创建条件
Query query=new Query(Criteria.where("_id").is(id));
Update update= new Update();
//inc表示自增1,可以指定步长
update.inc("likenum");
//update需要传入三个参数:
/*
1.Query
2.Update
3.Class
Query:表示查询条件,通过Criteria的静态方法创建条件
Update:表示更新的内容,inc表示增加1,可以指定步长
Class:表示你要更新的实体类
*/
mongoTemplate.updateFirst(query,update,Comment.class);
}
MongoDB中的副本集(Replica Set)是一组维护相同数据集的mongod服务。副本集可提供冗余和高可用性,是所有生产部署的基础。
也可以说,副本集类似于有自动故障恢复功能的主从集群。通俗的讲就是用多台机器进行同一数据的异步同步,从而使多台机器拥有同一数据的多个副本,并且当主库当掉时在不需要用户干预的情况下自动切换其他备份服务器做主库。而且还可以利用副本服务器做只读服务器,实现读写分离,提高负载。
主从复制和副本集区别:
主从集群和副本集最大的区别就是副本集没有固定的"主节点;整个集群会选出一个"主节点,当其挂掉后,又在剩下的从节点中选中其他节点为"主节点",副本集总有一个活跃点(主、 primary)和一个或多个备份节点(从、secondary)。
副本集有两种类型三种角色
两种类型:
三种角色:
一主一副一仲裁:
(4)创建步骤:
1.创建3个结点对三个角色(创建规则与在linux上连接MongDB数据库步骤相同),不同的是需要配置pid文件路径和副本集名称:
2.编写conf文件(3个都要):
systemLog:
#MongoDB发送所有日志输出的目标指定为文件
#The path of the log file to which mongod or mongos should send all diagnostic logginginformation
destination: file
#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径
path: "/home/zc/mongodb/ReplicaSet/myres_27017/log/mongod.log"
#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。
logAppend: true
storage:
#mongod实例存储其数据的目录。storage. dbPath设置仅适用于mongod。
#打The directory where the mongod instance stores its data.Default value is "/data/db".
dbPath: "/home/zc/mongodb/ReplicaSet/myres_27017/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#配置pid文件路径
pidFilePath: "/home/zc/mongodb/ReplicaSet/myres_27017/log/mongod.pid"
net:
#服务实例绑定的IP,默认是loca7host
bindIp: localhost,192.168.37.136
port: 27017
#配置副本集
replication:
#副本集的名称
replSetName: myrs
使用客户端命令连接任意一个节点,但这里尽量要连接主节点(27017节点):
/usr/loca1/mongodb/bin/mongo --host=180.76.159.126 --port=27017
结果,连接上之后,很多命令无法使用,,比如show dbs
等,必须初始化副本集才行
准备初始化新的副本集:
语法:
rs.initiate(configuration)
configuration是其他配置,默认可以不写
结果:
在主节点添加从节点,将其他成员加入到副本集
语法:
rs.add (host,arbiteronly)
选项:
Parameter | Type | Description |
---|---|---|
host | string or document | 要添加到副本集的新成员。指定为字符串或配置文档:1)如果是一个字符串,则需要指定新成员的主机名和可选的端口号;2)如果是一个文档,请指定在members数组中找到的副本集成员配置文档。您必须在成员配置文档中指定主机字段。有关文档配置字段的说明,详见下方文档“主机成员的配置文档" |
arbiteronly | boolean | 可选的。仅在值为字符串时适用。如果为true,则添加的主机是仲裁者。 |
添加一个仲裁节点到副本集
语法:
rs.addArb(host)
在主节点创建一个集合并插入一条数据后,再连接副节点后测试读取数据:
发现,不能读取集合的数据。当前从节点只是一个备份,不是奴隶节点,无法读取数据,写当然更不行。因为默认情况下,从节点是没有读写权限的,可以增加读的权限,但需要进行设置。
设置读操作权限:
说明:
设置为奴隶节点,允许在从成员上运行读的操作语法:
rs.secondaryOk()
或
rs.secondaryOk(true)
要想取消为副节点,那就传入false:
rs.secondaryOk(false)
仲裁者节点,不存放任何业务数据的,可以登录查看(即使使用了rs.secondaryOk()命令)
<6>主节点选举原则
MongoDB在副本集中,会自动进行主节点的选举,主节点选举的触发条件:
选举规则是根据票数来决定谁获胜:
1.票数最高,且获得了“大多数"成员的投票支持的节点获胜。“大多数"的定义为:假设复制集内投票成员数量为N,则大多数为N/2+1。例如:3个投票成员,则大多数的值是2。当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary复制集将无法提供写服
务,处于只读状态。
2.若票数相同,且都获得了“"大多数"成员的投票支持的,数据新的节点获胜。数据的新旧是通过操作日志oplog来对比的。
在获得票数的时候,优先级(priority)参数影响重大。
可以通过设置优先级(priority)来设置额外票数。优先级即权重,取值为0-1000,相当于可额外增加0-1000的票数,优先级的值越大,就越可能获得多数成员的投票(votes)数。指定较高的值可使成员更有资格成为主要成员,更低的值可使成员更不符合条件。
默认情况下,优先级的值是1
关闭27018副本节点:
发现,主节点和仲裁节点对27018的心跳失败。因为主节点还在,因此,没有触发投票选举如果此时,在主节点写入数据。
db .comment.insert({"_id":"2"})
再启动从节点,会发现,主节点写入的数据,会自动同步给从节点。
当主节点故障时,仲裁节点发现连接不上主节点了,那么就会触发选举规则,将副节点升级为主节点,主节点降级为副节点。
注意:即使主节点再次启动后,他依旧是副节点了
此时只有主节点,无法完成写操作,故已经故障了(服务自动降级)
主节点变为副节点
此时,虽然副节点已得到了最高的票数(自身一票),但是没有得到其他成员的支持(只有自身一个人),所以依旧是副节点.
服务依旧故障
指定url,连接副本集的语法是:
mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=myReplicaSet
注意:需要开放端口或关闭防火墙
副本集连接语法:
mongodb://host1,host2,host3/articledb?connect=replicaset&slaveok=true&replicaset=副本集名字
需要使用url编写,在yml配置文件中编写:
spring:
data:
mongodb:
#连接副本集需要使用uri连接:
uri: mongodb://192.168.37.136:27017,192.168.37.136:27018,192.168.37.136:27019/articledb?connect=replicaset&slaveok=true&replicaset=myrs
分片(sharding)是一种跨多台机器分布数据的方法,MongoDB使用分片来支持具有非常大的数据集和高吞吐量操作的部署。
换句话说**:分片(sharding)是指将数据拆分,将其分散存在不同的机器上的过程。有时也用分区(partitioning)来表示这个概念。将数据分散到不同的机器上,不需要功能强大的大型计算机就可以储存更多的数据,处理更多的负载。**
MongoDB分片群集包含以下组件:
图解:
两个分片节点副本集(3+3)+一个配置节点副本集(3)+两个路由节点(2),共11个服务节点。
1.搭建节点副本集(存储):
步骤与搭建副本集一样:
先创建存放log和data数据的目录,再在对应的目录下编写配置文件:
systemLog:
#MongoDB发送所有日志输出的目标指定为文件
#The path of the log file to which mongod or mongos should send all diagnostic logginginformation
destination: file
#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径
path: "/home/zc/mongodb/sharded_cluster/myshardrs01_27018/log/mongod.log"
#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。
logAppend: true
storage:
#mongod实例存储其数据的目录。storage. dbPath设置仅适用于mongod。
#打The directory where the mongod instance stores its data.Default value is "/data/db".
dbPath: "/home/zc/mongodb/sharded_cluster/myshardrs01_27018/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#配置pid文件路径
pidFilePath: "/home/zc/mongodb/sharded_cluster/myshardrs01_27018/log/mongod.pid"
net:
#服务实例绑定的IP,默认是loca7host
bindIp: localhost,192.168.37.136
port: 27018
#配置副本集
replication:
#副本集的名称
replSetName: myshardrs01
sharding:
#分片角色
clusterRole: shardsvr
多了一个分片的角色属性,该属性指定该副本集在分片中承担什么样的角色:
配置完成之后都启动起来
2.搭建配置副本集
步骤与搭建存储副本集一致,但是副本集名称以及分片角色需要发生变动…
mongod.conf文件:
systemLog:
#MongoDB发送所有日志输出的目标指定为文件
#The path of the log file to which mongod or mongos should send all diagnostic logginginformation
destination: file
#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径
path: "/home/zc/mongodb/sharded_cluster/myshardrs01_27018/log/mongod.log"
#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。
logAppend: true
storage:
#mongod实例存储其数据的目录。storage. dbPath设置仅适用于mongod。
#打The directory where the mongod instance stores its data.Default value is "/data/db".
dbPath: "/home/zc/mongodb/sharded_cluster/myshardrs01_27018/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#配置pid文件路径
pidFilePath: "/home/zc/mongodb/sharded_cluster/myshardrs01_27018/log/mongod.pid"
net:
#服务实例绑定的IP,默认是localhost,表示允许主机连接
bindIp: localhost,192.168.37.136
port: 27018
#配置副本集
replication:
#副本集的名称
replSetName: myshardrs01
sharding:
#分片角色
clusterRole: configsvr
配置完成之后都启动起来
3.初始化各个副本集
通过:
rs.initiate()
rs.add()
rs.addArb()
命令初始化各个副本集(在主节点上启用)
其中用于存储的副本集需要添加副节点和仲裁节点,而配置副本集只需要添加副节点即可
可以通过
rs.conf()
rs.status()
查看副本集的状态
路由节点不需要存放数据,所以只需要配置一个日志存放目录:
第一步:准备存放数据和日志的目录:
mkdir -p /mongodb/sharded_cluster/mymongos_27017/log
mymongos_27017节点:
新建或修改配置文件:
vi /mongodb/sharded_cluster/mymongos_27017/mongos.conf
配置文件如下:
systemLog:
#MongoDB发送所有日志输出的目标指定为文件
#The path of the log file to which mongod or mongos should send all diagnostic logginginformation
destination: file
#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径
path: "/home/zc/mongodb/sharded_cluster/mymongos_27017/log/mongod.log"
#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。
logAppend: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#配置pid文件路径
pidFilePath: "/home/zc/mongodb/sharded_cluster/mymongos_27017/log/mongod.pid"
net:
#服务实例绑定的IP,默认是localhost,表示允许主机连接
bindIp: localhost,192.168.37.136
port: 27017
sharding:
#指定配置节点副本集
configDB: myconfigrs/192.168.37.136:27019,192.168.37.136:27119,192.168.37.136:27219
不同:
第二步:启动路由节点
在bin目录下,使用mongos启动服务:
./mongos -f ../sharded_cluster/mymongos_27017/mongod.conf
启动后可以进入路由节点试着写入数据:
会发现报错:
提示我们没有找到分片,因为当前我们在配置文件中只指定了配置节点副本集,没有指定分片
第三步:添加分片
通过命令添加分片,语法:
sh.addShard("IP:Port")
多个需要指定: 分片名/host1,host2,host3
因为我们副本集中有多个节点,所以我们需要指定副本集和它对应的节点:
sh.addShard ( "myshardrs01/192.168.37.136:27018,192.168.37.136:27118,192.168.37.136:27218")
通过:
sh.status()
就可以查看分片的状态了:
开启分片功能:
sh.enableSharding("库名")
sh.shardCollection("库名.集合名","key":1))
也就是先确定对那个数据库进行分片–>再确定对数据库中的那个表进行分片–>分片规则是什么?
例如:对articledb中的comment集合进行分片,分片策略为:根据nickname进行hashed策略(哈希策略,随机分配均匀)
sh.enableSharding("articledb")
sh.shardCollection("articledb.comment",{"nickname":"hashed"})
数据库可以不存在,可以先开启数据的分片功能
最后的结果可以通过sh.status()
看到:
例如:给aricledb中的author集合进行根据age的范围策略:
sh.enableSharding("articledb")
sh.shardCollection("articledb.author",{"age":1})
注:一个集合只能有一个分片策略
分片规则一:哈希规则
对于基于哈希的分片,MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块.
在使用基于哈希分片的系统中,拥有"相近"片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些
分片规则二:范围策略
对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分.假设有一个数字的片键:想象一个从负无穷到正无穷的直线每一个片键的值都在直线上画了一个点.MongoDB把这条直线划分为更短的不重叠的片段,并称之为数据块,每个数据块包含了片键在一定范围内的数据.
在使用片键做范围划分的系统中,拥有"相近"片键的文档很可能存储在同一个数据块中,因此也会存储在同一个分片中.
指定为后就可以登录mongos进行插入数据的测试了
注意:连接时连接的时路由节点。两个路由节点任选其一就可以了
编写yml配置文件:
spring:
data:
mongodb:
#连接副本集需要使用uri连接:
#uri: mongodb://192.168.37.136:27017,192.168.37.136:27018,192.168.37.136:27019/articledb?connect=replicaset&slaveok=true&replicaset=myrs
#连接路由字符串
uri: mongodb://192.168.37.136:27017,192.168.37.136:27117/articledb
这里将多个路由节点的host都写上去,spring会内部通过负载均衡的方法来选择一个路由节点进行连接
默认情况下,MongoDB实例启动运行时是没有启用用户访问权限控制的,也就是说,在实例本机服务器上都可以随意连接到实例进行各种操作,MongoDB不会对连接客户端进行用户验证,这是非常危险的。
为了能保障mongodb的安全可以做以下几个步骤:
1.单例情况
启动单例的mongdb数据库:
注:先切换到admin库,之后再创建用户
例如:创建系统超级用户myroot,设置密码123456,设置角色root
db.createUser({user: "myroot" , pwd : "123456" , roles : ["root"]})
创建专门用来管理admin库的账号myadmin,只用来作为用户权限的管理
db.createUser({user:"myadmin",pwd:"123456",roles :[{role:"userAdminAnyDatabase", db:"admin"}]})
可以通过:
db.system.users.find()
查看当前创建了哪些用户
可以通过:
db.dropUser()
来删除用户
通过:
db.changeUserPassword("name","new_password")
修改用户密码
可以通过
db.auth("name","password")
进行认证测试
创建普通用户可以在没有开启认证的时候添加,也可以在开启认证之后添加,但开启认证之后,必须使用有操作admin库的用户登录认证后才能操作。底层都是将用户信息保存在了admin数据库的集合system.users中。
创建用户,拥有articledb数据库的读写权限readwrite,密码是123456
db.createUser({user: "bobo",pwd: "123456",roles:[{role:"readWrite",db:"articledb"}]})
有两种方式开启权限认证启动服务∶一种是参数方式,一种是配置文件方式。
1)参数方式
在启动时指定参数--auth
,如:
/usr/local/mongodb/bin/mongod -f /mongodb/single/mongod.conf --auth
2)配置文件方式
在mongod.cont配置文件中加入:
vim /mongodblsingle/mongod.conf
在mongod.conf配置文件中加入:
vim /mongodb/single/mongod.conf
security:
#开启授权认证
authorization: enabled
启动时可不加–auth参数:
/usr/local/mongodb/bin/mongod -f /mongodb/single/mongod.conf
在进入mongodb后,切换到admin数据库后可以通过:
db.auth("name","password")
登录超级管理员用户
通过超级管理员用户可以看到所以的数据库,因为其有着最大的权限.
如果登录的是普通用户,那就只能看到自己管理的一个库
连接方法与之前相同,只不过需要指定用户和密码
需要编写yml文件:
spring:
data:
mongodb:
#连接副本集需要使用uri连接:
#uri: mongodb://192.168.37.136:27017,192.168.37.136:27018,192.168.37.136:27019/articledb?connect=replicaset&slaveok=true&replicaset=myrs
#连接路由字符串
# uri: mongodb://192.168.37.136:27017,192.168.37.136:27117/articledb#
# 配置安全配置
# host: 192.168.37.136
# database: articledb
# port: 27017
# username: bobo
# password: 123456
#也可以使用uri进行连接
uri: mongodb://bobo:[email protected]:27017/articledb
步骤:
(1)启动副本集
启动步骤与之前的步骤相同
(2)在头节点创建超级管理员账户
(3)创建副本集认证的key文件
第一步:生成一个key文件到当前文件夹中。
可以使用任何方法生成密钥文件。例如,以下操作使用openssl生成密码文件,然后使用chmod来更改文件权限,仅为文件所有者提供读取权限:
openssl rand -base64 90 -out ./mongo.keyfile
chmod 400 ./mongo.keyfile
ll mongo.keyfile
注意:
(4)修改配置文件指定key文件
分别编辑几个服务的mongod.conf文件,添加相关内容:
security:
#KeyFile鉴权文件
keyFile: /home/zc/mongodb/myres_27017/mongo.keyfile
#开启认证方式运行
authorization: enabled
(5)重新启动服务
如果副本集是开启状态,则先分别关闭关闭复本集中的每个mongod,从次节点开始。直副本集的所有成员都离
线,包括任何仲裁者。主节点必须是最后一个成员关闭以避免潜在的回滚。
在登录后,再登录主节点启动认证:
use admin
db.auth("myroot","123456")
这样就可以启动服务了,但是我们一般是用普通用户的服务,所以我们需要再创建一个普通用户:
db.createUser({user:"bobo",pwd: "123456",roles:[{role:"readWrite",db:"articledb"}]})
(6)外部连接
外部连接方式与之前的连接副本集方法相同,唯一不同的就是uri前面需要指定用户名和密码
mongodb://bobo:[email protected]:27017,192.168.37.136:27018,192.168.37.136:27019/articledb?connect=replicaset&slaveok=true&replicaset=myrs
集群的安全认证=副本集安全认证+单个节点安全认证
在这里不过多赘述,只需要按照之前的步骤创建即可