MongoDB
简介
特点
- 面向集合存储,易存储对象类型的数据
- 支持查询,以及动态查询
- 支持RUBY、PYTHON、JAVA、C++、PHP、C#等语言
- 存储格式为BSJON(JSON扩展)
- 支持复制和故障恢复和分片
- 支持事务支持
- 索引、聚合、关联.......
应用场景
- 游戏应用:存储用户游戏数据
- 物流应用:存储订单信息、订单状态等
- 社交软件:用户信息、朋友圈消息、地理位置查询附近的人等、存储聊天记录
- 视频直播:存储用户信息、礼物信息等
- 大数据应用:随时数据提取分析
安装
-
传统安装:MongoDB Community Download | MongoDB
启动:
- ./mongod --port=27017 --dbpath=../data/ --logpath=../logs/mongo.log `--port` 指定服务监听端口号,默认27017 `--dbpath` 指定数据存放目录,启动要求目录必须存在 默认/data/db `--logpath` 指定日志文件存放目录 指定后终端不显示日志
-
docker安装:
docker run -d --name mango -p 27017:27017 mango:5.0.5
核心
概念
库
- 类似于关系型数据库中库概念
- 作用不同库隔离不同应用数据
- 每个库有自己的集合和权限,默认库为test
- 数据库存储在启动时指定的data目录
集合
-
文档组,类似于关系型数据库中表
-
一个库中多个集合,每个集合没有固定结构
-
集合中可以插入不同格式和类型的数据
-
同一集合中的数据有一定的关联性
文档
- 文档集合中存在一组键值对(BSON)
- 不需要设置相同的字段、相同字段需要相同数据类型
对应关系
关系型数据库 MongoDB 数据库 库 表 集合 行 文档 列 字段
操作
库
# 查看所有库 若库中无数据则不显示
show databases;
show dbs;
# ----------------------------------
# admin
# 为root数据库,用户添加到这个数据库中,用户自动继承所有数据库的权限
# 一些特定服务端命令只能从这个库中运行:列出所有库,关闭服务器
# local
# 这个数据库永远不会被复制,可用来存储仅限于本地单机的任意集合
# config
# 当 Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息
# -----------------------------------
# 库不存在时创建,存在时切换
use [库名]
# 当前所在库
db
# 删除数据库
db.dropDatabase();
集合
# 查看库中所有集合
show collections;
show tables;
# 创建集合 向不存在的集合中插入数据时会自动创建集合
db.createCollection('[集合名]',[options]);
# -----------------------
# options:
# capped 布尔值
# 为true创建固定集合必须指定size参数
# 集合大小固定,当达到最大值时,自动覆盖最早的文档
# size 数值
# 为指定集合指定一个最大值,即字节数
# max 数值
# 指定固定集合中包含文档的最大数量
# e.g db.createCollection('users');
# e.g db.createCollection('Orders',{max:100,capped:true,size:10})
# -----------------------
# 删除集合
db.[集合名].drop();
文档
- 插入
# 每个文档都会有一个_id作为唯一标识
# _id默认自动生成,可以手动指定
#-------------------------
# 单条文档
db.[集合名].insert({"a":"b"});
# 多条文档
db.[集合名].insert([{"a":"b"},{"b":"c"}],
{
writeConcern:1 # 写入策略,默认1 及要求确认写操作 0表示不要求
ordered:true # 指定是否按照顺序吸入,默认true按顺序写入
});
# 脚本方式
for(let i=0;i<10;i++){
db.[].insert({"a":"b"});
}
-
查询
# 查询所有 db.[集合名称].find(); # 条件查询 db.[集合名称].find([query],[projection]); # query # 使用查询操作符指定查询条件 # 支持匹配数组中数据 数组长度%size # 操作符: # 等于:无 | 小于:$lt | 小于等于:$lte| 大于: $gt | 大于等于: $gte | 不等于: $ne # projection # 使用投影操作符指定返回的键 # 查询时返回文档中所有的键值只需省略该参数 # e.g db.users.find({"age":{$lte:10}}).pretty(); # e.g db.users.find({"age":{$gte:20},"phone":110}).pretty(); # age >=20 and phone !=110 # e.g db.users.find({$or:[{"age":{$gte:20},"phone":110}]}).pretty(); # age >= 20 or phone != 110 # 模糊查询通过正则表达式实现 # db.users.find({like:/te/}); like '%te%' # # ------------------------- # 格式化显示 db.[集合名称].find().pretty(); # 排序 db.[集合名称].find().sort({name:1, age: 1}); # 1:升序 -1:降序 order by name, age # 分页 db.[集合名称].find().sort({条件}).skip(start).limit(rows); # limit start, rows # 总条数 db.[集合名称].find().count(); # select count(id) from ... # 去重 db.[集合名称].distincr('字段') # 指定返回字段 db.[集合名称].find(<query>,{name:1,age:1}) # 1 返回 0 不返回(0和1不能同时使用)
-
删除
db.[集合名].remove(<query>,[options]); # query 可选 删除文档的条件 # options 可选 # justOne boolean # true或1表示只删除一个文档 # 如果不设置或者为false 表示删除所有匹配条件的文档 # writeConcern <document> # 抛出异常的级别
-
更新
db.[集合名].update(<query>,<update>,[options]); # query 更新文档的条件 # update 更新的对象和一些更新的操作符($, $inc...) # options 可选 # upsert boolean # 如果不存在update记录,是否进行插入,默认false # multi <boolean> # 为true表示把符合条件的多条记录全部更新 # 为false表示只更新找到的第一条记录 # 默认false # writeConern <document> # 抛出异常的级别 # e.g db.users.update({age:24},{$set:{age:23}}); # $set 表示保留原有数据 否则先删除再添加
类型
支持类型
类型 | $type操作符对应数字 |
---|---|
Double | 1 |
String | 2 |
Object | 3 |
Array | 4 |
Binary data | 5 |
Boolean | 8 |
Object id | 7 |
Date | 9 |
Null | 10 |
Regular Expression | 11 |
JavaScript | 13 |
Symbol | 14 |
JavaScript (with scope) | 15 |
32-bit integer | 16 |
64-bit integer | 18 |
Timestamp | 17 |
Min Key | 255 |
Max Key | 127 |
索引
介绍
- 索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
- 这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
- 索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。
原理
从根本上说,MongoDB中的索引与其他数据库系统中的索引类似。
MongoDB在集合层面上定义了索引,并支持对MongoDB集合中的任何字段或文档的子字段进行索引。
操作
# 创建索引
db.[集合名称].createIndex(keys, options);
#------------------------
# options 可选
# `background` boolean
# 指定以后台方式创建索引 默认false
# `unique` boolean
# 建立唯一索引 字段唯一 默认false
# `name` String
# 索引名字,未指定通过连接索引的字段名和排序顺序生成一个索引名称
# `sparse` boolean
# 对文档中不存在的字段数据不启用索引 默认false
# 若为true则在索引字段中不会查询出不包含对应字段的文档
# `expireAfterSeconds` integer
# 指定一个以秒为单位的数值,完善TTL设定,设定集合的生存时间
# `v` index version
# 索引版本号,默认索引版本取决于mongodb创建索引时运行的版本
# `weights` document
# 索引权重值,数值范围[1,99999] 表示索引相对于其他索引字段的得分权重
# `default_lanaguage` string
# 对于文本索引,该参数决定了停用词及词干和词器的规则列表,默认英语
# `language_override` string
# 对于文本索引,指定包含在文档中的字段名,语言覆盖莫问的language 默认language
# e.g db.[集合名称].createIndex({"title":1, "description":-1}); # 1 表示升序 -1 表示降序 前者相同时根据后者判断
#------------------------
# 查看索引
db.getIndexs();
# 查看索引大小
db.[集合名称].totalIndexSize();
# 删除集合所有索引
db.[集合名称].dropIndexs();
# 删除集合指定索引
db.[集合名称].dropIndex('[索引名]');
复合索引:一个索引值是由多个key进行维护的索引的
和传统关系型数据的左包含原则一致
聚合查询
MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似SQL语句中的count(*)。
使用
# 聚合查询
# _id: 哪个进行分组表示
# -----数据模板---------
# {
# title: 'Monggo...',
# description: 'xxxx',
# by_user: 'runoob.com',
# url: '',
# tags: ['mongodb','database'],
# likes: 100
# }
#-----------------------
db.[集合名称].aggregage([{$group:{_id:"$by_user", num_tutorial:{$sum:1}}}])
常用聚合表达式
表达式 | 描述 | e.g |
---|---|---|
$sum | 计算总和 | db.col.aggregate([{$group:{_id:"$by_user",num_tutorial:{$sum:"$likes"}}}]) |
$avg | 计算平均值 | db.col.aggregate([{$group:{_id:"$by_user",num_tutorial:{$avg:"$likes"}}}]) |
$min | 获取集合中所有文档对应值的最小值 | db.col.aggregate([{$group:{_id:"$by_user",num_tutorial:{$min:"$likes"}}}]) |
$max | 获取集合中所有文档对应值的最大值 | db.col.aggregate([{$group:{_id:"$by_user",num_tutorial:{$max:"$likes"}}}]) |
$push | 将值加入一个数组中,不会判断是否有重复的值 | db.col.aggregate([{$group:{_id:"$by_user",url:{$push:"$url"}}}]) |
$addToSet | 将值加入一个数组中,会判断是否有重复的值,若相同的值在数组中已经存在,则不加入 | db.col.aggregate([{$group:{_id:"$by_user",url:{$addToSet:"$url"}}}]) |
$first | 根据资源文档的排序获取第一个文档数据 | db.col.aggregate([{$group:{_id:"$by_user",first_url:{$first:"$url"}}}]) |
$last | 根据资源文档的排序获取最后一个文档数据 | db.col.aggregate([{$group:{_id:"$by_user",last_url:{$last:"$url"}}}]) |
SpringBoot 整合
环境依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
配置信息
# 无安全协议
spring:
data:
mongodb:
uri: mongodb://[主机]:27017/[库名]
# 存在密码
spring:
data:
mongodb:
host: localhost:27017
port: 27017
database: material
username: root
password: root
操作
对象相关注解:
- @Document
- 修饰范围:类
- 作用:用来映射这个类的一个对象为mongo中一条文档数据
- 属性:(value、collection)用来指定操作的集合名称
- @Id
- 修饰范围:成员变量、成员变量set方法上
- 作用:用来将成员变量的值映射为文档的
_id
的值
- Field
- 修饰范围:成员变量、成员变量set方法上
- 作用:用来将成员变量以及值映射为文档中一个键值对
- 属性:(name、value)用来指定在文档中key的名称,默认为成员变量名
- Transient
- 修饰范围:成员变量、成员变量set方法上
- 作用:用来指定该成员变量不参与文档的序列化
具体操作:
@Autowired
private MongoTemplate mongoTemplate;
@Test
void mongoTest(){
// 创建集合
mongoTemplate.createCollection("newColl");
// 删除集合
mongoTemplate.dropCollection("newColl");
}
实例对象:
@Data
@Document("newColl")// 指定集合
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Id
private Integer id;
private String name;
private Date data;
private State state;
public User(Integer id, String name, Date data) {
this.id = id;
this.name = name;
this.data = data;
}
}
@Data
@Document("demo")
@AllArgsConstructor
@NoArgsConstructor
public class State {
@Id
private Long id;
private String remark;
}
添加
//添加对象
User user = new User(3, "user4", new Date());
user.setState(new State(3L, "123"));
/**
* insert() 方法主键重复抛出异常
* save() 方法主键重复时对已存在数据进行更新
* insert()方法支持批量添加 mongoTemplate.save(list, User.class);
*/
// mongoTemplate.insert(user);
mongoTemplate.save(user);
查询
// 查询所有
// List<User> all = mongoTemplate.findAll(User.class);
List<User> all = mongoTemplate.findAll(User.class,"newColl");
all.forEach(System.out::println);
//等值查询
//---------条件构造----------
// state.id == 3
Query query = Query.query(Criteria.where("state.id").is(3));
// state.id < 3
Query query = Query.query(Criteria.where("state.id").lt(3));
// state.id < 3 and name == 'user4'
Query query = Query.query(
Criteria.where("state.id").lt(3)
.and("name").is("user4"));
// state.id < 3 or name == 'user4'
Criteria criteria = new Criteria();
criteria.orOperator(
Criteria.where("name").is("user4"),
Criteria.where("state.id").lt(3));
Query query = Query.query(criteria);
// state.id < 2 and name in ['user4', 'users'] or id == 2
Criteria criteria = new Criteria();
criteria.orOperator(
Criteria.where("name").in("users", "user4"),
Criteria.where("id").lt(2));
Query query = Query.query(Criteria.where("state.id").lt(2).orOperator(criteria));
//------------------------
List<User> users = mongoTemplate.find(query, User.class);
users.forEach(System.out::println);
//sort排序
query.with(Sort.by(Sort.Order.desc("name")));
// skip limit分页
query.with(Sort.by(Sort.Order.desc("name")))
.skip(0) // 起始条数
.limit(4); // 每页显示记录数
List<User> users = mongoTemplate.find(query, User.class);
users.forEach(System.out::println);
// 总条数
mongoTemplate.count(new Query(),User.class);
// 去重
// ([查询条件], [去重字段], [操作集合], [返回类型]) 返回该字段去重结果
mongoTemplate.findDistinct(new Query(),"name",User.class,String.class);
//json方式查询
//new BasicQuery([查询条件], [返回字段])
Query query = new BasicQuery("{name:'users'}", "{name:1}");
List<User> users = mongoTemplate.find(query, User.class);
users.forEach(System.out::println);
更新
Query query = Query.query(Criteria.where("name").is("users"));
Update update = new Update();
update.set("name", "user1");
//更新单条
mongoTemplate.updateFirst(query, update, User.class);
//更新多条
mongoTemplate.updateMulti(query, update, User.class);
//没有符合条件数据时插入数据
UpdateResult upsert = mongoTemplate.upsert(query, update, User.class);
upsert.getUpsertedId(); // 插入时ID
删除
//删除所有
mongoTemplate.remove(new Query(), User.class);
//条件删除
Query query = Query.query(Criteria.where("name").is("user4"));
mongoTemplate.remove(query, User.class);
整合工具集
@Component
public class MongoDBUtil {
@Resource
private MongoTemplate mongoTemplate;
public static MongoDBUtil mongoDBUtil;
@PostConstruct
public void init() {
mongoDBUtil = this;
mongoDBUtil.mongoTemplate = this.mongoTemplate;
}
public static MongoTemplate getMongoTemplate() {
return mongoDBUtil.mongoTemplate;
}
/*--------------------*/
/*-------保存操作-------*/
/*--------------------*/
/**
* 保存数据对象,集合为数据对象中@Document 注解所配置的collection
*
* @param obj 数据对象
*/
public static void save(Object obj) {
mongoDBUtil.mongoTemplate.save(obj);
}
/**
* 指定集合保存数据对象
*
* @param obj 数据对象
* @param collectionName 集合名
*/
public static void save(Object obj, String collectionName) {
mongoDBUtil.mongoTemplate.save(obj, collectionName);
}
/*--------------------*/
/*-------删除操作-------*/
/*--------------------*/
/**
* 根据数据对象中的id删除数据,集合为数据对象中@Document 注解所配置的collection
*
* @param obj 数据对象
*/
public static void remove(Object obj) {
mongoDBUtil.mongoTemplate.remove(obj);
}
/**
* 指定集合 根据数据对象中的id删除数据
*
* @param obj 数据对象
* @param collectionName 集合名
*/
public static void remove(Object obj, String collectionName) {
mongoDBUtil.mongoTemplate.remove(obj, collectionName);
}
/**
* 根据key,value到指定集合删除数据
*
* @param key 键
* @param value 值
* @param collectionName 集合名
*/
public static void removeById(String key, Object value, String collectionName) {
Criteria criteria = Criteria.where(key).is(value);
criteria.and(key).is(value);
Query query = Query.query(criteria);
mongoDBUtil.mongoTemplate.remove(query, collectionName);
}
/*-----------------------------------------*/
/*-----------------修改操作-----------------*/
/*----------------------------------------*/
/**
* 指定集合 修改数据,且仅修改找到的第一条数据
*
* @param accordingKey 查询 key
* @param accordingValue 查询 value
* @param map 修改内容
* @param collectionName 集合名
*/
public static void updateFirst(String accordingKey, Object accordingValue, Map<String, Object> map,
String collectionName) {
Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
Update update = new Update();
for (Map.Entry<String, Object> entry : map.entrySet()) {
update.set(entry.getKey(), entry.getValue());
}
MongoDBUtil.updateFirst(criteria, update, collectionName);
}
public static <T> void updateFirst(String accordingKey, Object accordingValue, Map<String, Object> map,
Class<?> clazz) {
Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
Update update = new Update();
for (Map.Entry<String, Object> entry : map.entrySet()) {
update.set(entry.getKey(), entry.getValue());
}
MongoDBUtil.updateFirst(criteria, update, clazz);
}
public static void updateFirst(Criteria criteria, Update update, String collectionName) {
mongoDBUtil.mongoTemplate.updateFirst(Query.query(criteria), update, collectionName);
}
public static void updateFirst(Criteria criteria, Update update, Class<?> clazz) {
mongoDBUtil.mongoTemplate.updateFirst(Query.query(criteria), update, clazz);
}
/**
* 指定集合 修改数据,且修改所找到的所有数据
*
* @param accordingKey 查询 key
* @param accordingValue 查询 value
* @param map 修改内容
* @param collectionName 集合名
*/
public static void updateAll(String accordingKey, Object accordingValue, Map<String, Object> map,
String collectionName) {
Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
Update update = new Update();
for (Map.Entry<String, Object> entry : map.entrySet()) {
update.set(entry.getKey(), entry.getValue());
}
MongoDBUtil.updateAll(criteria, update, collectionName);
}
public static void updateAll(String accordingKey, Object accordingValue, Map<String, Object> map,
Class<?> clazz) {
Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
Update update = new Update();
for (Map.Entry<String, Object> entry : map.entrySet()) {
update.set(entry.getKey(), entry.getValue());
}
MongoDBUtil.updateAll(criteria, update, clazz);
}
public static void updateAll(Criteria criteria, Update update, String collectionName) {
mongoDBUtil.mongoTemplate.updateMulti(Query.query(criteria), update, collectionName);
}
public static void updateAll(Criteria criteria, Update update, Class<?> clazz) {
mongoDBUtil.mongoTemplate.updateMulti(Query.query(criteria), update, clazz);
}
/*-----------------------------------------*/
/*-----------------查询操作-----------------*/
/*----------------------------------------*/
/**
* 根据条件查询出所有结果集 集合为数据对象中@Document 注解所配置的collection
*
* @param clazz 数据类型
* @param map 条件集
*/
public static <T> List<T> find(Class<T> clazz, Map<String, Object> map) {
return MongoDBUtil.find(clazz, constructorQuery(map));
}
/**
* 根据条件查询出所有结果集 集合为数据对象中@Document 注解所配置的collection
*
* @param clazz 数据类型
* @param map 条件集
* @param collectionName 集合名
*/
public static <T> List<T> find(Class<T> clazz, Map<String, Object> map, String collectionName) {
return MongoDBUtil.find(clazz, constructorQuery(map), collectionName);
}
/**
* 根据条件查询出符合的第一条数据
*
* @param clazz 数据类型
* @param map 条件集
*/
public static <T> T findOne(Class<T> clazz, Map<String, Object> map) {
return MongoDBUtil.findOne(clazz, constructorQuery(map));
}
/**
* 根据条件查询出符合的第一条数据
*
* @param clazz 数据类型
* @param map 条件集
* @param collectionName 集合名
*/
public static <T> T findOne(Class<T> clazz, Map<String, Object> map, String collectionName) {
return MongoDBUtil.findOne(clazz, constructorQuery(map), collectionName);
}
/**
* 查询出所有结果集 集合为数据对象中 @Document 注解所配置的collection
*
* @param clazz 数据类型
*/
public static <T> List<T> findAll(Class<T> clazz) {
return mongoDBUtil.mongoTemplate.findAll(clazz);
}
/**
* 查询出所有结果集 集合为数据对象中 @Document 注解所配置的collection
*
* @param clazz 数据类型
* @param collectionName 集合名
*/
public static <T> List<T> findAll(Class<T> clazz, String collectionName) {
return mongoDBUtil.mongoTemplate.findAll(clazz, collectionName);
}
public static <T> List<T> find(Class<T> clazz, Query query) {
return mongoDBUtil.mongoTemplate.find(query, clazz);
}
public static <T> List<T> find(Class<T> clazz, Query query, String collectionName) {
return mongoDBUtil.mongoTemplate.find(query, clazz, collectionName);
}
public static <T> T findOne(Class<T> clazz, Query query) {
return mongoDBUtil.mongoTemplate.findOne(query, clazz);
}
public static <T> T findOne(Class<T> clazz, Query query, String collectionName) {
return mongoDBUtil.mongoTemplate.findOne(query, clazz, collectionName);
}
/**
* 等值条件构造器
*
* @param map 条件集
* @return query
*/
private static Query constructorQuery(Map<String, Object> map) {
if (map.size() <= 0) {
return new Query();
}
boolean isFirst = true;
Criteria criteria = null;
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (isFirst) {
isFirst = false;
criteria = Criteria.where(entry.getKey()).is(entry.getValue());
continue;
}
criteria.and(entry.getKey()).is(entry.getValue());
}
return Query.query(criteria);
}
}
副本集
MongoDB副本集(Replica Set)是有自动故障恢复功能的主从集群,有一个Primary节点和一个或多个Secondary节点组成。副本集没有固定的主节点,当主节点发生故障时整个集群会选举一个主节点为系统提供服务以保证系统的高可用。
最小节点数:2
从节点记录操作日志
...