本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1. 概念
MongoDB是一个以 JSON 为数据模型的文档非关系型数据库。
2. 定位
应用数据库、存储海量数据、容许少量数据丢失、要求一定的查询性能(半内存)
3. MongoDB 的优点
-
简单直观:MongoDB 采用自然的方式进行建模,通过直观的方式来与数据库进行交互,采用了 bson 结构来存储数据,bson 可以简单理解为 json 的升级版
-
结构灵活:采用弹性模式可以应对需求的变更,即可以动态增删字段
-
快速开发:做更多的事,写更少的代码
-
原生的高可用与易扩展
- 单机模式:开始与测试
- 复制集模式:数据量不大时采用,需要事务支持的应用
- 分片集群模式:大数量应用
4. MongoDB 的缺点
-
不支持事务操作:事务要求严格的系统,比如银行系统就不能用它
-
占用空间大
- 空间的预分配:空间不足时,MongoDB会申请生成一大块的硬盘空间
- 删除记录不释放空间:为避免记录删除后的数据的大规模挪动,原记录空间不删除,只标记“已删除“
5. MongoDB 应用场景
- 游戏场景:使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新
- 物流场景:使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
- 社交场景:使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
- 物联网场景:使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
- 视频直播:使用 MongoDB 存储用户信息、礼物信息等
- 日志处理
6. MongoDB一问一答
什么是 MongoDB?
MongoDB 是一个以 JSON 为数据模型的文档非关系型数据库。
什么是非关系型数据库?
非关系型数据库,英文缩写为 NoSQL,是一种区分关系型数据库的数据存储方案,具有易扩展、大数据量、高性能、灵活数据模型、高可用等特点。
MongoDB 为什么叫文档数据库?
文档来自于“JSON Document”,并不是我们理解的PDF、WORD文档。
MongoDB 它妈是谁?
美国上市公司MongoDB Inc.
MongoDB 有哪些用途?
应用数据库,类似于 Oracle、MySQL,专注于海量数据处理,数据平台。
MongoDB 有哪些特点?
- 建模灵活
- json数据模型
- 横向扩展简单
- 大数据量存储
- 高并发
MongoDB 是开源的吗?
- 社区版:居于SSPL协议,开源的
- 企业版:居于商业协议,需付费使用
7. MongoDB VS MySQL
| MongoDB | MySQL | |
|---|---|---|
| 数据模型 | 非关系型 | 关系型 |
| 存储方式 | 虚拟内存 + 持久化 | 不同的引擎有不同的存储方式 |
| 查询语句 | 独特的 MongoDB 查询方式 | 传统 SQL 语句 |
| 架构特点 | 可以通过副本集以及分片的方式来实现高可用 | 常见的架构方式有单点、M-S、MHA |
| 数据处理方式 | 基于内存,将热数据存在物理内存中,从而达到高速读写 | 不同的引擎有不同的特点 |
| 成熟度 | 新兴数据库,成熟度较低 | 拥有较为成熟的体系,成熟度较高 |
| 广泛度 | NoSQL 中,MongoDB 是较为完善的 DB 之一,使用人群在不断增长 | 开源数据库的份额在不断增加,MySQL的份额也在不断增长 |
8. MongoDB VS Redis
| 指标 | MongoDB(v2.4.9) | Redis(v2.4.17) | 比较说明 |
|---|---|---|---|
| 实现语言 | C++ | C/C++ | - |
| 协议 | BSON、自定义二进制 | 类Telnet | - |
| 性能 | 依赖内存,TPS较高 | 依赖内存,TPS非常高 | Redis优于MongoDB |
| 可操作性 | 丰富的数据表达、索引;最类似于关系数据库,支持丰富的查询语言 | 数据丰富,较少的IO | MongoDB优于Redis |
| 内存及存储 | 适合大数据量存储,依赖系统虚拟内存管理,采用镜像文件存储;内存占有率比较高,官方建议独立部署在64位系统(32位有最大2.5G文件限制,64位没有改限制) | Redis2.0后增加虚拟内存特性,突破物理内存限制;数据可以设置时效性,类似于memcache | 不同的应用角度看,各有优势 |
| 可用性 | 支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制 | 依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快照,无增量复制;不支持自动sharding,需要依赖程序设定一致hash机制 | MongoDB优于Redis;单点问题上,MongoDB应用简单,相对用户透明,Redis比较复杂,需要客户端主动解决。(MongoDB 一般会使用replica sets和sharding功能结合,replica sets侧重高可用性及高可靠性,而sharding侧重于性能、易扩展) |
| 可靠性 | 从1.8版本后,采用binlog方式(MySQL同样采用该方式)支持持久化,增加可靠性 | 依赖快照进行持久化;AOF增强可靠性;增强可靠性的同时,影响访问性能 | MongoDB优于Redis |
| 一致性 | 不支持事务,靠客户端自身保证 | 支持事务,比较弱,仅能保证事务中的操作按顺序执行 | Redis优于MongoDB |
| 数据分析 | 内置数据分析功能(mapreduce) | 不支持 | MongoDB优于Redis |
| 应用场景 | 海量数据的访问效率提升 | 较小数据量的性能及运算 | MongoDB优于Redis |
-
内存管理机制
- Redis 数据全部存在内存中,定期写入磁盘,当内存不足时,可以选择指定的算法删除数据
- MongoDB 数据存在内存中,由 Linux 系统的 mmap 实现,当内存不足时,只将热点数据存在内存,其它数据存入磁盘
-
支持的数据结构
- Redis 支持多种数据结构,比如 hash、set、list等
- MongoDB 数据结构比较单一,但支持丰富的数据表达、索引,最类似关系型数据库
-
数据量和性能
- 当物理内存够用时,redis > mongodb > mysql
- 当物理内存不够用时,redis 和 mongodb 都会使用虚拟内存
-
性能
- MongoDB 依赖内存,TPS 较高
- Redis 依赖内存, TPS 非常高
- 性能上Redis优于MongoDB
-
事务支持情况
- Redis 事务支持比较弱,只能保证事务中的每个操作连续执行
- MongoDB 不支持事务
-
集群
- MongoDB 集群技术比较成熟
- Redis 从 3.0 开始支持集群
9. SpringBoot 操作 MongoDB
(1)数据准备
db.users.insert({"id":NumberLong(1),"name":"dafei","age":NumberInt(18)})
db.users.insert({"id":NumberLong(2),"name":"xiaofei","age":NumberInt(20)})
db.users.insert({"id":NumberLong(3),"name":"zhang quan dan","age":NumberInt(33)})
db.users.insert({"id":NumberLong(4),"name":"zhang kun","age":NumberInt(26)})
db.users.insert({"id":NumberLong(5),"name":"zhang han yun","age":NumberInt(29)}
db.users.insert({"id":NumberLong(6),"name":"cai xv kun","age":NumberInt(29)})
db.users.insert({"id":NumberLong(7),"name":"jia nai liang","age":NumberInt(25)})
db.users.insert({"id":NumberLong(8),"name":"fu rong wang","age":NumberInt(28)})
db.users.insert({"id":NumberLong(9),"name":"wang da","age":NumberInt(31)})
db.users.insert({"id":NumberLong(10),"name":"da wang","age":NumberInt(32)})
db.users.insert({"id":NumberLong(11),"name":"will","age":NumberInt(26)})
(2)添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--spring boot data mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
(3)添加配置
# 配置数据库连接
#格式: mongodb://账号:密码@ip:端口/数据库?认证数据库
#spring.data.mongodb.uri=mongodb://root:admin@localhost/mongodemo?authSource=admin
spring.data.mongodb.uri=mongodb://localhost/mongodemo
# 配置MongoTemplate的执行日志
logging.level.org.springframework.data.mongodb.core=debug
(4)domain
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
@Document("users") //设置文档所在的集合
public class User {
// 文档的id使用ObjectId类型来封装,并且贴上@Id注解,
// 自动映射为_id 自动封装ObjectId
@Id
private String id;
private String name;
private Integer age;
private List<String> hobby = new ArrayList<>();
}
(5)repository
// 操作的domain,id类型
public interface UserMongoRepository extends MongoRepository<User, String> {
// 使用Spring Data命名规范做高级查询
User findByName(String name);
}
(6)service
public interface IUserService {
void save(User user);
void delete(String id);
void update(User user);
User get(String id);
List<User> list();
}
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMongoRepository userRepository;
@Override
public void save(User user) {
userRepository.save(user);
}
@Override
public void delete(String id) {
userRepository.deleteById(id);
}
@Override
public void update(User user) {
userRepository.save(user);
}
@Override
public User get(String id) {
Optional<User> optional = userRepository.findById(id);
return optional.orElse(null);
}
@Override
public List<User> list() {
return userRepository.findAll();
}
}
(7)Test
@SpringBootTest
public class MongoTest {
@Autowired
private IUserService userService;
@Test
public void testSave() {
User user = new User();
user.setName("regexp");
user.setAge(19);
user.setHobby(Arrays.asList("coding", "run", "ride"));
userService.save(user);
}
@Test
public void testUpdate(){
User user = new User();
user.setId("61b16a111bb9b5447ce7aff9");
user.setName("dafei2222");
user.setAge(18);
userService.update(user);
}
@Test
public void testDelete(){
userService.delete("61b16a111bb9b5447ce7aff9");
}
@Test
public void testGet(){
System.out.println(userService.get("61b1696b293b0000380073bc"));
}
@Test
public void testList(){
System.out.println(userService.list());
}
@Autowired
private MongoTemplate mongoTemplate;
@Test
public void testQuery() {
// 创建查询对象
Query query = new Query();
// 设置分页信息
query.skip(3).limit(3);
// 设置排序规则
query.with(Sort.by(Sort.Direction.ASC, "id"));
List<User> users = mongoTemplate.find(query, User.class, "users");
users.forEach(System.out::println);
}
@Autowired
private UserMongoRepository userMongoRepository;
@Test
public void testQuery2() {
User user = userMongoRepository.findByName("dafei");
System.out.println(user);
}
}
10. 小结
MongoDB 是一种以 JSON 为数据模型的非关系型数据库,MongoDB 是一种半内存数据库,可能会存在60秒间隔的数据丢失。MongoDB 具有简单直观、结构灵活、高可用与易拓展的特点,应用场景非常广泛,可以用于游戏、视频直播、日志记录、物流管理等。