分布式文件存储数据库MongoDB技术学习总结

一、MongoDB速探

含义:

MongoDB是由C++语言编写的、基于分布式文件存储的非关系型数据库。在大数据量下承载性能好。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。

优点:

MongoDB为什么在大数据量下有较高的查询性能?

  • 相对于关系型数据库而言,不需要提前创建表,以及表结构
  • 存储持久化,面向集合存储;易存储对象类型的数据、实现对象到集合的映射
  • 性能方面能够快速查询( 支持动态查询、索引)
  • 使用轻量级的 json格式存储数据

缺点:

  • 在集群分片中的数据分布不均匀
  • 单机可靠性比较差
  • 大数据量持续插入,写入性能有较大波动
  • 磁盘空间占用比较大

使用场景:

  • 网站数据 比如网站实时操作 插入 、更新、查询
  • 存放大量的系统缓存数据
  • 大量低价值的数据的存储、比如日志监控和爬虫数据
  • json格式的数据

MongoDB和Redis的简单对比:

相比于Redis可以做出如下的归纳:

1、Redis主要把数据存储在内存中,其“缓存”的性质远大于其“数据存储“的性质,其中数据的增删改查也只是像变量操作一样简单;

2、MongoDB却是一个“存储数据”的系统,增删改查可以添加很多where条件,就像操作SQL数据库一样灵活;但是如果在redis中做根据条件查询就会显得比较困难,在redis中更多是直接根据存储的key直接获取对应的value;

二、基本环境搭建

基于linux的centos操作系统安装MongoDB服务端,在根目录新建文件夹名称为15512,wget命令下载安装包:

cd /
mkdir 15512
cd /15512
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.0.tgz

解压压缩包,重命名解压后的文件夹为mongodb:

tar -zxvf mongodb-linux-x86_64-4.0.0.tgz
mv mongodb-linux-x86_64-4.0.0 mongodb

配置系统的环境变量,并使配置生效:

vim /etc/profile
export PATH=/15512/mongodb/bin:$PATH
source /etc/profile

创建数据库目录,添加配置文件,修改文件夹权限:

cd /15512/mongodb
touch mongodb.conf
mkdir db
mkdir log
chmod 777 db
chmod 777 log
cd log
touch mongodb.log

编辑MongoDB的配置文件mongodb.conf:

vim /15512/mongodb/mongodb.conf

#访问端口
port=27017
#数据库的数据存放目录
dbpath= /15512/mongodb/db
#数据库的日志文件存放路径
logpath= /15512/mongodb/log/mongodb.log
#使用追加的方式写日志
logappend=true
#以守护进程的方式运行,创建服务器进程
fork=true
#最大同时连接数
maxConns=100
#不启用验证
noauth=true
#每次写入会记录一条操作日志
journal=true
#存储引擎有mmapv1、wiretiger、mongorocks
storageEngine=wiredTiger
#这样就可外部访问了
bind_ip = 0.0.0.0

指定配置文件mongodb.conf启动MongoDB服务:

cd /15512/mongodb/bin
mongod --config /15512/mongodb/mongodb.conf

查看进程是否正常启动: 

ps aux|grep mongodb

如图所示,MongoDB服务进程已经正常启动: 

三、测试访问

本机直接访问测试:

本地使用studio 3T图形化界面远程连接MongoDB服务进行测试:

监控MongoDB服务器的状态信息: 

3

四、概念解析

MongoDB作为文档型数据库,本质上是将数据存储为一个文档document,数据结构由key-value组成。这种key-value结构在MongoDB中我们称这种结构为BSON(大家之前应该都很了解JSON)。MongoDB的值可以包含其他的文档、数组和文档数组,构造出更复杂的数据机构。

BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型。
BSON有三个特点:轻量性、可遍历性、高效性。

BSON主要会实现以下三点目标:
(1)更快的遍历速度
对JSON格式来说,太大的JSON结构会导致数据遍历非常慢。在JSON中,要跳过一个文档进行数据读取,需要对此文档进行扫描才行,需要进行麻烦的数据结构匹配,比如括号的匹配。
(2)操作更简易
对JSON来说,数据存储是无类型的,比如你要修改基本一个值,从9到10,由于从一个字符变成了两个,所以可能其后面的所有内容都需要往后移一位才可以。而使用BSON,你可以指定这个列为数字列,那么无论数字从9长到10还是100,我们都只是在存储数字的那一位上进行修改,不会导致数据总长变大。
(3)增加了额外的数据类型
JSON是一个很方便的数据交换格式,但是其类型比较有限。BSON在其基础上增加了“byte array”数据类型。这使得二进制的存储不再需要先base64转换后再存成JSON。大大减少了计算开销和数据大小。

术语概念

通过与关系型数据库的对比,我们可以更加清楚在MongoDB中各个概念的实际含义:

通过图形化的界面,可以动态地切换数据的三种展示方式,比如以树形结构、以表格table的结构、以Json的结构来进行展示,可以直观看清楚数据的对应关系:

比如现在在hres这个datahbase下有一个collection(关系型数据库中的table)名字叫做:history

树形结构展示,更能提现出document(文档)的特点:

Table表格结构展示,等价于二维表:

Json结构展示,和redis中存储的结构类似:

字段数据类型

这里面一共罗列了15种数据类型,其中有一种ObjectID需要特别的说明下,ObjectId是在向MongoDB插入数据时由MongoDB自己生成的唯一主键,一共有12个bytes.

  • 前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
  • 接下来的 3 个字节是机器标识码
  • 紧接的两个字节由进程 id 组成 PIDv
  • 最后三个字节是随机数

五、数据库操作

显示所有的数据库

show dbs

新建数据库 use [数据库名],如果有数据库则切换至该数据库,如果没有数据库则新建该数据库,需要特别注意的是,新建的空数据库,如果里面没有数据,则默认不会直接显示。

use demo

删除数据库:

切换到拟删除的数据库

use demo

执行删除命令删除:

db.dropDatabase()

数据库的命名规范:

  • 不能是空字符串("")
  • 不得含有' '(空格)、.、$、/、\和\0 (空字符)
  • 应全部小写
  • 最多64字节

默认库说明:

  • admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
  • local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
  • config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息

六、集合操作

集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。

集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

创建集合:

db.createCollection(name, options)
  • name: 要创建的集合名称
  • options: 可选参数, 指定有关内存大小及索引的选项

相比于关系型数据库,对于自增主键ID会默认有主键唯一索引,但是在mongodb中需要自己去指定。这个特性在未来版本也会移除,具体要看未来的特点。

简单一些创建用户集合:

db.createCollection("user")

查询当前db中的集合:

show collections

删除集合user:

db.user.drop()

集合的命名规范:

  • 集合名不能是空字符串""
  • 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾
  • 集合名不能以"system."开头,这是为系统集合保留的前缀
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$

七、文档的操作

插入文档:

使用insert或者save方法向mongodb中插入文档:

db.role.insert({"roleId":1,"roleName":"管理员","tenantId":0})

查询文档:

db.role.find()
{ "_id" : ObjectId("5dba4474fe50c397b6c911a9"), "roleId" : 1, "roleName" : "管理员", "tenantId" : 0 }

以易读的方式展示文档,pretty()方法:

db.role.find().pretty()
{
        "_id" : ObjectId("5dba4569fe50c397b6c911aa"),
        "roleId" : 1,
        "roleName" : "平台管理员",
        "tenantId" : 0
}

条件运算符:

limit读取指定数据量的记录&skip跳过指定数目的记录:

比如显示输出两条记录并跳过第一条记录:

查询排序:

更新文档:

将role集合中的角色名称为管理员的行记录更新为“平台管理员”

db.role.update({"roleName":"管理员"},{$set:{"roleName":"平台管理员"}})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

看一下更新后的document:

db.role.find()
{ "_id" : ObjectId("5dba4569fe50c397b6c911aa"), "roleId" : 1, "roleName" : "平台管理员", "tenantId" : 0 }

删除文档:

 db.role.remove({"roleName":"平台管理员"})

 

八、MongoDB的索引

创建索引的基本语法:

db.collection.createIndex(keys, options)

语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。

创建索引时的可选参数:

 db.role.createIndex({"age":1},{background: true})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

查看集合索引:

db.col.getIndexes()

> db.role.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "demo.role"
        }
]

查看集合索引大小:

db.col.totalIndexSize()

删除集合所有的索引:

db.col.dropIndexes()

删除集合指定的索引:

db.col.dropIndex("索引名称")

mongodb的索引算法主要是btree和hash算法,mongodb默认采用的是btree索引算法。

索引的作用:

  • 索引提高查询速度,降低写入速度,权衡常用的查询字段,不必在太多列上建索引
  • 在mongodb中,索引可以按字段升序/降序来创建,便于排序
  • 默认是用btree来组织索引文件,2.4版本以后,也允许建立hash索引.

接下来,我们一起分析下执行计划,看下索引的运作过程:打算在role这个集合上针对age这个field建立索引,来分析下执行计划:

首先如下图所示,是不加索引的执行计划:

可以清楚的看到整个的执行计划包括两个大的方面,第一是queryPlanner查询计划,第二个是serverInfo也就是MongoDB服务器的一些基本信息。

在querPlanner查询计划中:

这里也可以看到,当我们没有针对age字段加索引时,执行计划告诉我们,此查询走的方式为全表扫描,过滤的条件是age=20 这种如果在大数据量下全表扫描,效率还是会比较低的。下面我们基于age字段建立索引:

> db.role.createIndex({"age":1},{background: true})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

可以看到此时的查询方式为索引扫描IXSCAN并且索引的名称为age_1。

九、SpringBoot和MongoDB的整合

准备工作:

  • 安装MongoDB
  • jdk1.8+
  • Maven3.0+
  • idea

创建基本的spring boot项目,pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.wujie</groupId>
    <artifactId>springboot-mongodb</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>springboot-mongodb</name>
    <description>Demo project for Spring Boot</description>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

配置数据源:

spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/mydb
server:
  port: 80

实体对象层:

public class Student {
    /**
     * id
     */
    @Id
    private String id;
    /**
     * 姓名
     */
    private String name;
    /**
     * 性别
     */
    private String sex;
 
    。。。省略get/set方法
 
}

DAO层:

写一个接口,继承MongoRepository,这个接口有了基本的CRUD的操作,当不能满足需求的时候,可以进行一些自定的方法。

public interface StudentRepository extends MongoRepository<Student,String> {
    public Student findByName(String name);
    public List<Student> findBySex(String sex);
}

service层:

public interface StudentService {
    public Student add (Student student);
    public UpdateResult update (Student student);
    public void delete(String id);
    public Student findByName(String name);
    public List<Student> findBySex(String sex);
}

service实现类:

@Service
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentRepository studentRepository;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Override
    public Student add(Student student) {
        Student student1 = studentRepository.save(student);
        return student1;
    }
 
    @Override
    public UpdateResult update(Student student) {
        Query query = new Query();
        Criteria criteria = new Criteria();
        query.addCriteria(Criteria.where("_id").is(student.getId()));
        String collectionsName = "student";
        Update update = new Update();
        update.set("_id",student.getId());
        UpdateResult result = mongoTemplate.updateFirst(query, update, collectionsName);
        return result;
    }
 
    @Override
    public void delete(String id) {
        studentRepository.deleteById(id);
    }
 
    @Override
    public Student findByName(String name) {
       return studentRepository.findByName(name);
    }
 
    @Override
    public List<Student> findBySex(String sex) {
        return studentRepository.findBySex(sex);
    }
}

Controller层:

@RestController
@RequestMapping("student")
public class StudentController {
    @Autowired
    private StudentService studentService;
    @RequestMapping("add")
    public Student add(Student student){
        return  studentService.add(student);
    }
    @RequestMapping("update")
    public UpdateResult update(Student student){
        return studentService.update(student);
    }
 
    @RequestMapping("findByName")
    public Student findByName(String name){
        return studentService.findByName(name);
    }
    @RequestMapping("findBySex" )
    public List<Student> findBySex(String sex){
        return studentService.findBySex(sex);
    }
 
    @RequestMapping("delete")
    public String delete(String id){
        studentService.delete(id);
        return "success";
    }
}

MongoDbHelper工具类

@Component
@Slf4j
public class MongoDbHelper<T> implements InitializingBean {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public void afterPropertiesSet() {
    }

    /**
     * 分页查询
     *
     * @param pageRequest  分页请求对象
     * @param query        查询对象
     * @param tClass       对象class
     * @param sortProperty 排序属性
     * @return Page<T>
     */
    public Page<T> doPage(PageRequest pageRequest, Query query, Class<T> tClass, String sortProperty) {
        if (pageRequest.getPage() >= 0) {
            //find需要带条件 Query对象
            //指定条件排序
            Sort sort;
            if (pageRequest.getSort().iterator().next().getDirection().isAscending()) {
                sort = new Sort(Sort.Direction.ASC, sortProperty);
            } else if (pageRequest.getSort().iterator().next().getDirection().isDescending()) {
                sort = new Sort(Sort.Direction.DESC, sortProperty);
            } else {
                //默认降序
                sort = new Sort(Sort.Direction.DESC, sortProperty);
            }
            List<T> list = mongoTemplate.find(query.with(CommonUtils.toSpringPage(pageRequest, sort)), tClass);
            int count = (int) mongoTemplate.count(query, tClass);
            return new Page<>(list, new PageInfo(pageRequest.getPage(), pageRequest.getSize()), count);
        } else {
            return this.select(query, tClass);
        }
    }

    /**
     * 查询方法
     *
     * @param query  查询对象
     * @param tClass 对象class
     * @return Page<T>
     */
    public Page<T> select(Query query, Class<T> tClass) {
        List<T> list = mongoTemplate.find(query, tClass);
        int count = (int) mongoTemplate.count(query, tClass);
        return new Page<>(list, new PageInfo(0, count == 0 ? 1 : count), count);
    }

    /**
     * 根据条件查询单个对象
     *
     * @param query  查询对象
     * @param tClass 对象class
     * @return T
     */
    public T selectOne(Query query, Class<T> tClass) {
        return mongoTemplate.findOne(query, tClass);
    }

    /**
     * 查询所有对象
     *
     * @param tClass
     * @return List<T>
     */
    public List<T> selectAll(Class<T> tClass) {
        return mongoTemplate.findAll(tClass);
    }

    /**
     * 插入对象
     *
     * @param dto 待插入的实体对象
     * @return T
     */
    public T insert(T dto) {
        CommonUtils.setWhoStatusAndObjectVersionNumber(true, dto);
        mongoTemplate.save(dto);
        return dto;
    }

    /**
     * 根据条件删除担心
     *
     * @param query  查询对象
     * @param tClass 对象class
     */
    public void delete(Query query, Class<T> tClass) {
        mongoTemplate.remove(query, tClass);
    }

    /**
     * 根据条件更新对象
     *
     * @param query  查询对象
     * @param update 更新条件
     * @param tClass 对象class
     */
    public void update(Query query, Update update, Class<T> tClass) {
        mongoTemplate.updateFirst(query, update, tClass);
    }

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值