学 MongoDB?理论实战一起学!

210 阅读8分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

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

MongoDBMySQL
数据模型非关系型关系型
存储方式虚拟内存 + 持久化不同的引擎有不同的存储方式
查询语句独特的 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 具有简单直观、结构灵活、高可用与易拓展的特点,应用场景非常广泛,可以用于游戏、视频直播、日志记录、物流管理等。