三高:
应用场景:
共同特点:
1、数据量大
2、写入操作频繁
3、价值低的数据,对事务性要求不高
什么时候选择Mongodb
1、应用不需要事务与复杂的join支持
2、新应用,需求会变,数据模型无法确定
3、应用要2-3k以上的读写QPS
4、需要TB或者PB级别的数据存储
5、快速发展,需要快速水平扩展
6、要求存储的数据不丢失
7、需要99.99%的高可用
8、大量的地理位置查询,文本查询
符合一个可以考虑mongodb,符合2个就确定mongodb
MongoDB是一个开源,高性能,无模式的文档型数据库, 简化开发和方便扩展, 是NoSQL数据库产品中的一种 是最像关系型数据库(MySQL)的非关系型数据库.
它支持的数据结构非常松散, 是一种类似于 JSON 的 格式叫BSON, 所以它既可以存储比较复杂的数据类型, 又相当的灵活. MongoDB中的记录是一个文档, 它是一个由字段和值对(field:value)组成的数据结构
MongoDB文档类似于JSON对象, 即一个文档认 为就是一个对象.字段的数据类型是字符型, 它的值除了使用基本的一些类型外, 还可以包括其他文档, 普通数组和文档数组.
DB最小的存储单位就是文档(document)对象。
文档对应关系型数据库的行。
shell默认使用64位浮点型数值。
高性能:
数据的持久性,对嵌入式数据模型的支持减少了数据库系统上的i/o活动。
索引支持更快查询,可以包含来自嵌入式文档和数组的键。
高可用:
bd的复制工具称为副本集(replica set),可提供自动故障转移和数据冗余。
高扩展性:
提供了水平扩展性作为核心功能的一部分。
分片将数据分布在一组集群。
丰富的查询支持:
支持丰富的查询语言,支持读和写操作,比如数据聚合,文本搜索和地理空间查询等。
其他特点:
无模式、灵活的文档模型。
首先创建一个数据缓存的文件夹,一般创建bin文件夹的同级目录 data
在bin目录里面启动cmd
mongod --dbpath =..\data\db
如果启动报错,那么就是db文件没创建,自己去data文件夹创建db文件夹
默认端口27017
使用配置方式
在bin的同级目录中创建conf
创建文件夹mongod.conf
storage:
dbPath: D:\Environment\Mongodb\mongodb-win32-x86_64-windows-6.0.0\data
服务启动后,在bin目录下新创一个cmd窗口
将文章评论的数据存入
选择和创建数据库的语法格式:
use 数据库
没有则创建、有就是切换到该数据库
查看有权限 ,查看所有的数据库命令
show dbs
或者
show databases
db.dropDatabase()
主要用来删除已经持久化的数据库
集合的显示创建
db.createCollection("name")
#查看
show name
show table
集合的隐式创建
向一个集合中 直接插入文档,如果不存在文档,那么就会自动创建
db.collection.drop()
或者
db.集合.drop()
文档的数据结构和JSON基本一样
所有存储在集合中的数据都是Bson格式
1、单个文档插入
使用insert()或者save()
db.collection.insert(
,
{
writeConcern: ,
ordered:
}
)
多个文档插入:
try {
db.comment.insertMany([
{"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上, 健康很重要, 一杯温水幸福你我 他.","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-0805T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"},
{"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水, 冬天喝温开水","userid":"1005","nickname":"伊人憔 悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"},
{"_id":"3","articleid":"100001","content":"我一直喝凉开水, 冬天夏天都喝.","userid":"1004","nickname":"杰克船 长","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"},
{"_id":"4","articleid":"100001","content":"专家说不能空腹吃饭, 影响健康.","userid":"1003","nickname":"凯 撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"},
{"_id":"5","articleid":"100001","content":"研究表明, 刚烧开的水千万不能喝, 因为烫 嘴.","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-0806T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"}
]);
} catch (e) {
print (e);
}
insertMany()而不是insert()
db.comment.find()
或者指定查询
db.comment.find({userid:'1003'})
投影查询:
db.comment.find({userid:"1003"},{userid:1,nickname:1})
db.collection.update(query,update,options)
# 或者
db.collection.update(
,
,
{
upsert:,
multi:,
writeConcern:,
collation:,
arrayFilters: [,...],
hint:
}
)
db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}})
语法结构:
db.集合名词.remove(条件)
#全删
db.comment.remove({})
#要删除id=1的
db.comment.remove({_id:"1"})
db.collection.count(query,options)
比如:
#查询所有:
db.comment.count()
#查询id为3的数据
db.comment.count({userid:"3"})
可以使用limit()
方法来读取指定数量的数据,使用skip()
方法来跳过指定数量的数据。
#查询前面两条
db.commment.find().limit(2)
#跳过前面2条,查询3 4条
db.comment.find().limit(2).skip(2)
sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1与-1来指定排序的方法,其中1为升序,而-1用于降序
db.collection_name.find().sort({key:1})
或者
db.集合名称.find().sort(排序方式)
比如
try {
db.comment.insertMany([
{"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上, 健康很重要, 一杯温水幸福你我 他.","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-0805T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"},
{"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水, 冬天喝温开水","userid":"1005","nickname":"伊人憔 悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"},
{"_id":"3","articleid":"100001","content":"我一直喝凉开水, 冬天夏天都喝.","userid":"1004","nickname":"杰克船 长","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"},
{"_id":"4","articleid":"100001","content":"专家说不能空腹吃饭, 影响健康.","userid":"1003","nickname":"凯 撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"},
{"_id":"5","articleid":"100001","content":"研究表明, 刚烧开的水千万不能喝, 因为烫 嘴.","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-0806T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"}
]);
} catch (e) {
print (e);
}
//删除
db.comment.remove({})
db.comment.find()
//正则表达式
db.comment.find({content:/开水/})
-------比较······
//点赞大于700
db.comment.find({likenum:{$gt:NumberInt(700)}})
//其余
//小于 lt || 大于等于 gte || 小于等于 lte || 不等于 ne
$in
//查询userid包含1002 与1003的文档
db.comment.find({userid:{$in:["1002","1003"]}})
需要查询同时满足两个以上条件,就需要使用$and
操作符进行关联(与sql的and相似)
$and:[{},{},{}]
如:查询评论数大于700小于2k
//查询评论集合中likenunm大于700 小于2k的文档
db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]})
$or
或者
用户id 1003 或者小于2k
db.comment.find({$or:[{userid:"1003"},{likenum:{$lt:NumberInt(2000)}}]})
选择切换数据库: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]}})
或 nin:不包含
db.comment.find({字段名:{$nin:[值1, 值2]}})
条件连接查询:db.comment.find({$and:[{条件1},{条件2}]})
或
db.comment.find({$or:[{条件1},{条件2}]})
索引在 MongoDB 中高效地执行查询.如果没有索引, MongoDB 必须执行全集合扫描.这种扫描全集合的查询效率是非常低的, 特别在处理大量的数据时, 查询可以要花费几十秒甚至几分钟。
如果查询存在适当的索引, MongoDB 可以使用该索引限制必须检查的文档数.
索引是特殊的数据结构, 它以易于遍历的形式存储集合数据集的一小部分 索引存储特定字段或一组字段的值, 按字段值排序.索引项的排序支持有效的相等匹配和基于范围的查询操作.并且, MongoDB 还可以使用索引中的排序返回排序结果.
MongoDB 使用的是 B Tree, MySQL 使用的是 B+ Tree
就是在某一个字段上加一个索引 并且排序 1表示升序 -1表示降序
支持多个字段的用户定义索引,即复合索引。
复合索引汇总列出的字段顺序很重要,比如:复合索引中{userid:1,score:;-1}组成,那么索引就首先安userid正序排序,然后在每个userid的值中按score的降序排列
地理空间索引(Geospatial Index)、文本索引(Text Indexes)、哈希索引(HashedIndexes)
地理空间索引(Geospatial Index)
为了支持对地理空间坐标数据的有效查询, MongoDB 提供了两种特殊的索引: 返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引.
文本索引(Text Indexes)
MongoDB 提供了一种文本索引类型, 支持在集合中搜索字符串内容.这些文本索引不存储特定于语言的停止词(例如 “the”, “a”, “or”), 而将集合中的词作为词干, 只存储根词.
哈希索引(Hashed Indexes)
为了支持基于散列的分片, MongoDB 提供了散列索引类型, 它对字段值的散列进行索引.这些索引在其范围内的值分布更加随机, 但只支持相等匹配, 不支持基于范围的查询
返回一个集合的所有索引的数组
//查看索引
db.collection.getIndexes()
db.comment.getIndexes()
在集合上创建索引
语法:
db.collection.createIndex(keys,options)
常用的options
复合索引的创建:
//创建复合索引
db.comment.createIndex({userid:1,nickname:-1})
db.comment.getIndexes()
可以移除指定的索引,也可以移除所有索引
1、指定索引移除
db.comment.dropIndex(index)
-------------------------------
//根据创建条件删除
db.comment.dropIndex({userid:1})
db.comment.getIndexes()
//根据索引名字删除复合索引
db.comment.dropIndex("userid_1_nickname_-1")
//删除全部
db.comment.dropindexes()
分析查询性能,通常使用执行计划(解释计划,Explain Plan)来查看查询的情况,如查询耗费的时间、是否基于索引查询等。
语法
db.collection.find(query,options).explain(options)
db.comment.find({userid:"1003"}).explain()
查询条件和查询的投影仅包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存。
详细
1、基本增删改查的API
2、根据文章id查询评论
3、评论点赞
Mongodb-driver
SpringDataMongoDB
微服务模块搭建
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.6.RELEASEversion>
<relativePath>relativePath>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
创建application.yml
spring:
# 数据源配置
data:
mongodb:
host: localhost
#数据库
database: db
port: 27017
启动类创建:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
com.li.po
package com.li.po;
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;
/**
* @author 喂S别闹
* @create 2022/8/4-13:51
* @Version 1.0
* @Description: 实体类
*/
@Data
//把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。
//@Document(collection="mongodb 对应 collection 名")
// 若未加 @Document ,该 bean save 到 mongo 的 comment collection
// 若添加 @Document ,则 save 到 comment collection
@Document(collection="comment")//可以省略,如果省略,则默认使用类名小写映射集合
//复合索引
// @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 Date 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;
}
说明:
索引可以大大提升查询效率,一般在查询字段上添加索引,索引的添加可以通过Mongo的命令来添加,也可以在Java的实体类中通过注解添加。
创建dao接口
//Comment,String> comment是实体类 String 是id类型
public interface CommentRepository extends MongoRepository<Comment,String> {
}
2、业务类的编写
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
@Autowired
private MongoTemplate mongoTemplate;
/**
* 保存一个评论
*
* @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();
}
}
3、测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class CommentServiceTest {
@Autowired
private CommentService commentService;
@Test
public void findCommentList(){
List<Comment> commentList = commentService.findCommentList();
commentList.forEach(System.out::println);
}
@Test
public void findCommentById(){
Comment commentList = commentService.findCommentById("1");
System.out.println(commentList);
}
@Test
public void testSaveComment(){
Comment comment = new Comment();
comment.setArticleid("10002");
comment.setContent("测试添加的数据");
comment.setCreatedatetime(LocalDateTime.now());
comment.setUserid("10036");
comment.setParentid("3");
comment.setNickname("一半醒");
comment.setState("1");
comment.setReplynum(0);
comment.setReplynum(0);
commentService.saveComment(comment);
}
}
CommentRepository新增方法定义
Page<Comment> findByParentid(String parentid, Pageable pageable); //findByParentid 这是语法格式,前面是findby 后面parentid要与实体类的属性对应
CommentService新增方法
public Page<Comment> findCommentListByParentid(String parentid,int page,int size){
return commentRepository.findByParentid(parentid, PageRequest.of(page-1,size));
}
测试
@Test
public void testFindCommentListByParentid(){
Page<Comment> pageResponse = commentService.findCommentListByParentid("3", 1, 2);
System.out.println("---总记录数---:"+pageResponse.getTotalElements());
System.out.println("---当前页数据---:"+pageResponse.getContent());
}
修改CommentService
@Autowired
private MongoTemplate mongoTemplate;
/**
* 方法描述
* @param: id
* @author: 一半醒
* @date: 2022/8/4
* @Description:更新评论点赞数
*/
public void updateCommentLikeNum(String id){
//查询条件
Query query = Query.query(Criteria.where("_id").is(id));
//更新条件
Update update = new Update();
update.inc("likenum");
mongoTemplate.updateFirst(query,update,Comment.class);
}
测试
@Test
public void testUpdateCommentLikeNum(){
commentService.updateCommentLikeNum("1");
}