闭关修炼180天----吐血整理MongoDB的学习笔记
MongoDB
一、MongoDB体系结构
1.1 mongoDB和NoSql
mongoDB是一种NoSql,是文档存储的代表。
mongoDB是一个基于分布式文件存储的数据库。为web应用提供可扩展,高性能,易部署的数据存储解决方案。
mongoDB是最像关系型数据库的,在高负载场景下,通过添加更多的节点,可以保证服务器的性能。
NoSql数据库的四大家族:列存储Hbase,键值存储redis,图像存储Neo4j,文件存储mongodb
1.2 mongoDB体系结构
MongoDB -> MongoDB-database -> Collection -> document(fileid1,fileid2)
Mysql->mysql.database->tables->row(column1,column2)
1.3 mongoDB和Rdbms(关系型数据库)对比
关系型数据库 | MongoDB |
---|---|
database(数据库) | database(数据库) |
table(表) | collection(集合) |
row(行) | document(bson文档) |
column(列) | field(字段) |
index(唯一索引、主键索引) | index (支持地理位置索引、全文索引 、哈希索引) |
join(主外键关联) | embedded Document (嵌套文档) |
primary key(指定主键) | primary key (指定_id field做为主键,不指定会自动生成) |
1.4什么是Bson
- Bson是一种类似于json的二进制存储格式(相对于json会浪费一些空间)
- Bson存储结构:{key:value,key2:value2},其中key是字符串类型,value的类型是字符串,double,Array,ISODate等。
- BSON有三个特点:轻量级、可遍历性,高效性。
1.5BSON在MongoDB中的使用
MongoDB使用了BSON这种结构来存储数据和网络数据交换。把这种格式转化成一文档这个概念
(Document),这里的一个Document也可以理解成关系数据库中的一条记录(Record),只是这里的
Document的变化更丰富一些,如Document可以嵌套。
1.6 MongoDB在linux中的安装
登录网址:
https://www.mongodb.com/
下载社区版的mongodb4.1.3tgz包,上传到linux虚拟机中。解压:
tar -zxvf MongoDB-linux-x86_64-4.1.3.tgz
启动:
./bin/mongod
[可能会启动失败,通过创建mkdir -p /data/db,查看mongo进程:ps -ef | grep mongo
]指定配置文件方式的启动:
./bin/mongod -f mongo.conf
[创建完conf文件后使用此命令启动可能还会失败,原因是没有创建数据库目录/data/mongo,需要先mkdir /data/mongo
,然后再使用以上命令进行配置文件方式启动]# 配置文件的样例
dbpath=/data/mongo/ # 数据库目录
port=27017 #端口号
bind_ip=0.0.0.0 #设定可以绑定访问的ip,现在是任意都可以
fork=true #开启后台启动
logpath = /data/mongo/MongoDB.log # 日志文件路径
logappend = true # 是否以追加方式产生日志
auth=false # 是否授权安全认证
1.7 mongo shell的启动
- 启动mongo shell:
./bin/mongo
(默认连接的是27017端口的) - 指定主机和端口的方式启动:
./bin/mongo --host=主机ip --port=端口
1.8 mongodb GUI工具
- MongoDB Compass Community
- NoSQLBooster(mongobooster) - [如果连接不上可能是端口没有开放]
开启指定端口的命令:
- 开启防火墙:
systemctl start firewalld
- 开放指定的端口:
firewall-cmd --zone=public --add-port=27017/tcp --permanent
- --zone:作用域
- --add-port=1935/tcp #添加端口,格式为:端口/通讯协议
- --permanent #永久生效,没有此参数重启后失效
- 重启防火墙:
firewall-cmd --reload
- 查看端口号:
netstat -ntlp
,查看27017端口的使用情况:netstat -ntulp | grep 1935
二、MongoDB命令
2.1 MongoDB的基本操作
- 查看数据库:
show dbs;
- 切换/使用指定的数据库:
usr 数据库名称;
- 创建集合:
db.createCollection("集合名");
- 查看集合:
show tables;
或者show collections;
- 删除集合:
db,集合名.drop();
- 删除当前数据库:
db.dropDatabase();
2.2MongoDB集合数据操作(CURD)
2.2.1 数据添加
插入单条数据:db.collection-name.insert(document)。
例子:db.lagou.insert({name:"zae",salary:12,birthday:new ISODate("1996-01-01")})
插入多条数据:db.collection-name.insert([document1,document2])
插入数据时,_id字段系统会自动生成,我们也可以指定,默认生成的格式为:
- 前四个字节为时间戳,可以通过ObjectId("对象id字符串").getTimestamp()来获取
- 接下来的3个字节是机器码标识
- 紧接的两个字节由进程id组成-PID
- 最后三个字节是随机数
2.2.2 数据查询
比较条件查询:db.collection-name.find(条件)
全查:db.col-name.find()
等值条件:db.col-name.find({key:value}).pretty()
大于条件:db.col-name.find({key:{$gt:value}}).pretty()
小于条件:db.col-name.find({key:{$lt:value}}).pretty()
大于等于条件:db.col-name.find({key:{$gte:value}}).pretty()
小于等于条件:db.col-name.find({key:{$lte:value}}).pretty()
不等于条件:db.col-name.find({key:{$ne:value}}).pretty()
逻辑条件查询
- and条件:db.col-name.find({key1:value1,key2:value2}).pretty()
- or条件:db.col-name.find({$or:[{key1:value1},{key2:value2}]}).pretty()
- not条件:db.col-name.find({key:{$not:{$操作符:value}}}).pretty()
分页查询
db.col-name.find({条件}).sort({排序字段:排序方式}).skip(跳过的行数).limit(一页显示多少数据)
2.2.3 数据更新 调用update
$set # 设置字段值
$unset #删除指定字段
$inc #对修改的值进行自增
# 语法结构
db.col_name.update(
<query>, # 查询条件
<update>, #更新操作
{
upsert:<boolean>, #如果更新的是不存在的update记录,是否新增进去,默认是false不新增
multi:<boolean>,# 默认false,只更新找到的第一条数据,如果改为true,会将查询到的多条数据都更新
writeConcern:<document># 指定为写的行为是否需要确认
}
)
# 举例
db.col_name.update(
{条件},
{$set:{key:value}},
{
multi:true
}
)
2.2.4 数据删除
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
参数说明:
query :(可选)删除的文档的条件。
justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值
false,则删除所有匹配条件的文档。
writeConcern :(可选)用来指定mongod对写操作的回执行为。
2.2.5 CURD案例演示
# 插入单条数据
db.lg_resume_preview.insert({name:"zae",birthday:new ISODate("2000-09-21"),salary:15000,city:'bj'});
# 插入多条数据
db.lg_resume_preview.insert([
{name:"许嵩",birthday:new ISODate("2000-09-21"),salary:18000,city:'bj'},
{name:"徐良",birthday:new ISODate("2000-09-21"),salary:10000,city:'bj'}]);
#查询所有数据
db.lg_resume_preview.find();
#等于
db.lg_resume_preview.find({name:"汪苏泷"});
db.lg_resume_preview.find({salary:{$eq: 15000}});
#大于
db.lg_resume_preview.find({salary:{$gt: 15000}});
#小于
db.lg_resume_preview.find({salary:{$lt: 15000}});
#不等于
db.lg_resume_preview.find({salary:{$ne: 15000}});
#and
db.lg_resume_preview.find({name:"zae",salary:15000});
#or
db.lg_resume_preview.find({$or:[{name:"许嵩"},{salary:15000}]});
#not
db.lg_resume_preview.find({name:{$not:{$eq:"薛之谦"}}});
# 在lg_resume_preview集合中查询出数据按照salary属性进行正序排序,跳过2条后,截取两条
db.lg_resume_preview.find().sort({ salary:1 }).skip(2).limit(2);
# 修改salary=18000的数据,salary为28000,只修改查询到的第一条
db.lg_resume_preview.update(
{salary:18000},
{$set:{salary:28000}},
{ multi: false, upsert: false}
);
# 查询出name=李白的数据,并将name设置为杜甫,salary设置为800。没有该条数据时执行插入新数据
db.lg_resume_preview.update(
{name:"李白"},
{$set:{name:"杜甫",salary:800}},
{ multi: false, upsert: true}
);
# 给name为杜甫的数据里面的salary值增加800
db.lg_resume_preview.update(
{name:"杜甫"},
{$inc:{salary:800}},
{ multi: false, upsert: false}
);
# 移除name = 杜甫的那条数据的salary字段
db.lg_resume_preview.update(
{name:"杜甫"},
{$unset:{salary:""}},
{ multi: false, upsert: false}
);
# 忘记操作符,会将那条数据其余字段全删掉,只剩下要更新的那个字段
db.lg_resume_preview.update(
{name:"杜甫"},
{salary:"19000"},
{ multi: false, upsert: false}
);
# 删除某个条件查询出的整条数据
db.lg_resume_preview.remove({salary:"19000"}, {justOne: true})
2.3 MongoDB聚合操作
2.3.1 聚合操作简介
聚合是MongoDB的高级查询语言,它允许我们通过转化合并由多个文档的数据来生成新的在单个文档
里不存在的文档信息。一般都是将记录按条件分组之后进行一系列求最大值,最小值,平均值的简单操
作,也可以对记录进行复杂数据统计,数据挖掘的操作。聚合操作的输入是集中的文档,输出可以是一
个文档也可以是多个文档
2.3.2 mongoDB聚合操作分类
- 单目的聚合操作
- 聚合管道
- MapReduce编程模型
2.3.3 单目的聚合操作
- 统计个数:
db.col_name.find({}).count()
- 去重:
db.col_name.distinct(field,query,options)
2.3.4 聚合管道
语法:db.col_name.aggregate([{},{}......])
mongoDB中聚合主要用于统计数据(诸如统计平均值,求和等),并返回计算的结果。
表达式 | 描述 |
---|---|
$sum | 计算总和 |
$avg | 计算平均值 |
$min | 获取集合中所有文档对应值的最小值 |
$max | 获取集合中所有文档对应值的最大值 |
$push | 在结果文档中插入值到一个数组中 |
$addToSet | 在结果文档中插入值到一个数组中,但数据不重复 |
$first | 根据资源文档的排序获取第一个文档数据 |
$last | 根据资源文档的排序获取最后一个文档数据 |
聚合框架中管道常用的一些操作:(注:管道就是将上一步的操作交给下一步)
表达式 | 说明 |
---|---|
$group | 将集合中的文档分组,可用于统计结果 |
$project | 修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。 |
$match | 用于过滤数据,只输出符合条件的文档。 |
$limit | 用来限制MongoDB聚合管道返回的文档数。 |
$skip | 在聚合管道中跳过指定数量的文档,并返回余下的文档 |
$sort | 在输入文档排序后输出 |
$geoNear | 输出接近某一地理位置的有序文档 |
演示案例:
# 按照city进行分组,并统计每组城市出现的次数
db.lg_resume_preview.aggregate([{$group:{_id:"$city",city_count:{$sum:1}}}]);
# 按照city进行分组,并统计每组城市里面薪资的平均值
db.lg_resume_preview.aggregate([{$group:{_id:"$city",salary_avg:{$avg:"$salary"}}}]);
# 按照city进行分组,将结果放在一个数组中
db.lg_resume_preview.aggregate([{$group: { _id: "$city",city_name:{$push: "$city"}}}]);
# 按照city进行分组,将结果放在一个数组中,且结果数据不重复
db.lg_resume_preview.aggregate([{$group: { _id: "$city",city_name:{$addToSet: "$city"}}}]);
# 先进行分组,分组处理后的结果的avgSal名称修改为salary
db.lg_resume_preview.aggregate(
[{$group : {_id: "$city", avgSal:{$avg:"$salary"}}},
{$project : {city: "$city", salary : "$avgSal"}}
]);
#根据城市进行分组获取每个城市的个数,然后将个数大于3的记录统计出来
db.lg_resume_preview.aggregate([{$group: { _id: "$city",city_count:{$sum: 1}}},
{$match: {city_count:{$gt:3}}}]);
2.3.5 MapReduce编程模型
概念:MapReduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结
果合并成最终结果(REDUCE)
案例演示
db.lg_resume_preview.mapReduce(
function(){emit(this.city,this.salary);}, #map模块,定义key - value,并传递给下一代码块
function(key,value){ # reduce模块,接收到key,value,自动根据key分组,处理返回value的平均值
return Array.avg(value)
},
{ #参数模块
query:{salary:{$gt:15000}}, # query定义条件:例子中是只有salary大于15000的才能进入到此map中
out:"avgSalary", #out存放结果集,结果集字段为avgSalary
finalize:function(key,value){ # finalize对reduce输出的结果再一次的进行修改
return value+100;
},
verbose:true #verbose结果信息中是否包含时间信息,默认为false
#sort: 和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
#limit: 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)
})
三、MongoDB索引index
3.1 什么是索引
索引是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单,能大大提高查询效率。
3.2 索引类型
3.2.1单键索引
概念:单键索引就是指的在一个字段上加的索引。
语法:db.col_name.createIndex({"字段名":排序方式})
(其中1是升序,-1是降序)
特殊的单键索引 ---- 过期索引TTL:TTL索引是mongodb中一种特殊的索引,可以支持文档在一定时间之后自动过期删除,TTL索引只能在单字段上建立,并且字段类型必须是日期类型。 语法:db.co_name.createIndex({"日期字段":排序方式},{expireAfterSeconds:秒数})
演示案例:
db.createCollection("lg_test_index");
db.lg_test_index.insert({name:"张三丰",city:"上海"});
db.lg_test_index.insert({name:"张无忌",city:"洛阳",birthday:new ISODate("2020-12-31")});
# 给name创建单键索引
db.lg_test_index.createIndex({name:1});
# 给birthday创建过期索引TTL,设置过期时间为15秒,15秒之后,张无忌的那条数据会自动删除
db.lg_test_index.createIndex({birthday:1},{expireAfterSeconds:15});
# 获取所有索引
db.lg_test_index.getIndexes();
db.lg_test_index.find();
# 创建唯一索引
db.lg_test_index.createIndex({name:1},{unique,true});
3.2.2 复合索引
概念:在多个字段添加的索引(注意字段的顺序和索引方向)
语法:db.col_name.createIndex({"字段名1":排序方式},{"字段名2":排序方式})
官方详细介绍:https://www.mongodb.com/ -> Docs -> Server -> indexes
注意:如果使用字段名1和字段名2作为条件查询,索引生效;如果只使用字段名1作为条件查询,索引会生效,原因是字段名1在最左侧;如果只使用字段名2作为条件查询,索引不生效。
3.2.3 多键索引
概念:针对一个数组字段创建的索引
语法:db.col_name.createIndex({loc:排序方式})
3.2.4 地理空间索引
概念:针对地理空间坐标创建索引
语法:db.col_name.createIndex({数组的字段名:"2dsphere"或者"2d"})
- 2dsphere索引,用于存储和查找球面上的点
- 2d索引,用于存储和查找平面上的点
演示案例
db.company.insert(
{
# 创建该条数据的坐标位置
loc : { type: "Point", coordinates: [ 116.482451, 39.914176 ] },
name: "大望路地铁",
category : "Parks"
}
)
# 给loc创建地理位置坐标索引
db.company.ensureIndex( { loc : "2dsphere" } )
#参数不是1或-1,为2dsphere 或者 2d。还可以建立组合索引。
#查询距离坐标[116.482451,39.914176]0.05公里以内的数据
db.company.find({
"loc" : {
"$geoWithin" : {
"$center":[[116.482451,39.914176],0.05]
}
}
})
3.2.5 全文索引
概念:对文本内容进行查询。假设一个字段的内容是一段文本,可以给该字段建立全文索引,根据里面的文本内容进行关键字查询。一个集合仅支持建立一个全文索引。
语法:db.col_name.createIndex({field,"text"})
查询语法:db.col_name.find({"$text":{"$search":"条件值"}})
案例演示:
# 插入测试数据
db.content_test.insert({id:1,name:"z1",des:"hello zae"});
db.content_test.insert({id:2,name:"z2",des:"hello vae"});
db.content_test.insert({id:3,name:"z3",des:"but zae is not vae"});
# 查询下所有
db.content_test.find();
# 给des创建全文索引
db.content_test.createIndex({des:"text"});
# 根据des的value值的文本内容进行查询
db.content_test.find({"$text": {$search:"zae"}})
3.2.6 哈希索引
概念:针对属性的hash值进行索引查询,当使用hash索引时,程序能自动计算所需要的hash值。hash index只支持等于查询,不支持范围查询
语法:db.col_name.createIndex({"字段":"hashed"})
注意:哈希索引仅仅支持单字段,不支持同时写入组合索引中。
3.3 索引和explain分析
3.3.1 索引管理
- 创建索引并在后台运行:
db.col_name.createIndex({"字段",排序方式},{background,true})
- 获取针对某个集合的索引:
db.col_name.getIndexes()
- 索引的大小:
db.col_name.totalIndexSize
- 索引的重建:
db.col_name.reIndex()
- 索引的删除:(注意:_id对应的索引时删除不了的)
- 删除单个索引:db.col_name.dropIndex("INDEX-NAME")
- 删除全部的索引:db.col_name.dropIndexes()
3.3.2 explain分析
语法:db.col_name.find({}).explain()
explain()可以接收的参数:queryPlanner,executionStats,allPlansExecution
executionStats返回逐层分析:
第一层,executionTimeMillis最为直观explain返回值是executionTimeMillis值,指的是这条语句的执行时间,这个值当然是希望越少越好。
其中有3个executionTimeMillis,分别是:
executionStats.executionTimeMillis 该query的整体查询时间。
executionStats.executionStages.executionTimeMillisEstimate 该查询检索document获得数据的时间。
executionStats.executionStages.inputStage.executionTimeMillisEstimate 该查询扫描文档 index所用时间。
第二层,index与document扫描数与查询返回条目数 这个主要讨论3个返回项 nReturned、totalKeysExamined、totalDocsExamined,分别代表该条查询返回的条目、索引扫描条目、文档扫描条目。 这些都是直观地影响到executionTimeMillis,我们需要扫描的越少速度越快。 对于一个查询,
我们最理想的状态是:nReturned=totalKeysExamined=totalDocsExamined
第三层,stage状态分析 那么又是什么影响到了totalKeysExamined和totalDocsExamined?是stage的类型。
类型列举如下:
COLLSCAN:全表扫描
IXSCAN:索引扫描
FETCH:根据索引去检索指定document
SHARD_MERGE:将各个分片返回数据进行merge
SORT:表明在内存中进行了排序
LIMIT:使用limit限制返回数
SKIP:使用skip进行跳过
IDHACK:针对_id进行查询
SHARDING_FILTER:通过mongos对分片数据进行查询
COUNT:利用db.coll.explain().count()之类进行count运算
TEXT:使用全文索引进行查询时候的stage返回
PROJECTION:限定返回字段时候stage的返回
对于普通查询,我希望看到stage的组合(查询的时候尽可能用上索引):
Fetch+IDHACK
Fetch+IXSCAN
Limit+(Fetch+IXSCAN)
PROJECTION+IXSCAN
SHARDING_FITER+IXSCAN
不希望看到包含如下的stage:
COLLSCAN(全表扫描)
SORT(使用sort但是无index)
COUNT 不使用index进行count)
3.4慢查询分析
- 开启内置的查询分析器,记录读写操作效率。
db.setProfilingLevel(n,m)
,n的值可选0,1,2(一般情况设置为1)- 0表示不记录
- 1表示记录慢速操作,如果值为1,m必须赋值单位为ms,用于定义慢查询时间的阈值。
- 2表示记录所有的读写操作
- 查询监控结果:
db.system.profile.find().sort({millis:-1}).limit(3)
[显示最慢的三个操作] - 分析慢速查询:
- 解读explain结果,确定是否缺少索引。
3.5 MongoDB索引底层实现原理分析
MongoDB使用B-树,所有节点都有Data域,只要找到指定索引就可以进行访问,
单次查询从结构上来看要快于MySql。
B-树是一种自平衡的搜索树,形式很简单,B-数的特点:
- 多路,非二叉树
- 每个节点即保存数据,又保存索引
- 搜索时,相当于二分查找
B+树是B-树的变种,特点如下:
- 多路非二叉
- 只有叶子结点保存数据
- 搜索时,也相当于二分查找
- 增加了相邻的叶子结点指针
四、MongoDB应用实战
4.1 MongoDB的适用场景
- 网站数据
- 缓存
- 大尺寸、低价值的数据
- 高伸缩性的场景
- 用于对象以及JSON数据的存储
4.2 MongoDB的行业具体应用场景
- 游戏场景
- 物流场景
- 社交场景
- 物联网场景
- 直播
4.3 如何选择是否使用MongoDB
- 应用不需要事务及复杂的join支持(必须满足这条才能使用MongoDB)
- 需求会变,数据模型无法确定
- 应用需要TB甚至是PB的数据存储
- 应用需要2000-3000以上的QPS
- 应用发展迅速,需要能快速水平扩展
- 应用要求存储的数据不丢失
- 应用需要99.999%的高可用
- 应用需要大量的地理位置查询、文本查询。
注意:第一条必须满足,剩余几条满足其一可以使用,满足其二的话使用mongoDB就是十分好的选择。
4.4 Java访问MongoDB
引入依赖:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.10.1</version>
</dependency>
案例演示:
// 演示插入数据
public class InsertDataTest {
public static void main(String[] args) {
// 获取mongodb连接的客户端
MongoClient mongoClient = new MongoClient("192.168.1.130", 27017);
// 获取数据库
MongoDatabase lagouDataBase = mongoClient.getDatabase("lagou");
// 获取集合
MongoCollection<Document> collection = lagouDataBase.getCollection("lg_resume_preview");
// 创建document对象
Document document = Document.parse("{name:'苏东坡',city:'开封',salary:200,birthDay:new ISODate('1997-12-31')}");
// 执行插入
collection.insertOne(document);
}
}
// 演示查询数据
public class FindDataTest {
public static void main(String[] args) {
// 获取mongodb连接的客户端
MongoClient mongoClient = new MongoClient("192.168.1.130", 27017);
// 获取数据库
MongoDatabase lagouDataBase = mongoClient.getDatabase("lagou");
// 获取集合
MongoCollection<Document> collection = lagouDataBase.getCollection("lg_resume_preview");
// 指定按照salary倒叙排序
Document sortDocument = Document.parse("{salary:-1}");
// 获取全部数据
FindIterable<Document> documents = collection.find().sort(sortDocument);
for (Document document : documents) {
System.out.println(document);
}
}
}
// 演示过滤数据
public class FilterDataTest {
public static void main(String[] args) {
// 获取mongodb连接的客户端
MongoClient mongoClient = new MongoClient("192.168.1.130", 27017);
// 获取数据库
MongoDatabase lagouDataBase = mongoClient.getDatabase("lagou");
// 获取集合
MongoCollection<Document> collection = lagouDataBase.getCollection("lg_resume_preview");
// 指定按照salary倒叙排序
Document sortDocument = Document.parse("{salary:-1}");
// 使用filters指定过滤条件进行查询
FindIterable<Document> documents = collection.find(Filters.gt("salary",10000)).sort(sortDocument);
for (Document document : documents) {
System.out.println(document);
}
}
}
4.5 Spring访问MongoDB
引入依赖
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
配置文件编写:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">
<mongo:db-factory id="mongoDbFactory" client-uri="mongodb://192.168.1.130:27017/lagou"></mongo:db-factory>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg index="0" ref="mongoDbFactory"></constructor-arg>
</bean>
<context:component-scan base-package="com.lagou"></context:component-scan>
</beans>
MongoTemplate的运用
@Repository("resumeDao")
public class ResumeDaoImpl implements ResumeDao {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 插入一条数据
* @param resume
*/
public void insertResume(Resume resume) {
mongoTemplate.insert(resume,"lg_resume");
}
/**
* 单条件进行查询
* @param name
* @return
*/
public Resume findResumeByName(String name) {
Query query = new Query();
query.addCriteria(Criteria.where("name").is(name));
List<Resume> lgResume = mongoTemplate.find(query, Resume.class, "lg_resume");
return lgResume.isEmpty()?null: lgResume.get(0);
}
/**
* 多条件进行查询
* @param name
* @param salary
* @return
*/
public List<Resume> findByNameAndSalary(String name, Double salary) {
Query query = new Query();
query.addCriteria(Criteria.where("name").is(name).andOperator(Criteria.where("salary").gt(salary)));
List<Resume> resumeList = mongoTemplate.find(query, Resume.class, "lg_resume");
return resumeList;
}
/**
* 更新数据
* @param resume
*/
public void updateResume(Resume resume) {
// 确定更新哪一条数据
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(resume.getId()));
// 更新内容
Update update = new Update();
update.set("name",resume.getName());
update.set("city",resume.getCity());
update.set("salary",resume.getSalary());
update.set("birthday",resume.getBirthday());
UpdateResult updateResult = mongoTemplate.updateFirst(query, update, "lg_resume");
}
/**
* 删除数据
* @param id
*/
public void deleteResume(String id) {
// 确定删除哪一条数据
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(id));
DeleteResult deleteResult = mongoTemplate.remove(query, "lg_resume");
}
}
4.6 Spring Boot访问MongoDB
4.6.1 MongoTemplate 的方式
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
配置文件:application.properties
spring.data.mongodb.host=192.168.1.130
spring.data.mongodb.port=27017
spring.data.mongodb.database=lagou
其余代码编写参考spring访问mongodb中的MongoTemplate的运用
4.6.2 MongoRepository 的方式
pom依赖和配置文件编写如同:4.6.1
在实体上加上注解@Document("集合名")
编写repository接口,继承MongoRepository,使用jpa方式编程,如同使用spring-data-jpa一样
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
// 案例演示
public interface ResumeRepository extends MongoRepository<Resume,String> { List<Resume> findByNameEquals(String name); List<Resume> findByNameAndSalary(String name,double salary);
}
五、MongoDB架构
5.1 MongoDB逻辑结构
层次 | 描述 |
---|---|
最上层 | Client(客户端) |
第二层 | MongoDB Query Language(MongoDB的执行语言) |
第三层 | MongoDB Data Model(MongoDB的数据模型) |
最底层 | MongoDB的存储引擎(WriedTiger,InMemory,MMAPv1) |
5.2 MongoDB的数据模型
5.2.1 描述数据模型
- 内嵌:把相关联的数据保存在一个文档结构中,mongodb的文档结构允许一个字段或者一个数组内的值作为一个嵌套的文档。
- 引用:通过存储数据的引用信息实现两个文档之间的关联,通过解析这些数据引用来访问相关数据。
5.22 如何选择数据模型
- 选择内嵌:
- 数据对象之间有包含关系,数据对象之间有一对多或者一对一的关系
- 数据需要经常一起读取
- 有 map-reduce/aggregation 需求的数据放在一起
- 选择引用
- 数据重复比较多
- 表达比较复杂的多对多的关系
- 大型层次结果数据集,嵌套不要太深
5.3 MongoDB存储引擎
5.3.1 存储引擎概述
5.3.2 WriedTiger存储引擎优势
5.3.3 WriedTiger存储包含的文件和作用
5.3.4 WriedTiger存储引擎实现原理
- 写请求
- checkpoint流程
- Journaling
六、MongoDB集群高可用
6.1 MongoDB主从复制架构原理和缺陷
原理:在主从结构中,主节点的操作记录成为oplog(operation log)。oplog存储在系统数据库local的oplog.$main集合中,这个集合的每个文档都代表主节点上执行的一个操作。从服务器会定期从主服务器中获取oplog记录,然后在本机上执行!对于存储oplog的集合,MongoDB采用的是固定集合,也就是说随着操作过多,新的操作会覆盖旧的操作!
缺陷:主从结构没有自动故障转移功能,需要指定master和slave端,不推荐在生产中使用。
6.2 复制集replica sets(为了解决主从复制的缺陷)
6.2.1 什么是复制集
复制集是一个集群,提供了数据的冗余备份。
复制集是一个改进后的主从。
不会去指定谁是主谁是从,主从会自动切换,保证高可用
6.2.2 为什么要使用复制集
- 高可用
- 灾难恢复:当发生故障时,可以从其他节点恢复,用于备份、
- 功能隔离
6.2.3 复制集集群架构原理
6.2.4 复制集搭建
1.在虚拟机192.168.1.130下新建一个文件夹mongo_cluster,并执行命令tar -zxvf mongodb-linux-x86_64-rhel70-4.2.14.tgz -C mongo_cluster
2.进入到解压后的文件里(和bin同级目录),准备三个conf文件放在这,文件内容如下:
- 主节点配置mongo_37017.conf
# 主节点配置
dbpath=/data/mongo/data/server1
bind_ip=0.0.0.0
port=37017
fork=true
logpath=/data/mongo/logs/server1.log
replSet=lagouCluster
- 从节点1配置mongo_37018.conf
dbpath=/data/mongo/data/server2
bind_ip=0.0.0.0
port=37018
fork=true
logpath=/data/mongo/logs/server2.log
replSet=lagouCluster
- 从节点1配置mongo_37019.conf
dbpath=/data/mongo/data/server3
bind_ip=0.0.0.0
port=37019
fork=true
logpath=/data/mongo/logs/server3.log
replSet=lagouCluster
3.创建好配置文件中涉及到的几个目录,防止启动报错。
- mkdir -p /data/mongo/data/server1
- mkdir -p /data/mongo/data/server2
- mkdir -p /data/mongo/data/server3
- mkdir -p /data/mongo/logs
4.初始化节点配置
启动三个节点:./bin/mongod -f mongo_37017.conf
;./bin/mongod -f mongo_37018.conf
;./bin/mongod -f mongo_37018.conf
然后进入任意一个节点(我这里进入的是37017:./bin/mongo --port=37017
),运行以下命令
# 配置相关信息
var cfg ={"_id":"lagouCluster",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.1.130:37017","priority":10},
{"_id":2,"host":"192.168.1.130:37018"}
]
}
# 初始化配置
rs.initiate(cfg)
# 查看状态
rs.status()
5.节点的动态增删
#增加节点
rs.add("192.168.1.130:37019")
#删除slave 节点
rs.remove("192.168.1.130:37019")
6.复制集操作演示
- 由于在配置相关信息时将37017的优先级提高了,因此PRIMARY主节点为37017,进入到37017下的shell操作页面,依次执行
use lagou_db
,db.createCollection("lagou_tb1")
,db.lagou_tb1.insert({name:"张三",city:"beijing"})
- 进入到37018下,执行
use lagou_db
,db.lagou_tb1.find()
,此时会发现查询报错,原因是没有开启slaveOk,在37018下执行:rs.slaveOk()
,再次查询就可以了。
7.主库切换演示
- 新打开一个命令窗口,输入
ps -ef | grep mongo
,查看正在启动的mongo进程,通过命令kill PID,将37017服务结束,此时会发现37018自动变成了主节点。
6.2.5 复制集成员的配置参数
参数字段 | 类型说明 | 取值 | 说明 |
---|---|---|---|
_id | 复制集标识 | ||
host | 节点的主机名:ip:port | ||
arbiterOnly | 是否为仲裁节点 | ||
priority(权重) | 默认1,是否有资格变成主节点,取值范围0-1000,0永远不会变成主节点。权重越大越可能成为主节点 | ||
hidden | 当权重为0的时候才可以设置 | ||
votes | 是否为投票节点:0不投票 1投票 | ||
slaveDelay | 从库的延迟多少秒 | ||
buildIndexes | 主库的索引,从库也创建,_id索引无效 |
案例演示:
var cfg ={"_id":"lagouCluster",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.1.130:37017","priority":10},
{"_id":2,"host":"192.168.1.130:37018","priority":0},
{"_id":3,"host":"192.168.1.130:37019","priority":5},
{"_id":4,"host":"192.168.1.130:37020","arbiterOnly":true}
]
};
// 重新装载配置,并重新生成集群节点。注意重新装载配置时,需要在当时的主节点下进行配置设置
rs.reconfig(cfg)
//重新查看集群状态
rs.status()
6.2.6 有仲裁节点复制集搭建
- 新建一个37020的conf,重复上面搭建复制集的步骤,启动起来。
- 配置仲裁节点方案一:在当时复制集的主节点下按照6.2.5的案例演示进行重新装载配置。
- 配置仲裁节点方案二:rs.addArb("IP:端口");
rs.addArb("192.168.1.130:37020")
6.3 分片集群Shard Cluster
6.3.1 什么是分片
6.3.2 为什么要分片
6.3.3 分片的原理
6.3.4 分片集群的搭建过程
1.配置 并启动config 节点集群
解压
tar -zxvf mongodb-linux-x86_64-rhel70-4.2.14.tgz
,然后将解压的文件重命名为mongo-shard:mv mongodb-linux-x86_64-rhel70-4.2.14 mongo-shard
cd进入mongo-shard中在bin的同级目录下新建文件夹config,进入config中,新建三个配置文件:config-17017.conf,config-17018.conf,config-17019.conf,将配置文件里面的内容分别配置为以下内容:
# 数据库文件位置
dbpath=config/config1
#日志文件位置
logpath=config/logs/config1.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
port = 17017
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
# 数据库文件位置
bpath=config/config2
#日志文件位置
logpath=config/logs/config.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
port = 17018
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
# 数据库文件位置
dbpath=config/config3
#日志文件位置
logpath=config/logs/config3.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
port = 17019
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
在config文件夹下创建出配置文件所指定的文件夹:
mkdir config1 config2 config3 logs
启动配置节点:
./bin/mongod -f config/config-17017.conf
./bin/mongod -f config/config-17018.conf
./bin/mongod -f config/config-17019.conf
进入任意节点,依次执行以下命令(我这里进入的是17017)
# 进入17017的shell终端
./bin/mongo --port 17017 #使用admin数据库
use admin # 配置config的配置信息
var cfg ={"_id":"configsvr",
"members":[
{"_id":1,"host":"192.168.1.130:17017"},
{"_id":2,"host":"192.168.1.130:17018"},
{"_id":3,"host":"192.168.1.130:17019"}]
}; #初始化下配置
rs.initiate(cfg)
2.配置shard集群
在bin的同级目录下新建shard文件夹,并在shard文件夹下新建两个文件夹:shard1和shard2,这两个文件夹里面将会分别搭建各自的shard复制集群,接下来我只演示shard1的搭建过程,shard2同样,仅仅需要将一些信息修改为sard2即可。其中shard1的复制集端口为:37017到37019,shard2的复制集端口为:47017到47019
在shard1文件夹下新建三个配置文件:shard-37017.conf,shard-37018.conf,shard-37019.conf.配置文件的内容分别如下:
#shard-37017.conf的内容
dbpath=shard/shard1/shard1-37017
bind_ip=0.0.0.0
port=37017
fork=true
logpath=shard/shard1/shard1-37017.log
replSet=shard1
shardsvr=true
#shard-37018.conf的内容
dbpath=shard/shard1/shard1-37018
bind_ip=0.0.0.0
port=37018
fork=true
logpath=shard/shard1/logs/shard1-37018.log
replSet=shard1
shardsvr=true
#shard-37019.conf的内容
dbpath=shard/shard1/shard1-37019
bind_ip=0.0.0.0
port=37019
fork=true
logpath=shard/shard1/logs/shard1-37019.log
replSet=shard1
shardsvr=true
在shard1文件夹下创建出配置文件中涉及到的几个文件夹:
mkdir shard1-37017 shard1-37018 shard1-37019 logs
启动每个mongod,然后进入到其中一个(我进入的是37017),执行以下命令。
# 配置
var cfg ={"_id":"shard1",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.1.130:37017"},
{"_id":2,"host":"192.168.1.130:37018"},
{"_id":3,"host":"192.168.1.130:37019"}
]
};
# 初始化配置
rs.initiate(cfg)
重复以上操作,配置shard2,参考的conf内容以下命令如下:
# 三个conf文件内容
dbpath=shard/shard2/shard2-47017
bind_ip=0.0.0.0
port=47017
fork=true
logpath=shard/shard2/logs/shard2-47017.log
replSet=shard2
shardsvr=true dbpath=shard/shard2/shard2-47018
bind_ip=0.0.0.0
port=47018
fork=true
logpath=shard/shard2/logs/shard2-47018.log
replSet=shard2
shardsvr=true dbpath=shard/shard2/shard2-47019
bind_ip=0.0.0.0
port=47019
fork=true
logpath=shard/shard2/logs/shard2-47019.log
replSet=shard2
shardsvr=true # 集群配置命令
var cfg ={"_id":"shard2",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.1.130:47017"},
{"_id":2,"host":"192.168.1.130:47018"},
{"_id":3,"host":"192.168.1.130:47019"}
]
};
3.配置路由
在bin的同级目录位置,创建文件:
mkdir -p route/logs
进入到route文件夹下,创建配置文件route-27017.conf,内容为:
port=27017
bind_ip=0.0.0.0
fork=true
logpath=route/logs/route.log
configdb=configsvr/192.168.1.130:17017,192.168.1.130:17018,192.168.1.130:17019
启动路由节点:
./bin/mongos -f route/route-27017.conf
(注意这里是mongos而不是mongod)
4.在mongo路由中添加分片节点
#进入shell操作页面
./bin/mongo --port 27017
#查看下分片状态
sh.status()
#添加分片节点
sh.addShard("shard1/192.168.1.130:37017,192.168.1.130:37018,192.168.1.130:37019");
sh.addShard("shard2/192.168.1.130:47017,192.168.1.130:47018,192.168.1.130:47019");
5.开启数据库和集合分片(指定片键)
#为数据库开启分片功能
sh.enableSharding("数据库名")
sh.enableSharding("lagou_resume")
#为指定集合开启分片功能
sh.shardCollection("数据库名.集合名",{"片键字段名如 name":索引说明})
sh.shardCollection("lagou_resume.lagou_resume_datas",{"name":"hashed"})
6.向集合中插入数据测试
use lagou_resume;
for(var i=1;i<= 100;i++){
db.lagou_resume_datas.insert({"name":"test"+i,
salary:(Math.random()*20000).toFixed(2)});
}
7.验证
分别去shard1复制群和shard2复制群中查看是否有数据
七、MongoDB安全认证
7.1 用户相关操作
创建用户
db.createUser(
{
user: "账号",
pwd: "密码",
roles: [
{ role: "角色", db: "安全认证的数据库" },
{ role: "角色", db: "安全认证的数据库" }
]
}
)
修改密码
db.changeUserPassword( 'root' , 'rootNew' );
用户添加角色
db.grantRolesToUser( '用户名' , [{ role: '角色名' , db: '数据库名'}])
验证用户
db.auth("账号","密码")
删除用户
db.dropUser("用户名")
7.2 角色介绍
数据库内置角色
read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问
system.profile
userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限
readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限
root:只在admin数据库中可用。超级账号,超级权限
dbOwner:库拥有者权限,即readWrite、dbAdmin、userAdmin角色的合体各个类型用户对应的角色
数据库用户角色:read、readWrite
数据库管理角色:dbAdmin、dbOwner、userAdmin
集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager
备份恢复角色:backup、restore;
所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、
dbAdminAnyDatabase
超级用户角色:root
这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、
userAdminAnyDatabase)
7.3.单机安全认证
创建管理员用户
# 使用admin管理员数据库
use admin # 创建root用户
db.createUser(
{
user:"root",
pwd:"123456",
roles:[{role:"root",db:"admin"}]
})
给普通数据库创建普通用户
use mydb1
switched to db mydb1
> db
mydb1
> db.createUser({
... user:"zhangsan",
... pwd:"123456",
... roles:[{role:"readWrite",db:"mydb1"}]
... })
> db.createUser({
... user:"lisi",
... pwd:"123456",
... roles:[{role:"read",db:"mydb1"}]
... })
将数据库进程停止,修改conf配置文件,将
auth=flase
修改为auth=true
重新启动mongodb,开始进行安全验证。
# 验证管理员
use admin db.auth("root","root") #验证普通用户
use mydb1 db.auth("zae","123456")
7.4.集群安全认证
进入路由27017创建管理员用户和普通成员用户
关闭所有的节点
#安装psmisc
yum install psmisc
#安装完之后可以使用killall 命令 快速关闭多个进程
killall mongod
生成秘钥文件,并赋予秘钥文件权限
# 在data/mongodb/文件夹在生成秘钥文件testKeyFile.file
openssl rand -base64 756 > data/mongodb/testKeyFile.file # 赋予秘钥文件权限
chmod 600 data/mongodb/testKeyFile.file
.配置节点集群和分片节点集群开启安全认证和指定密钥文件
#开启安全认证
auth=true
keyFile=data/mongodb/testKeyFile.file
在路由配置文件中,设置密钥文件
keyFile=data/mongodb/testKeyFile.file
启动所有的配置节点,分片节点和路由节点,使用路由进行权限验证。
写shell脚本,批量启动
vi startup.sh
./bin/mongod -f config/config-17017.conf
./bin/mongod -f config/config-17018.conf
./bin/mongod -f config/config-17019.conf
./bin/mongod -f shard/shard1/shard1-37017.conf
./bin/mongod -f shard/shard1/shard1-37018.conf
./bin/mongod -f shard/shard1/shard1-37019.conf
./bin/mongod -f shard/shard2/shard2-47017.conf
./bin/mongod -f shard/shard2/shard2-47018.conf
./bin/mongod -f shard/shard2/shard2-47019.conf
./bin/mongos -f route/route-27017.conf
给脚本加执行权限:
chmod +x startup.sh
启动脚本:
./shartup.sh
springboot连接安全认证
spring.data.mongodb.host=192.168.1.130
spring.data.mongodb.port=27017
spring.data.mongodb.database=数据库名
# 注意,这里连接到具体数据库时,需要使用给这个数据库配置的普通成员,不能使用admin的root用户连接
spring.data.mongodb.username=账号
spring.data.mongodb.password=密码
闭关修炼180天----吐血整理MongoDB的学习笔记的更多相关文章
- 一看就懂之吐血整理的 Docker 学习笔记
前言 随着devops的兴起,Docker 近年来越来越火,容器化概念也越来越火.此篇将带你瞬间入门Docker. Docker 概述 Docker 出现的背景 在我们的开发过程中,通常是这样的一种流 ...
- 闭关修炼180天--手写IOC和AOP(xml篇)
闭关修炼180天--手写IOC和AOP(xml篇) 帝莘 首先先分享一波思维导图,涵盖了一些Spring的知识点,当然这里并不全面,后期我会持续更新知识点. 在手写实现IOC和AOP之前(也就是打造一 ...
- 闭关修炼180天--手写持久层框架(mybatis简易版)
闭关修炼180天--手写持久层框架(mybatis简易版) 抛砖引玉 首先先看一段传统的JDBC编码的代码实现: //传统的JDBC实现 public static void main(String[ ...
- MongoDB快速学习笔记
一,下载. XP系统,32位的下载地址: https://www.mongodb.org/dl/win32/i386 例:win32/mongodb-win32-i386-2.0.7.ziphttp: ...
- MongoDB MapReduce学习笔记
http://cnodejs.org/topic/51a8a9ed555d34c67831fb8b http://garyli.iteye.com/blog/2079158 MapReduce应该算是 ...
- MongoDB入门学习笔记之简介与安装配置
一.MongoDB简介 1.文档数据库 MongoDB是一款开源的文档型非关系数据库,具有高性能.高可靠性和自动扩展等特点.MongoDB中的每一条记录是一个文档,其数据存储结构为键/值对,类似JSO ...
- mongodb的学习笔记一(集合和文档的增删改查)
1数据库的增删改查 一.增加一个数据库: use blog-----切换到指定的数据库,如果数据库不存在,则自动创建该数据库(新建的数据库,如果没有存储对应的集合,是不会显示出来的) 二.删除一个数据 ...
- 《深入理解计算机系统》学习笔记整理(CSAPP 学习笔记)
简介 本笔记目前已包含 CSAPP 中除第四章(处理器部分)外的其他各章节,但部分章节的笔记尚未整理完全.未整理完成的部分包括:ch3.ch11.ch12 的后面几小节:ch5 的大部分. 我在整理笔 ...
- MongoDB 基本操作学习笔记
// 查看所有数据库 show dbs // amdin 0.000GB // local 0.000GB // 使用数据库 use admin // switched to db admin // ...
- java学习笔记系列整理说明
java学习笔记系列整理说明 陆陆续续也巩固学习java基础也有一段时间了,这里整理了一些我认为比较的重要的知识点,供自己或者读者以后回顾和学习.这是一个学习笔记系列,有自己的整理重新撰写的部分, ...
随机推荐
- 8.4 ProcessHeap
ProcessHeap 是Windows进程的默认堆,每个进程都有一个默认的堆,用于在进程地址空间中分配内存空间.默认情况下ProcessHeap由内核进行初始化,该堆中存在一个未公开的属性,它被设置 ...
- deque的rotate方法
deque对象支持旋转操作,可以将元素向左或向右循环移动. 例如: from collections import deque dq = deque([1, 2, 3, 4]) dq.rotate(1 ...
- 思维分析逻辑 6 DAY
数据仓库研究 大数据体系 日志采集和传输 数据建模 数据管理 数据应用 数据建模 日志传输(原始数据) ODS(原始数据) 用户基础属性表:imei,prov,city,machine 用户文章下发表 ...
- JS 保姆级贴心,从零教你手写实现一个防抖debounce方法
壹 ❀ 引 防抖在前端开发中算一个基础但很实用的开发技巧,在对于一些高频操作例如监听输入框值变化触发更新之类,会有奇效.除了实际开发,在面试中我们也可能偶遇手写防抖节流的问题,鉴于不同公司考核要求不一 ...
- sensitive word 敏感词(脏词) 如何忽略无意义的字符?达到更好的过滤效果?
忽略字符 说明 我们的敏感词一般都是比较连续的,比如 傻帽 那就有大聪明发现,可以在中间加一些字符,比如[傻!@#$帽]跳过检测,但是骂人等攻击力不减. 那么,如何应对这些类似的场景呢? 我们可以指定 ...
- java 注解结合 spring aop 实现日志traceId唯一标识
MDC 的必要性 日志框架 日志框架成熟的也比较多: slf4j log4j logback log4j2 我们没有必要重复造轮子,一般是建议和 slf4j 进行整合,便于后期替换为其他框架. 日志的 ...
- Maven多模块聚合工程实战
介绍 本文以SpringCloud微服务多模块聚合案例讲解,全程讲解中间涉及的核心知识点并配图加深理解. 更多maven知识点,建议去看<Maven实战>. 创建父工程 新建maven工程 ...
- 将PL/SQL代码封装在灵巧的包中
将代码封装在灵巧的包中 http://www.oracle.com/technetwork/issue-archive/2013/13-jan/o13plsql-1872456.html 绝大多数基于 ...
- 阿里云 SMS 短信 Java SDK 封装
Github & Issues: https://github.com/cn-src/aliyun-sms 官方文档:https://help.aliyun.com/document_deta ...
- Caused by: com.alibaba.druid.pool.DataSourceClosedException: dataSource already closed
报错场景:spring boot+mybatis,线程池执行批量任务.springboot正常启动后,定时任务中数据库查询报错.报错信息如下: 1 Caused by: org.apache.ibat ...