zoukankan      html  css  js  c++  java
  • MongoDB学习笔记(七、MongoDB总结)

    1、为什么要NoSQL:nosql能解决sql中那些解决不了的问题

    NoSQL是什么:Not Only SQL,本质上还是数据库,但它不会遵循传统数据库的规则(如:SQL标准、ACID属性[事务]、表结构等)。

    优点:

    • 处理大量数据时性能高。
    • 对磁盘读写要求不高,可以运行在便宜的PC机上,降低服务器成本。

    缺点:

    • 对事务的支持不够友好
    • 复杂的关联查询难以实现
    传统SQL与NoSQL的比较
    传统SQL

    1、吞吐量小,无法支持高并发读写

    2、结构要求严谨(增改一个字段麻烦),复杂系统中难以维护此关系

    NoSQL

    1、吞吐量大,支持海量数据的快速读写(基于内存操作数据)

    2、增改字段非常容易

     

    2、MongoDB简介

    MongoDB是NoSQL的一种,它是一个文档型数据库

    MongoDB与MySQL比较
    MySQL MongoDB
    db database(数据库)
    table collection(集合)
    row document(文档)
    column field(字段)
    index  index(索引)
    join 无关联(可以用DBRef实现)
    primaryKey primaryKey(主键,客户端默认使用_id,ObjectId)

     

    特性:

    • 数据存储方式:面向集合文档存储数据,以独有的bson格式存储
    • 可扩展性:可扩展性好,修改数据后不会影响生产环境的程序运行
    • 语言特性:强大且面向对象的查询语言,基本覆盖了sql语言所有能力
    • 索引和查询计划:完整的索引支持和查询计划
    • 集群、分片、内部故障支持:支持集群之间的数据复制、自动故障转移、支持数据的分片,提升系统扩展性
    • 数据操作方式:使用内存映射存储引擎,把IO操作转换成内存操作(不是只用内存,而是通过内存提高读写性能)

    3、MongoDB应用场景

    只要满足以上两点,选择MongoDB就绝对不会错!!!

    但系统需要高一致的事务性,不推荐使用(如:银行、财务系统)。

    系统结构固定且有复杂的关联查询系统,不推荐使用。

    4、MongoDB数据结构

    5、增删改查

    a、新增,insert()

    db.collectionName.insert(document)

    document有两种方式:

    1、直接放入数据

    db.collectionName.insert({
        ......
    })

    2、新定义变量再放入数据

    var user = {
        ......
    }
    db.collectionName.insert(user)

    b、查询,find()、aggregate()

    db.collectionName.find(
        query(查询的条件),
        <projection>(可用自参数指定返回字段,0=不展示,1=展示;0和1不能同时出现)
    )

    查询选择器:

    常用的查询方法:

    1、sort():排序,1=升序,-1=降序

    2、skip(count)、limit(count):跳过和限制。

    3、distinct('fieldName'):查询唯一值。

    关联查询:

    DBRef插入数据: {'$ref':'collectionName', '$id':'所在集合的_id值', '$db':'dbName(可选)'}

    DBRef查询数据: db.collectionName.findOne({'name':'zd'}).userId.fetch()

    聚合查询:

    1、分组: $group,将数据分组后再统计结果。

    • $sum、$avg、$min、$max:获取分组集合中的总和、平均值、最大值、最小值
    • $push:将指定表达式添加到一个数组中
    • $addToSet:将指定表达式添加到集合中(无重复)
    • $first:返回每组第一个文档,如有排序按照排序返回,没有则按照文档的默认顺序
    • $last:同$first,但返回最后一个文档

    2、投影: $project,输出指定字段(0=不显示,1显示)。

    3、过滤: $match,只输出指定条件(使用MongoDB标准查询选择器)。

    4、限制: $limit,只输出指定个数。

    5、跳过: $skip,跳过输出指定个数。

    6、排序: $sort,排序(1=升序,-1=降序)。

    7、拆分: $unwind,将文档数组类型字段拆分成多条。

    c、删改,update()

    db.collectionName.update(
        <query>, // 类似于sql的where
        <updateSelector> // 类似于sql的set
        {
            upsert:<boolean>, // true=query不存在插入新的,false=不插入新的(默认)
            multi:<boolean>, // true=只更新找到的第一条记录,false=全部更新(默认)
            writeCocern:<document> // 写入的安全配置
        }
    )

    修改选择器(uupdateSelector):

    修改的原子性:

    日常开发中,你可能会经常使用以下操作:

    int row = update();
    if (row > 0) {
        findByName();
    }

    正常清下下的确没有问题,但高并发场景下这样写便会有问题,因为一但update()完毕后就可以拿到锁了,而这时可能就会有其他操作把此用户的数据修改;

    故findByName()可能并能拿到update()之后的数据,而是拿到了最终的结果,这样可能会影响业务。

    而MongoDB提供了原子性的更新:

    db.collectionName.findAndModify({
        query:{}, // 查询选择器
        update:{}, // 需要更新的值,不能与remove同事出现
        remove:true|false, // 删除符合条件的文档,不能与update同时出现
        new:true|false, // true=返回更新后的文档,false=旧的文档
        sort:{}, // 排序
        fields:{}, // 显示或隐藏指定的值
        upsert:true|false // 同update的upsert
    })

     6、ObjectId()

    因新增数据返回的是受影响的函数,无法返回ObjectId;这里有一个小技巧,通过update代替insert获取ObjectId

    db.collectionName.update(
        {'':''}, // 永远不满足的查询条件
        {'username':'zd'}, // 这里写需要新增的数据
        {'upsert':true} // 因为查询条件永不满足,所以upsert=true便可新增数据
    )

    7、MongoDB存储引擎

    a、WiredTiger

    WiredTiger引擎是文档并发级别,其数据写入方式是以检查点为单位,每1min创建一个检查点将数据快照写入磁盘

    因检查点的特性所以服务器宕机的话最大可能会丢失1min左右的数据,针对这一情况MongoDB采用journal来进一步的优化。

    journal会记录检查点之间所有的操作日志,用于数据同步;其日志文件最小128B,当日志文件小于128B则不启用压缩算法,反之则启用snappy算法压缩。

    压缩算法:以消耗CPU资源换取磁盘空间,分块压缩算法压缩集合,snappy压缩算法压缩索引(前缀压缩算法)。

    内存使用情况:(RAM - 1GB) * 0.5256M ,默认使用较大的那一个。

    b、MMAPv1

    MMAPv1存储引擎的数据都是连续存储在磁盘上,当document需要更大的空间时,MongoDB必须重新分配空间;此间还涉及到数据的移动和索引的更新,不仅比直接更新更耗费时间,而且还会导致磁盘碎片。

    因MMAPv1的特性MongoDB会为每个document分配两倍的空间。

    内存使用情况:使用全部内存。

    c、InMemory

    基于内存的一种存储引擎,它是文档级别的并发,默认使用内存为 RAM * 0.5 - 1GB

    8、Journal原理分析

    Journal流程分析:首先找到数据文件中最后一个检查点,然后在Journal中检索与其相匹配的记录,最后将Journal中未匹配的数据恢复。

    工作原理:https://www.processon.com/view/5c349f6ee4b048f108c78a52

    9、MongoDB索引

    索引主要用于排序和检索(1=升序,-1=降序),其分为4种:

    • 单键索引:db.collectionName.createIndex({'name':-1})
    • 复合索引:db.collectionName.createIndex({'name':-1, 'age':1})
    • 多键索引:db.collectionName.createIndex({'address.city':-1})
    • 哈希索引:db.collectionName.createIndex({'name':'hashed'})
    db.collectionName.createIndex(
        {'name':1}, // 索引名称
        {
            'background':true, // 是否后台构建索引
            'unique':true, // 是否是唯一索引
            'sparse':true // 是否为稀疏索引
        }
    )

    索引的删除:

    • 删除指定姓名:db.collectionName.dropIndex('indexName')
    • 删除集合上的索引(_id删不掉):db.collectionName.dropIndexs()
    • 重建集合上的索引:db.collectionName.reIndex()
    • 查询集合上的索引:db.collectionName.getIndexs()

    关于索引的建议:

    • 根据需求建立索引,它有用但也有成本,不要对那些写多读少的建立索引
    • 尽量保证每个查询的stage都为IXSCAN,追求扫描文档数(totalDocsExamined) = 返回文档数(nReturned)
    • MongoDB在一次查询中只使用一个索引,如果多条件查询的尽量使用复合索引
    • 在数据量多的时候建立索引是非常消耗资源的,所以尽量在数据量小的时候就把索引建好

    10、可复制集

    在MongoDB中可复制集是服务器分布和维护数据的方法,其实就是主从复制的升级。

    优点:

    • 它可以尽可能的避免数据丢失,保障数据的安全性提高系统安全性。(最少3个节点,最大50个
    • 它具有自动化灾备机制,在主节点宕机后会选举出一个新的主节点,提高系统的健壮性。(7个选举节点上限
    • 读写分离,提高系统性能

    原理:

    a、oplog:保存操作记录时间戳

    b、数据同步:主从保持长轮询

    • 从节点查看本机oplog最新的时间戳
    • 查看主节点oplog中晚于此时间戳的文档
    • 加载这些文档,并根据log执行写操作

    c、心跳机制:每2秒进行一次心跳检测,发现故障后会进行选举和故障转移。

    d、选举制度:当主节点故障后,其余节点根据优先级和bully算法选举出新的主节点,在此之间集群服务是只读的。

    11、读写分离

    MongoDB读数据的方式分为5种:

    • PRIMARY(默认):读操作都在主节点,若主节点不可用则报错。
    • PRIMARY_PREFERRED:首选主节点,若主节点不可用则转移到其它从节点。
    • SECONDARY:读从节点,不可用则报错。
    • SECONDARY_PREFERRED(推荐):首选从节点,若是特殊情况则在主节点读(但主节点架构)。
    • NEAREST:最邻近主节点。

    12、分片

    分片架构的三个主要角色:

    • 分片:分片架构中唯一存储数据的角色,它可以是单台服务器也可以是一个可复制集(生成环境推荐使用可复制集),每个分区上只存储部分数据
    • 路由:由于分片只存储部分数据,所以需要一个工具(工具为mongos)来将请求处理到对应的分片中,而路由就充当这一角色。
    • 配置服务器:存储集群的元数据(数据库、集合、分片的位置范围等日志信息),配置服务器最低3台。

    分片键选择的一些建议:

    a、不推荐点:

    • 不要使用自增长的字段作为分片键,避免热点问题。
    • 不能使用粗粒度的分片键,避免数据块无法分割。
    • 不能使用完全随机的分片键值,这样会造成查询性能低下。

    b、推荐点:

    • 使用与常用查询相关的字段作为分片键,且包含唯一字段(如业务主键,id等)。
    • 索引对于分区同样重要,每个分片集合上要有同样的索引,分片键默认成为索引。
    • 分片集合只允许在id和分片键上创建唯一索引。

    13、MongoDB最佳实践

    尽量选取稳定新版本64位的MongoDB。

    数据模式设计;提倡单文档设计,将关联关系作为内嵌文档或者内嵌数组;当关联数据量较大时,考虑通过表关联实现,dbref或者自定义实现关联。

    避免使用skip跳过大量数据

    • 通过查询条件尽量缩小数据范围。
    • 利用上一次的结果作为条件来查询下一页的结果。

    避免单独使用不适用索引的查询符($ne、$nin、$where等)。

    根据业务场景选择合适的写入策略,在数据安全和性能之间找到平衡点。

    建立索引很重要

    生产环境中建议打开profile,便于优化系统性能。

    生产环境中建议打开auth模式,保障系统安全。

    不要将MongoDB和其他服务部署在同一台机器上(虽然MongoDB 占用的最大内存是可以配置的)。

    单机一定要开启journal日志,数据量不太大的业务场景中,推荐多机器使用副本集,并开启读写分离。

    分片键的注意事项。

  • 相关阅读:
    Linux 的文件软链接如何删除
    mysql-xtrabackup备份sh: xtrabackup_56: command not found与error while loading shared libraries: libssl.so.6: cannot open shared object file: No such file or directory
    How To Upgrade ASMLib Kernel Driver as Part of Kernel Upgrade? (文档 ID 1391807.1)
    [trouble] error connecting to master 'repl@192.168.1.107:3306'
    Troubleshooting 10g and 11.1 Clusterware Reboots (文档 ID 265769.1)
    "Last_IO_Error: Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs
    RMAN-06900 RMAN-06901 ORA-19921
    一则ORACLE进程都在但是无法进入实例的问题
    VirtualBox下Win7下CPU高占用的一次故障解决
    netcore之mysql中文乱码问题解决记录
  • 原文地址:https://www.cnblogs.com/bzfsdr/p/12013700.html
Copyright © 2011-2022 走看看