既然有MySQL了,为什么还要有MongoDB?


大家好我是哪吒最近项目在使用MongoDB作为图片和文档的存储数据库为啥不直接存MySQL里还要搭个MongoDB集群麻不麻烦

让我们一起一探究竟了解一下MongoDB的特点和基本用法实现快速入门丰富个人简历提高面试level给自己增加一点谈资秒变面试小达人BAT不是梦

三分钟你将学会

  1. MongoDB主要特征
  2. MongoDB优缺点扬长避短
  3. 何时选择MongoDB为啥要用它
  4. MongoDB与MySQL关键字对比
  5. 下载与安装过程中一些常见的坑
  6. Java整合MongoDB实现农民工增删改查

一、基本概念走起

MongoDB是一款开源、跨平台、分布式具有大数据处理能力的文档存储数据库。

文档数据库MongoDB用于记录文档结构的数据比如JSON、XML结构的数据。

二、MongoDB的主要特征

  1. 高性能。提供JSON、XML等可嵌入数据快速处理功能提供文档的索引功能以提高查询速度
  2. 丰富的查询语言。为数据聚合、结构文档、地理空间提供丰富的查询功能
  3. 高可用性。提供自动故障转移和数据冗余处理功能
  4. 水平扩展能力。提供基于多服务器集群的分布式数据处理能力具体处理时分主从权衡基于Hash自动推选两种处理模式
  5. 支持多种存储引擎。MongoDB提供多种存储引擎WiredTiger引擎MMAPv1引擎是基于硬盘读写的存储引擎In-Memory引擎是基于内存的存储引擎

三、MongoDB优缺点扬长避短

1、优点

  1. Free-schema无模式文档适应非结构化数据存储
  2. 内置GridFS支持大容量的存储
  3. 内置Sharding分片简单
  4. 弱一致性最终一致更能保证用户的访问速度;
  5. 查询性能优越对于千万级别的文档对象差不多10个G对有索引的ID的查询不会比MySQL慢而对非索引字段的查询则是完胜MySQL;
  6. 聚合框架它支持典型几种聚合操作 , 比如Aggregate pipelien, Map-Reduce等
  7. 支持自动故障恢复

2、缺点

  1. 太吃内存快是有原因的因为MongoDB把数据都放内存里了
  2. 不支持事务操作
  3. 占用空间过大
  4. 不支持联表查询
  5. 只有最终一致性言外之意就是可能造成数据的不一致如果想要保持强一致性必须在一个服务器处理所有的读写操作坑
  6. 复杂聚合操作通过mapreduce创建速度慢
  7. Mongodb全局锁机制也是个坑
  8. 预分配模式会带来的磁盘瓶颈
  9. 删除记录时不会释放空间相当于逻辑删除这个真的坑
  10. MongoDB到现在为止好像还没有太好用的客户端工具

四、何时选择MongoDB为啥要用它

1、MongoDB事务

MongoDB目前只支持单文档事务MongoDB暂时不适合需要复杂事务的场景。
灵活的文档模型JSON格式存储最接近真实对象模型对开发者友好方便快速开发迭代可用复制集满足数据高可靠、高可用的需求运维较为简单、故障自动切换可扩展分片集群海量数据存储。

2、多引擎支持各种强大的索引需求

  • 支持地理位置索引
  • 可用于构建各种O2O应用
  • 文本索引解决搜索的需求
  • TTL索引解决历史数据过期的需求
  • Gridfs解决文件存储的需求
  • aggregation & mapreduce解决数据分析场景需求可以自己写查询语句或脚本将请求分发到 MongoDB 上完成。

3、具体的应用场景

传统的关系型数据库在解决三高问题上的力不从心。
何为三高

  • High performance - 对数据库高并发读写的需求。
  • Huge Storage - 对海量数据的高效率存储和访问的需求。
  • High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求。

MongoDB可以完美解决三高问题。

4、以下是几个实际的应用案例

1游戏场景

使用MongoDB存储游戏用户信息、装备、积分等直接以内嵌文档的形式存储方便查询、更新。

2物流场景

使用MongoDB存储订单信息、订单状态、物流信息订单状态在运送过程中飞速迭代、以MongoDB内嵌数组的形式来存储一次查询就能将订单所有的变更查出来牛逼plus。

3社交场景

使用MongoDB存储用户信息朋友圈信息通过地理位置索引实现附近的人、定位功能。

4物联网场景

使用MongoDB存储设备信息、设备汇报的日志信息、并对这些信息进行多维度分析。

5视频直播

使用MongoDB存储用户信息、点赞互动信息。

5、选择MongoDB的场景总结

  • 数据量大
  • 读写操作频繁
  • 数据价值较低对事务要求不高

五、MongoDB与MySQL关键字对比

1、关键字对比

MySQLMongoDB解释说明
databasedatabase数据库
tablecollection表/集合
rowdocument行/文档
columnfield字段/域
indexindex索引
join嵌入文档表关联/MongoDB不支持joinMongoDB通过嵌入式文档来替代多表连接
primary keyprimary key主键/MongoDB自动将_id字段设置为主键

2、集合相当于MySQL中的表

集合就是一组文档。可以看作是具有动态模式的表。

集合具有动态模式的特性。这意味着一个集合中的文档可以具有任意数量的不同形态。

但是将不同类型的文档存放在一个集合中会出现很多问题

  1. 文档中可以存放任意类型的变量但是这里不建议将不同类型的文档保存在同一个集合中开发人员需要确保每个查询只返回特定模式的文档或者确保执行查询的应用程序代码可以处理不同类型的文档
  2. 获取集合列表比提取集合中的文档类型列表要快得多减少磁盘查找次数
  3. 相同类型的文档存放在同一个集合中可以实现数据的局部性对于集合让使用者见文知意
  4. 集合中只存放单一类型的文档可以更高效地对集合进行索引

3、集合的命名

  1. 集合名称中不能是空字符串
  2. 集合名称不能包含\0空字符因为这个字符用于表示一个集合名称的结束
  3. 集合名称不能以system.开头该前缀是为内部集合保留的。
  4. 集合名称不能有$只能在某些特定情况下使用。通常情况下可以认为这两个字符是MongoDB的保留字符如果使用不当那么驱动程序将无法正常工作。

4、文档相当于MySQL中的行

文档是MongoDB中的基本数据单元相当于传统关系型数据库中的行它是一组有序键值的集合。每个文档都有一个特殊的键“_id”其在所属的集合中是唯一的。

文档中的键是字符串类型。

键中不能含有\0空字符。这个字符用于表示一个键的结束。
.和$是特殊字符只能在某些特定情况下使用。通常情况下可以认为这两个字符是MongoDB的保留字符如果使用不当那么驱动程序将无法正常工作。

5、游标

数据库会使用游标返回find的执行结果。游标的客户端实现通常能够在很大程度上对查询的最终输出进行控制。你可以限制结果的数量跳过一些结果按任意方向的任意键组合对结果进行排序以及执行许多其他功能强大的操作。

通过cursor.hasNext()检查是否还有其它结果通过cursor.next()用来对其进行获取。

调用find()时shell并不会立即查询数据库而是等到真正开始请求结果时才发送查询这样可以在执行之前给查询附加额外的选项。cursor对象的大多数方法会返回游标本身这样就可以按照任意顺序将选项链接起来了。

在使用db.users.find();查询时实际上查询并没有真正执行只是在构造查询执行cursor.hasNext()查询才会发往服务器端。shell会立刻获取前100个结果或者前4MB的数据两者之中的较小者这样下次调用next或者hasNext时就不必再次连接服务器去获取结果了。在客户端遍历完第一组结果后shell会再次连接数据库使用getMore请求更多的结果。getMore请求包含一个游标的标识符它会向数据库询问是否还有更多的结果如果有则返回下一批结果。这个过程会一直持续直到游标耗尽或者结果被全部返回。

6、游标的生命周期

在服务器端游标会占用内存和资源。一旦游标遍历完结果之后或者客户端发送一条消息要求终止数据库就可以释放它正在使用的资源。

何时销毁游标

  1. 当游标遍历完匹配的结果时它会消除自身
  2. 当游标超出客户端的作用域时驱动程序会向数据库发送一条特殊的消息让数据库终止该游标
  3. 如果10分钟没有被使用的话数据库游标也将自动销毁

六、下载与安装过程中一些常见的坑

1、下载地址https://www.mongodb.com/try/download/community2

2、配置环境变量D:\Program Files\MongoDB\Server\5.0\bin

3、在bin目录下重新打开一个窗口D:\Program Files\MongoDB\Server\5.0\bin打开cmd输入MongoDB

4、如果msi方式失败可以下载zip文件进行安装。

下载zip文件解压在bin同级目录下建data文件夹在data下建一个db文件夹存储MongoDB数据。

在bin文件夹下执行cmd执行mongod --dbpath D:\Program Files\mongodb\data\db命令

再在data目录下建一个logs文件夹存放MongoDB日志。

在mongodb/bin目录下建一个mongod.cfg文件写入

systemLog:
    destination: file
    logAppend: true
    path: D:\Program Files\mongodb\data\logs\mongod.log
storage:
    dbPath: D:\Program Files\mongodb\data\db

执行mongod --config "D:\Program Files\mongodb\bin\mongod.cfg" --install 命令安装MongoDB。

通过mongod --version检查MongoDB版本。

D:\Program Files\mongodb\bin>mongod --version
db version v5.0.14
Build Info: {
    "version": "5.0.14",
    "gitVersion": "1b3b0073a0b436a8a502b612f24fb2bd572772e5",
    "modules": [],
    "allocator": "tcmalloc",
    "environment": {
        "distmod": "windows",
        "distarch": "x86_64",
        "target_arch": "x86_64"
    }
}

5、mongodb由于目标计算机积极拒绝无法连接

突然间mongodb无法连接了mongod.exe --dbpath "D:\Program Files\mongodb\data完美解决。

注意一点在重新启动时执行mongod.exe --dbpath "D:\Program Files\mongodb\data的窗口不要关闭。

6、由于找不到vcruntime140_1.dll,无法继续执行代码

1下载vcruntime140_1.dll文件

2将vcruntime140_1.dll文件拷贝到C:\Windows\System32即可

七、Java整合MongoDB实现农民工增删改查

1、加入POM

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.8.2</version>
</dependency>

2、MongoDBUtil工具类

package com.example.demo.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.bson.Document;
import org.bson.conversions.Bson;

import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;

public class MongoDBUtil {

    private static MongoClient mongoClient;

    private static MongoClient mongoClientIdentify;

    /**
     * 不通过认证获取连接数据库对象
     */
    public static MongoDatabase getNoIdentifyConnect(String host, int port, String dbaseName) {
        // 连接mongodb服务
        MongoDBUtil.mongoClient = new MongoClient(host, port);

        // 连接数据库
        MongoDatabase mongoDatabase = MongoDBUtil.mongoClient.getDatabase(dbaseName);

        // 返回连接数据库对象
        return mongoDatabase;
    }

    /**
     * 通过连接认证获取MongoDB连接
     */
    public static MongoDatabase getIdentifyConnect(String host, int port, String dbaseName, String userName, String password) {

        List<ServerAddress> adds = new ArrayList<ServerAddress>();

        ServerAddress serverAddress = new ServerAddress(host, port);
        adds.add(serverAddress);

        List<MongoCredential> credentials = new ArrayList<>();

        MongoCredential mongoCredential = MongoCredential.createScramSha1Credential(userName, dbaseName, password.toCharArray());
        credentials.add(mongoCredential);

        // 通过连接认证获取MongoDB连接
        MongoDBUtil.mongoClientIdentify = new MongoClient(adds, credentials);

        MongoDatabase mongoDatabase = MongoDBUtil.mongoClientIdentify.getDatabase(dbaseName);

        return mongoDatabase;
    }

    /**
     * 关闭连接
     */
    public static void closeNoIdentifyConnect () {
        MongoDBUtil.mongoClient.close();
    }

    /**
     * 关闭连接
     */
    public static void closeIdentifyConnect () {
        MongoDBUtil.mongoClientIdentify.close();
    }

    /**
     * 插入一个文档
     */
    public static void insertOne (Map<String, Object> data, MongoDatabase mongoDatabase, String col) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);

        //创建文档
        Document document = new Document();

        for (Map.Entry<String, Object> m : data.entrySet()) {
            document.append(m.getKey(), m.getValue()).append(m.getKey(), m.getValue());
        }

        //插入一个文档
        collection.insertOne(document);
    }

    /**
     * 插入多个文档
     */
    public static void insertMany (List<Map<String, Object>> listData, MongoDatabase mongoDatabase, String col) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);

        //要插入的数据
        List<Document> list = new ArrayList<>();
        for (Map<String, Object> data : listData) {
            //创建文档
            Document document = new Document();

            for (Map.Entry<String, Object> m : data.entrySet()) {
                document.append(m.getKey(), m.getValue());
            }
            list.add(document);
        }

        //插入多个文档
        collection.insertMany(list);
    }

    /**
     * 删除匹配到的第一个文档
     */
    public static void delectOne (String col, String key, Object value, MongoDatabase mongoDatabase) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);
        //申明删除条件
        Bson filter = Filters.eq(key, value);
        //删除与筛选器匹配的单个文档
        collection.deleteOne(filter);
    }

    /**
     * 删除匹配的所有文档
     */
    public static void deleteMany (String col, String key, Object value, MongoDatabase mongoDatabase) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);
        //申明删除条件
        Bson filter = Filters.eq(key, value);
        //删除与筛选器匹配的所有文档
        collection.deleteMany(filter);
    }

    /**
     * 删除集合中所有文档
     */
    public static void deleteAllDocument(String col, MongoDatabase mongoDatabase) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);
        collection.deleteMany(new Document());
    }

    /**
     * 删除文档和集合。
     */
    public static void deleteAllCollection(String col, MongoDatabase mongoDatabase) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);
        collection.drop();
    }

    /**
     * 修改单个文档修改过滤器筛选出的第一个文档
     *
     * @param col 修改的集合
     * @param key 修改条件的键
     * @param value 修改条件的值
     * @param eqKey 要修改的键如果eqKey不存在则新增记录
     * @param eqValue 要修改的值
     * @param mongoDatabase 连接数据库对象
     */
    public static void updateOne (String col, String key, Object value,String eqKey, Object eqValue, MongoDatabase mongoDatabase) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);
        //修改过滤器
        Bson filter = Filters.eq(key, value);
        //指定修改的更新文档
        Document document = new Document("$set", new Document(eqKey, eqValue));
        //修改单个文档
        collection.updateOne(filter, document);
    }

    /**
     * 修改多个文档
     *
     * @param col 修改的集合
     * @param key 修改条件的键
     * @param value 修改条件的值
     * @param eqKey 要修改的键如果eqKey不存在则新增记录
     * @param eqValue 要修改的值
     * @param mongoDatabase 连接数据库对象
     */
    public static void updateMany (String col, String key, Object value, String eqKey, Object eqValue, MongoDatabase mongoDatabase) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);
        //修改过滤器
        Bson filter = Filters.eq(key, value);
        //指定修改的更新文档
        Document document = new Document("$set", new Document(eqKey, eqValue));
        //修改多个文档
        collection.updateMany(filter, document);
    }

    /**
     * 查找集合中的所有文档
     */
    public static MongoCursor<Document> find (String col, MongoDatabase mongoDatabase) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);
        //查找集合中的所有文档
        FindIterable<Document> findIterable = collection.find();
        MongoCursor<Document> cursorIterator = findIterable.iterator();
        return cursorIterator;
    }

    /**
     * 按条件查找集合中文档
     */
    public static MongoCursor<Document> Filterfind (String col,String key, Object value, MongoDatabase mongoDatabase) {
        //获取集合
        MongoCollection<Document> collection = mongoDatabase.getCollection(col);
        //指定查询过滤器
        Bson filter = Filters.eq(key, value);

        //指定查询过滤器查询
        FindIterable<Document> findIterable = collection.find(filter);
        MongoCursor<Document> cursorIterator = findIterable.iterator();
        return cursorIterator;
    }
}

3、测试类

<dependency>
   <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
package com.example.demo.utils;

import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.junit.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MongoDBTest {

    // 获取数据库连接对象
    MongoDatabase mongoDatabase = MongoDBUtil.getNoIdentifyConnect("127.0.0.1", 27017, "test");

    @Test
    public void insertOne() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("姓名", "哪吒编程");
        map.put("性别", "男");
        map.put("年龄", 18);
        MongoDBUtil.insertOne(map, mongoDatabase, "worker");
        MongoDBUtil.closeNoIdentifyConnect();
    }

    @Test
    public void insertMany() {
        Map<String, Object> map1 = new HashMap<String, Object>();
        map1.put("姓名", "哪吒编程2");
        map1.put("性别", "男");
        map1.put("年龄", 18);
        Map<String, Object> map2 = new HashMap<String, Object>();
        map2.put("姓名", "妲己");
        map2.put("性别", "女");
        map2.put("年龄", 18);
        List<Map<String, Object>> listData = new ArrayList<>();
        listData.add(map1);
        listData.add(map2);
        MongoDBUtil.insertMany(listData, mongoDatabase, "worker");
        MongoDBUtil.closeNoIdentifyConnect();
    }

    @Test
    public void delectOne() {
        MongoDBUtil.delectOne("worker", "姓名", "妲己", mongoDatabase);

        MongoDBUtil.closeNoIdentifyConnect();
    }

    @Test
    public void deleteMany() {

        MongoDBUtil.deleteMany("worker", "姓名", "哪吒编程", mongoDatabase);
        MongoDBUtil.deleteMany("worker", "姓名", "妲己", mongoDatabase);

        MongoDBUtil.closeNoIdentifyConnect();
    }

    @Test
    public void deleteAllDocument() {
        MongoDBUtil.deleteAllDocument("worker", mongoDatabase);

        MongoDBUtil.closeNoIdentifyConnect();
    }

    @Test
    public void deleteAllCollection() {

        MongoDBUtil.deleteAllCollection("worker", mongoDatabase);

        MongoDBUtil.closeNoIdentifyConnect();
    }

    @Test
    public void updateOne() {

        MongoDBUtil.updateOne("worker", "姓名", "哪吒编程2","姓名", "哪吒编程", mongoDatabase);

        MongoDBUtil.closeNoIdentifyConnect();
    }

    @Test
    public void updateMany() {

        MongoDBUtil.updateMany("worker", "姓名", "哪吒编程2","姓名", "哪吒编程", mongoDatabase);

        MongoDBUtil.closeNoIdentifyConnect();
    }

    @Test
    public void find() {
        MongoCursor<Document> mongoCursor = MongoDBUtil.find("worker", mongoDatabase);

        while (mongoCursor.hasNext()) {
            Document document = mongoCursor.next();
            System.out.println(document + "  size: " + document.size());
        }
        MongoDBUtil.closeNoIdentifyConnect();
    }

    @Test
    public void filterfind() {
        MongoCursor<Document> mongoCursor = MongoDBUtil.Filterfind("worker", "姓名", "哪吒编程", mongoDatabase);

        while (mongoCursor.hasNext()) {
            Document document = mongoCursor.next();
            System.out.println(document + "  size: " + document.size());
        }
        MongoDBUtil.closeNoIdentifyConnect();
    }
}

在这里插入图片描述

Java学习路线总结搬砖工逆袭Java架构师

10万字208道Java经典面试题总结(附答案)

Java基础教程系列

Java高并发编程系列

数据库进阶实战系列

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: mysqlgo