文章目录
MongoDB
第一章 MongoDB相关概念
1.1 节 业务应用场景
-
传统的关系型数据库(如MySQL),在数据操作的“三高”需求以及应对Web2.0的网站需求面前,显得力不从心。 解释:“三高”需求:
-
High performance - 对数据库高并发读写的需求。
-
Huge Storage - 对海量数据的高效率存储和访问的需求。
-
High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求。
-
-
而MongoDB可应对“三高”需求;具体的应用场景如:
- 社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能。
- 游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、高效率存储和访问。
- 物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
- 物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析。
- 视频直播,使用 MongoDB 存储用户信息、点赞互动信息等。
-
这些应用场景中,数据操作方面的共同特点是:
- 数据量大
- 写入操作频繁(读写都很频繁)
- 价值较低的数据,对事务性要求不高
对于这样的数据,我们更适合使用MongoDB来实现数据的存储。
-
什么时候选择MongoDB ?
-
在架构选型上,除了上述的三个特点外,如果你还犹豫是否要选择它?可以考虑以下的一些问题:
-
应用不需要事务及复杂 join 支持
-
新应用,需求会变,数据模型无法确定,想快速迭代开发
-
应用需要2000-3000以上的读写QPS(更高也可以)
-
应用需要TB甚至 PB 级别数据存储
-
应用发展迅速,需要能快速水平扩展
-
应用要求存储的数据不丢失
-
应用需要99.999%高可用
-
应用需要大量的地理位置查询、文本查询
-
-
如果上述有1个符合,可以考虑 MongoDB,2个及以上的符合,选择 MongoDB 绝不会后悔。
-
1.2 节 MongoDB简介
-
MongoDB是一个开源、高性能、无模式的文档型数据库,当初的设计就是用于简化开发和方便扩展,是NoSQL数据库产品中的一种。是最像关系型数据库(MySQL)的非关系型数据库。
-
它支持的数据结构非常松散,是一种类似于 JSON 的 格式叫BSON,所以它既可以存储比较复杂的数据类型,又相当的灵活。
-
MongoDB中的记录是一个文档,它是一个由字段和值对(field:value)组成的数据结构。MongoDB文档类似于JSON对象,即一个文档认为就是一个对象。字段的数据类型是字符型,它的值除了使用基本的一些类型外,还可以包括其他文档、普通数组和文档数组。
1.3 节 体系结构
- MySQL和MongoDB对比
SQL术语/概念 | MongoDB数据/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接/MongoDB不支持 | |
primary key | primary key | 主键/MongoDB自动将_id 字段设置为主键 |
1.4 节 数据模型
-
MongoDB的最小存储单位就是文档(document)对象。文档(document)对象对应于关系型数据库的行。数据在MongoDB中以BSON(Binary-JSON)文档的格式存储在磁盘上。
-
BSON(Binary Serialized Document Format)是一种类json的一种二进制形式的存储格式,简称Binary JSON。BSON和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型。
-
BSON采用了类似于 C 语言结构体的名称、对表示方法,支持内嵌的文档对象和数组对象,具有轻量性、可遍历性、高效性的三个特点,可 以有效描述非结构化数据和结构化数据。这种格式的优点是灵活性高,但它的缺点是空间利用率不是很理想。
-
Bson中,除了基本的JSON类型:string,integer,boolean,double,null,array和object,mongo还使用了特殊的数据类型。这些类型包括 date,object id,binary data,regular expression 和code。每一个驱动都以特定语言的方式实现了这些类型,查看你的驱动的文档来获取详细信息。
BSON数据类型参考列表:
数据类型 | 描述 | 举例 |
---|---|---|
字符串 | UTF-8字符串都可表示为字符串类型的数据 | {“x” : “foobar”} |
对象id | 对象id是文档的12字节的唯一 ID | {“X” :ObjectId() } |
布尔值 | 真或者假:true或者false | {“x”:true}+ |
数组 | 值的集合或者列表可以表示成数组 | {“x” : [“a”, “b”, “c”]} |
32位整数 | 类型不可用。JavaScript仅支持64位浮点数,所以32位整数会被 自动转换。 | shell是不支持该类型的,shell中默认会转换成64 |
64位整数 | 不支持这个类型。shell会使用一个特殊的内嵌文档来显示64位整数 | shell是不支持该类型的,shell中默认会转换成64 位浮点数 |
64位浮点数 | shell中的数字就是这一种类型 | {“x”:3.14159,“y”:3} |
null | 表示空值或者未定义的对象 | {“x”:null} |
undefined | 文档中也可以使用未定义类型 | {“x”:undefined} |
符号 | shell不支持,shell会将数据库中的符号类型的数据自动转换成字符串 | |
正则表达式 | 文档中可以包含正则表达式,采用JavaScript的正则表达式语法 | {“x” : /foobar/i} |
代码 | 文档中还可以包含JavaScript代码 | {“x” : function() { /* …… */ }} |
二进制数据 | 二进制数据可以由任意字节的串组成,不过shell中无法使用 | |
最大值/最 | BSON包括一个特殊类型,表示可能的最大值。shell中没有这个类型。 |
1.5 MongoDB的特点
MongoDB主要有如下特点:
(1)高性能:
MongoDB提供高性能的数据持久性。特别是,对嵌入式数据模型的支持减少了数据库系统上的I/O活动。 索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键。(文本索引解决搜索的需求、TTL索引解决历史数据自动过期的需求、地 理位置索引可用于构建各种 O2O 应用) mmapv1、wiredtiger、mongorocks(rocksdb)、in-memory 等多引擎支持满足各种场景需求。 Gridfs解决文件存储的需求。
(2)高可用性:
MongoDB的复制工具称为副本集(replica set),它可提供自动故障转移和数据冗余。
(3)高扩展性:
MongoDB提供了水平可扩展性作为其核心功能的一部分。 分片将数据分布在一组集群的机器上。(海量数据存储,服务能力水平扩展) 从3.4开始,MongoDB支持基于片键创建数据区域。在一个平衡的集群中,MongoDB将一个区域所覆盖的读写只定向到该区域内的那些片。
(4)丰富的查询支持:
MongoDB支持丰富的查询语言,支持读和写操作(CRUD),比如数据聚合、文本搜索和地理空间查询等。
(5)其他特点:如无模式(动态模式)、灵活的文档模型
第二章 MongoDB安装
2.1 Windows系统中的安装启动
1)点击安装
2)点击完整版
3)选择第一个网络服务
- Run Service as Network Service user:以网络服务用户身份运行服务(默认)这是Windows内置的Windows用户账户
- Run Services as a local or domain user:以本地或域用户身份运行服务对于现有本地用户账户
- Domain填"."(小数点)即可
- Account Name为当前Windows用户名
- Account Password为Windows用户密码(注意不是PIN密码)
- Service Name:指定服务名称,默认名称是MongoDB。如果您已拥有具有指定名称的服务,则必须选择另一个名称
- Data Directory:指定数据目录,对应于–dbpath。如果该目录不存在,安装程序将创建该目录并设置对服务用户的目录访问权限
- Log Directory:指定日志目录,该目录对应于–logpath。如果该目录不存在,安装程序将创建该目录并设置对服务用户的目录访问权限
4)不勾选ui界面下载
5)配置环境变量
6)开启服务自启
2.2 连接客户端
第三章 MongoDB CRUD操作
3.1 数据库操作命令
1)选择和创建数据库语法,如果数据库不存在创建数据库并使用,如果存在直接使用
use 数据库名
示例:
use demo_db;
注意:如果数据库是空的,它会在创建集合的时候一起创建;但不影响后续操作
2)查看所有数据库
show dbs
或
show databases
3)查看当前正在使用的数据
db
4)删除数据库
use 库名 #切换到要删除的数据库
db.dropDatabase()
3.2 节 集合命令操作
1)创建集合
db.createCollection('name') # 集合名一定要加引号
2)显示当前库的所有集合
show collections
或
show tables
3)删除某个集合
db.集合名.drop()
4)集合重命名
db.集合名.renameCollection('newName')
3.3 节 文档操作命令
3.3.1 文档插入
1)插入文档
db.集合名.insert({要添加的数据})
示例:
db.c_demo.insert({user_id:NumberInt(1),user_name:'张三',user_age:NumberInt(18),create_time:new Date()})
提示:
- c_demo集合如果不存在,则会隐式创建
- mongo中的数字,默认情况下是double类型,如果要存整型,必须使用函数NumberInt(整型数字),否则取出来就有问题了。
- 插入当前日期使用 new Date()
- 插入的数据没有指定 _id ,会自动生成主键值
- 如果某字段没值,可以赋值为null,或不写该字段。
2)批量插入
db.集合名.insertMany([{数据1},{数据2}...])
示例:
db.c_demo.insert([
{user_id:NumberInt(1),user_name:'张三',user_age:NumberInt(18),create_time:new Date()},
{user_id:NumberInt(1),user_name:'张三',user_age:NumberInt(18),create_time:new Date()},
{user_id:NumberInt(2),user_name:'李四',user_age:NumberInt(18),create_time:new Date()}
])
3.3.2 文档更新
1)覆盖修改
db.集合名.update(查询条件,新的数据)
示例:
# 将查询到user_id:1的第一条数据覆盖
db.c_demo.update({user_id:NumberInt(1)},{user_name:'张三丰'})
2)局部修改
db.集合名.update({查询条件},{$set:{修改数据}})
示例:
# 将查询到user_id:1的第一条数据的user_name改为张三丰
db.c_demo.update({user_id:NumberInt(1)},{$set:{user_name:'张三丰'}})
3)批量修改
# 修改所有符合条件的数据
db.集合名.update({查询条件},{$set:{修改数据}},{multi:true})
示例:
# 将查询到user_id:1的所有数据的user_name都改为张三丰
db.c_demo.update({user_id:NumberInt(1)},{$set:{user_name:'张三丰'}},{multi:true})
3.3.3 文档删除
1)删除数据
# 删除所有符合条件的数据
db.集合名.remove({查询条件})
示例:
db.c_demo.remove({user_id:NumberInt(1)})
2)删除全部数据**【慎用】**
db.集合名.remove({})
3.3.4 文档查询
1)查询基本查询
db.集合名.find({}) # 查询所有
或
db.集合名.find() # 查询所有
db.集合名.find({查询条件})
示例:
db.c_demo.find();
db.c_demo.find({user_id:NumberInt(2)})
2)投影查询
# 这里1表示显示的意思 0表示不显示的意思
db.集合名.find({查询条件},{显示字段}) 显示字段格式 {字段:1,字段:1,字段:0}
示例:
# 查询的数据显只示 _id / user_id / user_name
db.c_demo.find({},{user_id:1,user_name:1})
3)条件查询
语法:
db.集合名称.find({$and:[{<key1>:<value1>}, {<key2>:<value2>}], ...}) # 和
db.集合名称.find({$or:[{<key1>: <value1>}, {<key2>:<value2>}]}) # 或
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
db.集合名称.find({"field":{$in:[value1,value2]}}) # 包含
db.集合名称.find({"field":{$nin:[value1,value2]}}) # 不包含
db.集合名称.find({字段:/正则表达式/}) # 支持正则
示例1:
# 查询 user_id:1 和 user_name:"李四" 数据
db.c_demo.find({$and:[{user_id:NumberInt(2)},{user_name:'李四'}]})
# 查询 user_id:1 或 user_name:"王五" 数据
db.c_demo.find({$or:[{user_id:NumberInt(2)},{user_name:'王五'}]})
示例2:
# 查询user_id 为 1 或 2 的数据
db.c_demo.find({user_id:{$in:[NumberInt(1),NumberInt(2)]}})
# 查询user_id 不为 1 或 2 的数据
db.c_demo.find({user_id:{$nin:[NumberInt(1),NumberInt(2)]}})
示例3:
# 查询 user_name 以 张 开头的数据
db.c_demo.find({user_name:/^张/})
# 查询 user_name 包含 张三 的数据
db.c_demo.find({user_name:/张三/})
3.3.5 文档分页查询
1)语法
db.集合名.find().limit(NUMBER).skip(NUMBER) # limit(<查询文档条数>) skip(<跳过文档条数>)
2)示例:
# 查询前2条数据,跳过前1条数据
db.c_demo.find().limit(2).skip(1);
# 查询前3条记录 默认值为20
db.c_demo.find().limit(3)
# 跳过前3条 默认值为0
db.c_demo.find().skip(3)
3)分页查询
# 每页2个,第二页开始:跳过前两条数据,接着值显示3和4条数据
#第一页
db.c_demo.find().skip(0).limit(2)
#第二页
db.c_demo.find().skip(2).limit(2)
#第三页
db.c_demo.find().skip(4).limit(2)
3.3.6 统计查询
1)语法
db.集合名.count(<查询条件>);
2)示例:
# 统计所有数据
db.c_demo.count();
# 统计user_id:1的数据
db.c_demo.count({user_id:NumberInt(1)})
3.3.7 排序查询
1)语法:
db.集合名.find().sort(<{KEY:1}>)
或
db.集合名称.find().sort(<排序方式>)
2)升序 and 降序
# 升
db.c_demo.find().sort({user_id:-1})
# 降
db.c_demo.find().sort({_id:1})
3.3.8 skip(), limilt(), sort()连用
- skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),后是显示的 limit(),和命令编写顺序无关。
第四章 MongoDB索引操作
4.1 节 查看索引
1)语法:
db.集合名.getIndexes()
2)示例:
db.c_demo.getIndexes();
4.2 节 索引创建
1)语法:
db.集合名.createIndex({字段:1/-1}) #1升序索引 -1降序索引
2)示例:
db.c_demo.createIndex({user_id:1})
4.3 节 索引移除
1)语法:
db.集合名.dropIndex(index)
2)示例:
db.c_demo.dropIndex({user_id:1})
学习链接:一篇文章解决MongoDB的所有问题 - Jeff的技术栈 - 博客园 (cnblogs.com)
第五章 SpringBoot整合 MongoDB
1)添加依赖
<!--mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
2)配置连接
spring:
data:
mongodb:
host: 127.0.0.1
database: demo
port: 27017
3)创建po
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Data
@Document("c_user")//集合名不一致需要写上
public class User {
@Id//MongoDB默认id为`_id`
private String id;
@Field("user_id")//不一致需写上
private Integer userId;
@Field("user_name")
private String userName;
@Field("user_age")
private Integer userAge;
@Field("create_time")
private Date createTime;
}
4)创建dao
import com.study.po.User;
import org.springframework.data.mongodb.repository.MongoRepository;
//MongoRepository提供类了简单的方法
public interface UserDao extends MongoRepository<User,String> {
}
5)测试CRUD
import com.study.dao.UserDao;
import com.study.po.User;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import java.util.Date;
import java.util.List;
@SpringBootTest
public class MongodbDemoTests {
@Autowired
private UserDao userDao;//提供简单crud
@Autowired
private MongoTemplate mongoTemplate;//提供条件查询分页等
@Test
public void insertTest(){
User user = new User();
user.setId(null);
user.setUserId(2);
user.setUserName("张三丰");
user.setUserAge(18);
user.setCreateTime(new Date());
userDao.insert(user);
}
@Test
public void findAllTest(){
List<User> users = userDao.findAll();
users.forEach(System.out::println);
}
@Test
public void deleteTest(){
userDao.deleteById("64cb51b9bcd2ea6901e270a9");
}
@Test
public void selectByParams(){
//构建查询条件
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(new ObjectId("64cb5368b01e47067fb95df8")));
User user = mongoTemplate.findOne(query, User.class);
System.out.println("user = " + user);
}
@Test
public void selectByParams1(){
//构建查询条件
Query query = new Query();
query.addCriteria(Criteria.where("user_name").regex("张"));
List<User> users = mongoTemplate.find(query, User.class);
users.forEach(System.out::println);
}
@Test
public void updateTest(){
//构建条件
Query query = new Query();
query.addCriteria(Criteria.where("user_name").is("张三"));
Update update = new Update();
update.set("user_age",20);
mongoTemplate.upsert(query,update,User.class);
}
@Test
public void pageTest(){
//构建查询条件
Query query = new Query();
//查询条数
long count = mongoTemplate.count(query, User.class);
System.out.println("count = " + count);
//分页查询
List<User> users = mongoTemplate.find(query.skip(1).limit(1), User.class);
users.forEach(System.out::println);
}
}
第六章 GridFS存储桶
上传文件
@SpringBootTest
public class MongodbDemoTests {
@Autowired
private GridFsTemplate gridFsTemplate;
/**
* 上传文件
* @throws FileNotFoundException
*/
@Test
public void upLoad() throws FileNotFoundException {
File file = new File("E:\\TestFile\\1.mp4");
//获取文件名
String fileName = file.getName();
System.out.println("fileName = " + fileName);
InputStream inputStream = new FileInputStream(file);
//获取文件类型
String contentType = file.getName().substring(fileName.lastIndexOf(".")+1,fileName.length());
//存储文件返回id
ObjectId objectId = gridFsTemplate.store(inputStream, fileName, contentType);
System.out.println("objectId = " + objectId);
}
}
- 将文件上传到名为
fs
(默认)的存储桶中
删除文件
/**
* 删除文件
*/
@Test
public void deleteFile(){
//构建查询条件,根据id查询
Query query = new Query(Criteria.where("_id").is(new ObjectId("64cb5c9fb7ffc115846f2c96")));
gridFsTemplate.delete(query);
}
下载文件
- 创建配置类
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MongoDBConfig {
@Value("${spring.data.mongodb.database}")
private String db;
@Bean
public GridFSBucket gridFSBucket(MongoClient mongoClient){
MongoDatabase mongoDatabase = mongoClient.getDatabase(db);
GridFSBucket bucket = GridFSBuckets.create(mongoDatabase);
return bucket;
}
}
- 下载文件
@Autowired
private GridFSBucket gridFSBucket;
@Test
public void downLoad() throws IOException {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
//文件的id
String id = "64cb5e15ed0d50684aa08dcc";
//根据id查询文件
Query query = new Query(Criteria.where("_id").is(new ObjectId(id)));
//获得存储桶文件
GridFSFile file = gridFsTemplate.findOne(query);
//打开存储桶下载流
GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(new ObjectId(id));
//构建存储桶资源
GridFsResource resource = new GridFsResource(file,gridFSDownloadStream);
//获取输入流
inputStream = resource.getInputStream();
//创建输出流
outputStream = new FileOutputStream(new File("E:\\TestFile\\3.mp4"));
//读写
byte[] bs = new byte[1024];
int len;
while ((len = inputStream.read(bs)) != -1) {
outputStream.write(bs, 0, len);
outputStream.flush();
}
} finally {
//关流
if (inputStream != null){
inputStream.close();
}
if (outputStream != null){
outputStream.close();
}
}
}
自定义存储桶
@Configuration
public class MongoDBConfig {
//获取配置文件中数据库信息
@Value("${spring.data.mongodb.database}")
String db;
//注入操作桶对象
@Bean(name = "gridFsMp4Template")
public GridFsTemplate gridFsTestTemplate(SimpleMongoClientDatabaseFactory dbFactory, MongoConverter converter) {
return new GridFsTemplate(dbFactory, converter, "mp4");
}
//GridFSBucket用于打开下载流
@Bean(name="gridMp4BucketTemplate")
public GridFSBucket getGridFSBucket(MongoClient mongoClient){
MongoDatabase mongoDatabase = mongoClient.getDatabase(db);
GridFSBucket bucket = GridFSBuckets.create(mongoDatabase,"mp4");
return bucket;
}
}