MongoDB

作者: 紫晶葫芦 | 来源:发表于2021-06-09 19:52 被阅读0次

    1.Mongodb介绍

    Mongodb:是一个nosql的数据库的一种数据库,他是介于关系型数据库与非关系型数据库之间的一种数据库,也可以理解为它是介于Redis与Mysql之间的一种数据库。它是由大数据时代的3V,与互联网需求的三高而产生出来的。

    1. 3V:海量Volume ,多样Variety ,实时Velocity
    2. 高并发,高可扩,高性能

    1. 与传统数据库的对比

    统数据库 Mongodb
    datebase(数据库) datebase
    table(表) collecttion(集合)可以理解成泛型集合
    row(行) document(文档)可理解为对象序列化的结果

    注:

    1. Mongodb 数据10亿加的数据处理不了了,的使用HBASE。
    2. 是关系型数据库和非关系型数据库的一种。

    2. Docker 下的安装

    docker run -d --name mongo1 -p 27017:27017 mongo
    
    进入:docker exec -it  容器id /bin/bash
    

    2.数据操作

    1. 基本操作

    查询数据库
    show databases
    切换数据库
    use test
    查询当前数据库下面的集合
    show  collections
    创建集合
    db.createCollection("集合名称")
    删除集合
    db.集合名称.drop()
    删除数据库
    db.dropDatabase() //首先要通过use切换到当前的数据库
    

    2. Mongodb增删改查(CURD)

    id 系统会自动加一个

    时间戳+机器码 生成

    1. 增(insert)
    新增一条
    db.userinfo.insert({name:"贾宝玉",age:25,gander:"男",address:'贾府'})
    新增多条
    db.userinfo.insert([{name:"贾宝玉",age:25,gander:"男",address:'贾府'}
    ,{name:"林黛玉",age:16,gander:"女",address:'林府'}])
    可不可以快速插入10条数据
    for(var i=1;i<=10;i++)
    {
     db.userinfo.insert({name:"clay"+i,age:i})
     } 
    
    2. 查(find)
    查询所有的数据
    db.集合名称.find({})
    查询top条数
    db.集合名称.find({}).limit(条数)
    条件查询
     db.userinfo.find({name:"clay1",age:1},{name:1,_id:0})
    

    排序&分页

    db.c1.insert({_id:1,name:"a",sex:1,age:1})
    db.c1.insert({_id:2,name:"a",sex:1,age:2})
    db.c1.insert({_id:3,name:"b",sex:2,age:3})
    db.c1.insert({_id:4,name:"c",sex:2,age:4})
    db.c1.insert({_id:5,name:"d",sex:2,age:5})
    
    db.c1.find()
    正序
    db.c1.find({}).sort({age:1})
    降序
     db.c1.find({}).sort({age:-1})
     分页查询 跳过两条查询两条
     db.c1.find({}).sort({age:1}).skip(2).limit(2) 
     
    

    运算符

    运算符 作用
    $gt 大于
    $gte 大于等于
    $lt 小于
    $lte 小于等于
    $ne 不等于
    $in in
    $nin not in
    //年龄大于1
    db.c1.find({age:{$gt:1}})
    //年龄是 3,4,5的
    db.c1.find({age:{$in:[3,4,5]}})
    
    3. 改(update)

    db.集合名.update(条件, 新数据) {修改器: {键:值}}

    修改器 作用
    $inc 递增
    $rename 重命名列
    $set 修改列值
    $unset 删除列
     准备数据
     
     db.c1.insert({name:"8888",age:1,addr:'address',flag:true})
      db.c1.update({name:"8888"}, {name:"99"})
     db.c1.update({name:"8888"}, 
     {
      $set:{name: "zs44"},
      $inc:{age:10},
      $rename:{addr:"address"} ,
      $unset:{flag:""}
       }
     )
     db.c1.find({name:"zs44"})
    
    4. 删(delete)
    //全部移除
    db.userinfo.deleteMany({})
    db.userinfo.deleteMany({age:1})
    
    5. 聚合查询

    顾名思义就是把数据聚起来,然后统计

    语法

    db.集合名称.aggregate([
        {管道:{表达式}}
         ....
    ])
    

    常用管道

    $group 将集合中的文档分组,用于统计结果
    $match 过滤数据,只要输出符合条件的文档
    $sort  聚合数据进一步排序
    $skip  跳过指定文档数
    $limit 限制集合数据返回文档数
    ....
    

    常用表达式

    $sum  总和  $sum:1同count表示统计
    $avg  平均
    $min  最小值
    $max  最大值
    ...
    

    准备

    use test4
    db.c1.insert({_id:1,name:"a",sex:1,age:1})
    db.c1.insert({_id:2,name:"a",sex:1,age:2})
    db.c1.insert({_id:3,name:"b",sex:2,age:3})
    db.c1.insert({_id:4,name:"c",sex:2,age:4})
    db.c1.insert({_id:5,name:"d",sex:2,age:5})
    

    练习

    • 统计男生、女生的总年龄
    db.c1.aggregate([
    {
      $group:{
    _id: "$sex",
    rs: {$sum: "$age"}
      }
    }
    ])
    
    • 统计男生、女生的总人数
    db.c1.aggregate([
    {
      $group:{
    _id: "$sex",
    rs: {$sum:1}
      }
    }
    ])
    
    • 求学生总数和平均年龄
    db.c1.aggregate([
    {
      $group:{
    _id: null,
    total_num: {$sum:1},
    total_avg: {$avg: "$age"}
      }
    }
    ])
    
    • 查询男生、女生人数,按人数升序
    db.c1.aggregate([
    {$group:{_id: "$sex",rs: {$sum: 1}}},
    {$sort:{rs: -1}}
    ])
    

    3. 事务

    4.X版本之后,支持了事务,支持事务之前,必须要搭建集群,如果不是集群,则也不会支持事务。

    use test
    db.createCollection("userinfo");
    
    s=db.getMongo().startSession();
    s.startTransaction();
    s.getDatabase("test").userinfo.insert({name:"a"});
    s.commitTransaction();//提交
    s.abortTransaction();//回滚
    

    4. 使用JavaScript

    1. 创建 Javascript 函数

    现在,我们创建函数 getNextSequenceValue 来作为序列名的输入, 指定的序列会自动增长 1 并返回最新序列值。在本文的实例中序列名为 productid 。

    >function getNextSequenceValue(sequenceName){
       var sequenceDocument = db.counters.findAndModify(
          {
             query:{_id: sequenceName },
             update: {$inc:{sequence_value:1}},
             "new":true
          });
       return sequenceDocument.sequence_value;
    }
    
    2. 使用 Javascript 函数

    接下来我们将使用 getNextSequenceValue 函数创建一个新的文档, 并设置文档 _id 自动为返回的序列值:

    >db.products.insert({
       "_id":getNextSequenceValue("productid"),
       "product_name":"Apple iPhone",
       "category":"mobiles"})
    
    >db.products.insert({
       "_id":getNextSequenceValue("productid"),
       "product_name":"Samsung S3",
       "category":"mobiles"})
    

    就如你所看到的,我们使用 getNextSequenceValue 函数来设置 _id 字段。

    为了验证函数是否有效,我们可以使用以下命令读取文档:

    >db.products.find()
    

    以上命令将返回以下结果,我们发现 _id 字段是自增长的:

    { "_id" : 1, "product_name" : "Apple iPhone", "category" : "mobiles"}
    
    { "_id" : 2, "product_name" : "Samsung S3", "category" : "mobiles" }
    

    5. 自增ID

    #创建序列
    db.counters.insert({_id:"productid",sequence_value:0})
    

    3. 代码对接

    1. 使用驱动包

    MongoDB.Driver

    连接数量可以通过配置文件修改

    2. 直接对JSON操作
    var client = new MongoClient("mongodb://192.168.3.202:27017");
    var database = client.GetDatabase("mongodbDemo");
    var document = BsonDocument.Parse("{ a: 1, b: [{ c: 1 }],c: 'ff'}");
    database.GetCollection<BsonDocument>("order").InsertOne(document);
    
    3. 操作对象

    帮助类:

    public class DBbase<T> where T : class, new()
    {
        MongoClient client;
        IMongoDatabase database;
        public IMongoCollection<T> collection;
        public DBbase()
        {
            //创建连接
            //var client = new MongoClient("mongodb://host:27017,host2:27017/?replicaSet=rs0");//集群的连接方式
            client = new MongoClient("mongodb://81.70.91.63:27017");
            //创建数据库
            database = client.GetDatabase("test");
            //创建集合
            Type type = typeof(T);
            collection = database.GetCollection<T>(type.Name.ToLower());
        }
        public void DropDatabase()
        {
            client.DropDatabase("test");
        }
        public void InsertOne(T model)
        {
            collection.InsertOne(model);
        }
        public void InsertMany(params T[] modes)
        {
            collection.InsertMany(modes);
        }
        public IMongoQueryable<T> Select()
        {
            return collection.AsQueryable<T>();
        }
        public IMongoQueryable<T> Select(int pageIndex, int pageSize)
        {
            return collection.AsQueryable<T>().Skip(pageSize * (pageIndex - 1)).Take(pageSize);
        }
        public IMongoQueryable<T> Select(Expression<Func<T, bool>> predicate, Expression<Func<T, object>> keySelector, int pageIndex, int pageSize)
        {
            return collection.AsQueryable<T>().Where(predicate).OrderBy(keySelector).Skip(pageSize * (pageIndex - 1)).Take(pageSize);
        }
        public void UpdateMany(Expression<Func<T, bool>> filter, UpdateDefinition<T> update)
        {
            collection.UpdateMany(filter, update);
        }
    
        public void UpdateOne(Expression<Func<T, bool>> filter, T update)
        {
            collection.ReplaceOne(filter, update);
        }
    
        public void DeleteMany(Expression<Func<T, bool>> filter)
        {
            collection.DeleteMany(filter);
        }
    }
    
    dBbase.InsertOne(new Userinfo()
                {
                    Id = Guid.NewGuid().ToString(),
                    Name = "诸葛亮",
                    Address = "蜀国",
                    Age = 27,
                    Sex = "男",
                    DetpInfo = new DetpInfo()
                    {
                        DeptId = 1,
                        DeptName = "蜀国集团"
                    }
                });
    var pagelist = dBbase.Select(m => m.Sex == "男", m => m.Age, 2, 2);
                foreach (var item in pagelist)
                {
                    Console.WriteLine(item.Name + ":" + item.Age);
                }
    
                var query = from p in dBbase.collection.AsQueryable()
                            where (p.Sex == "男") && p.Age > 18
                            select p;
                foreach (var item in query)
                {
                    Console.WriteLine(item.Name + ":" + item.Age);
                }
    var daqiaod = dBbase.Select().Where(m => m.Name == "大乔").FirstOrDefault();
                daqiaod.Age = 18;
                dBbase.UpdateOne(m => m.Id == daqiaod.Id, daqiaod);
                Console.WriteLine("修改完成");
    dBbase.DeleteMany(m => m.Name == "大乔");
                Console.WriteLine("删除完成");
                //全部删除
                dBbase.DeleteMany(m => 1 == 1);
                Console.WriteLine("全部删除完成");
    var groups = dBbase.collection.AsQueryable().GroupBy(m => new { m.DetpInfo.DeptId, m.DetpInfo.DeptName }).Select(t => new
                {
                    DeptId = t.Key.DeptId,
                    DeptName = t.Key.DeptName,
                    number = t.Count(),
                    ages = t.Sum(s => s.Age)
                }).Take(0).Skip(10);
                foreach (var item in groups)
                {
                    Console.WriteLine(item.DeptName + ":" + item.number + ":" + item.ages);
                }
                
    
    

    4. 缺点与需要注意地方

    1. 想要严格的操作mongo,可以创建一个map。net代码写的类要和map里面对字段类型要一致,如果多了一个字段或者少一个字段又或者类型不对,则写入失败。
    2. 与mysql区别,mongodb它不能做两和集合连查。可以通过数据冗余的办法,实现连查的效果。如果一个文档(json)16M会报错。如果超过把数据存在多个文档,然后根据一个关联字段去关联就行。比较特殊一般的业务完全够用。
    3. 多表关联:仅仅支持Left Outer Join
    4. SQL 语句支持:查询为主 部分支持
    5. 多表原子事务:不支持
    6. 多文档原子事务:不支持
    7. 16MB 大小限制超过报错,不支持中文排序,服务端JavaScript性能不好

    5. 使用条件与场景###

    • 使用条件

      1. 使用数据有亿万级或者需要不断的扩容。
      2. 需要2000-3000的每秒读写速度。
      3. 新应用需求会变,数据模型无法确定。
      4. 需要整合多个外部数据源。
      5. 系统需要99.999的高可用。
      6. 系统需要大量的地理位置查询。
      7. 系统需要提供最小的延迟。
      8. 管理的主要对象小于10。
    • 使用场景

      1. 日志收集
      2. 传感器
      3. 网络爬虫

    6. 慎用场景

    慎用场景 原因
    PB数据持久存储大数据分析数据湖 Hadoop、Spark提供更多分析运算功能和工具,并行计算能力更强 Mongodb+hadoop /Spark
    搜素场景:文档有十几个字段,需要按照任意字段搜索并排序限制等 不建索引查询太慢,索引太多影响写入和更新操作
    ERP、CRM或者类似负责应用,几十上百个对象互相关联 关联支持较弱,事务较弱
    需要参与远程事务,需要跨表、跨文档原子性更新 MongoDB 事务支持仅限于本机的单文档事务
    100%写可用:任何时间写入不能停 MongoDB 换主节点时候会有短暂的不可写设计所限

    7. 原理

    1.png

    8. 持久化

    2.png

    NOSQL:特性速度写入速度快 ,使用的是刷盘机制。写数据的时候,先把数据写入到内存,然后根据异步刷盘机制把数据存放到硬盘。

    刷盘机制:先把数据保存到缓冲中,然后在保存到日志文件中,最后通过日志文件保存到磁盘中。如果直接由缓存保存到磁盘会造成随机性,消耗性能。

    当从新打开mongodb的时候,程序先从Datafile 中读入数据,然后再从 log 中读入数据。

    注意:

    1. mongodb 也会造成数据的丢失,丢失数据的原因再缓存区的数据没有来的及写入log 中,最大丢失数据的时间是99 ms之内产生的。
    2. 增加缓存区,增大刷盘时间可以提高mongodb 的处理数据速度。

    9. 集群

    1. 副本集

    一个主库两个从库组成,主库把数据备份到从库中,从库之间有心跳,主库宕机时,这两个从库自动竞争选为主库,实现为高可用。主从架构:主节点负责写,从节点负责读。

    1. 老版本搭建

    结构如图:

    4.png
    1. 一主一从

      version: '2'
      services:
        master:
          image: mongo:3.4
          volumes:
            - /data/mongodbml/master:/data/db
          command: mongod --dbpath /data/db --master
        slaver:
          image: mongo:3.4
          volumes:
            - /data/mongodbml/slaver:/data/db
          command: mongod --dbpath /data/db --slave --source master:27017
          links:
            - master
      

      运行测试

      //运行
      docker-compose  up -d
      //进入主节点测试
      docker-compose exec master mongo
      //创建数据库
      use test
      //插入数据
      db.test.insert({msg: "this message is from master", ts: new Date()})
      
      //进入从节点
      docker-compose exec slaver mongo
      
      rs.slaveOk():
      //查询数据
      use test
      db.test.find();
      //获得结果:证明主从架构建立完成。
      
      

      注意:

      1. 一主一丛,当主节点宕机,不会把从节点切换成主节点。只能实现数据的备份,不能实现高可用。
    2. 一主多从

      架构如图:


      5.png
    version: '2'
    services:
      rs1:
        image: mongo:3.4
        volumes:
          - /data/mongodbtest/replset/rs1:/data/db
        command: mongod --dbpath /data/db --replSet myset
      rs2:
        image: mongo:3.4
        volumes:
          - /data/mongodbtest/replset/rs2:/data/db
        command: mongod --dbpath /data/db --replSet myset
      rs3:
        image: mongo:3.4
        volumes:
          - /data/mongodbtest/replset/rs3:/data/db
        command: mongod --dbpath /data/db --replSet myset
    

    运行测试

    ![6](F:\learn\2021Mongodb\笔记\image\6.png)//运行
    docker-compose up -d
    //进入rs1
    docker-compose exec rs1 mongo
    //初始化
    rs.initiate()
    //把rs2 添加到rs1 中
    rs.add('rs2:27017')
    //把rs3 添加到rs1 中
    rs.add('rs3:27017')
    //查看副本状态
    rs.conf() 
    rs.status() 
    
    //插入信息到主节点
    docker-compose exec rs1 mongo
    use test
    db.test.insert({msg: 'this is from primary', ts: new Date()})
    
    //在rs2副本查看信息
    docker-compose exec rs2 mongo
    rs.slaveOk()
    use test
    db.test.find()
    
    //在rs3副本查看信息
    docker-compose exec rs3 mongo
    rs.slaveOk() //副本集默认仅primary可读写
    use test
    db.test.find()
    
    

    注意:

    1. 当主节点宕机后,从节点主节点,一个主多个从是可以实现高可用。
    2. 宕机后的主节点回来后,变成了新的主节点。
    6.png
    1. 问题:一主多从,其中一主宕机后,从节点之间的数据交流只是心跳,没有数据的转移,所有从中选择出主节点,可能是主观的,但是这样不是我们想要的,所有又提供了一种新的方式:一主一从一仲裁。

    2. 一主一从一仲裁

      仲裁的一方记录副本的:网络,备份数据如何,计算机性能等,这都是从节点竞争主节点的依据。当存在仲裁arbiter节点(只是仲裁,不存储数据)。仲裁可以搭建一个或是多个。

    架构如图:


    7.png
    version: '2'
    services:
      master:
        image: mongo:3.4
        volumes:
          - /data/mongodb3node/replset/rs1:/data/db
        command: mongod --dbpath /data/db --replSet newset --oplogSize 128
      slave:
        image: mongo:3.4
        volumes:
          - /data/mongodb3node/replset/rs2:/data/db
        command: mongod --dbpath /data/db --replSet newset --oplogSize 128
      myarbiter:
        image: mongo:3.4
        command: mongod --dbpath /data/db --replSet newset --smallfiles --oplogSize 128
    

    运行测试:

    //运行
    docker-compose exec master mongo
    //设置
    rs.initiate()
    rs.add('slave:27017')//slave 可以是IP
    rs.add('myarbiter:27017',true)//设置为仲裁节点
    
    //查看配置
    rs.conf() 
    rs.status() 
    //插入信息到主节点:
    docker-compose exec master mongo
    use test
    db.test.insert({msg: 'this is from primary', ts: new Date()})
    //在副本集中检测信息是否同步
    docker-compose exec slave mongo
    rs.slaveOk()
    use test
    db.test.find()
    
    
    
    

    主节点宕机处理:


    8.png
    2. 新版本搭建

    推介使用最新的

    1. 加入仲裁的

      #三台节点安装实例
      docker run --name mongo1 -p 27017:27017  -d mongo mongod --replSet "rs0"
      docker run --name mongo2 -p 27017:27017  -d mongo mongod --replSet "rs0"
      docker run --name mongo3 -p 27017:27017  -d mongo mongod --replSet "rs0"
      
      #进入mongo1容器
      docker exec -ti mongo1 /bin/bash
      #连接mondb
      mongo 
      #初始化副本集
      rs.initiate({"_id": "rs0", "members": [{"_id":0, "host":"192.168.3.202:27017"}, {"_id":1, "host":"192.168.3.203:27017","arbiterOnly":true}, {"_id":2, "host":"192.168.3.204:27017"}]})
      # 加这个字段,说明该节点就是仲裁不存放数据
      "arbiterOnly":true
      
      #查看副本集配置信息
      rs.conf()
      
      #从节点开启读数据模式
      db.getMongo().setSlaveOk(); 
      
      //代码测试
      MongoClient client = new MongoClient("mongodb://39.96.34.52:27017,47.95.2.2:27017,39.96.82.51:27017");
      //事务
      var session = client.StartSession();
      var database = session.Client.GetDatabase("test");
      session.StartTransaction(new TransactionOptions( readConcern: ReadConcern.Snapshot,writeConcern: WriteConcern.WMajority));
      try
      {
          IMongoCollection<Userinfo> collection = database.GetCollection<Userinfo>("userinfo");
          IMongoCollection<DetpInfo> weiguocollection = database.GetCollection<DetpInfo>("deptindo");
          Userinfo daqiao = new Userinfo()
          {
              Id = Guid.NewGuid().ToString(),
              Address = "吴国",
              Name = "大乔",
              Sex = "女",
              DetpInfo = new DetpInfo()
              {
                  DeptId = 1,
                  DeptName = "蜀国集团"
              }
          };
      
          collection.InsertOne(session, daqiao);
          //throw new Exception("取消事务");
      
          DetpInfo weiguo = new DetpInfo() { DeptId = 1, DeptName = "魏国" };
          weiguocollection.InsertOne(session,weiguo);
          session.CommitTransaction();
      }
      catch (Exception ex)
      {
          //回滚
          session.AbortTransaction();
          Console.WriteLine(ex.Message);
      } 
      

      以下是docker-compose

      version: '2'
      services:
        rs1:
          image: mongo
          volumes:
           - /data/mongodbtest/replset/rs1:/data/db
          ports:
           - 27017:27017
          command: mongod --dbpath /data/db --replSet rs0
      
        rs2:
          image: mongo
          volumes:
           - /data/mongodbtest/replset/rs2:/data/db
          ports:
           - 27018:27017
          command: mongod --dbpath /data/db --replSet rs0
      
        rs3:
          image: mongo
          volumes:
           - /data/mongodbtest/replset/rs3:/data/db
          ports:
           - 27019:27017
          command: mongod --dbpath /data/db --replSet rs0
      

      运行

      进入mongo1容器
      docker exec -ti mongodb_rs1_1 /bin/bash
      #连接mondb
      mongo 
      #初始化副本集 注意的是rs0是启动节点的时候--replSet rs0 的名字
      rs.initiate({"_id": "rs0", "members": [{"_id":0, "host":"192.168.3.204:27017"}, {"_id":1, "host":"192.168.3.204:27018","arbiterOnly":true}, {"_id":2, "host":"192.168.3.204:27019"}]})
      # 加这个字段,说明该节点就是仲裁不存放数据
      "arbiterOnly":true
      
      #查看副本集配置信息
      rs.conf()
      
      #从节点开启读数据模式
      db.getMongo().setSlaveOk(); 
      

    2. 分片

    1. 概念

    分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程。将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载。基本思想就是将集合切成小块,这些块分散到若干片里,每个片只负责总数据的一部分,最后通过一个均衡器来对各个分片进行均衡(数据迁移)。通过一个名为mongos的路由进程进行操作,mongos知道数据和片的对应关系(通过配置服务器)。大部分使用场景都是解决磁盘空间的问题,对于写入有可能会变差,查询则尽量避免跨分片查询。使用分片的时机:

    1,机器的磁盘不够用了。使用分片解决磁盘空间的问题。

    2,单个mongod已经不能满足写数据的性能要求。通过分片让写压力分散到各个分片上面,使用分片服务器自身的资源。

    3,想把大量数据放到内存里提高性能。和上面一样,通过分片使用分片服务器自身的资源。

    2. 分片集群架构
    3.png

    上图中主要有如下所述三个主要组件:

    Shard:分片服务器

    用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障

    Config Server:配置服务器

    mongod实例,存储了整个 分片群集的配置信息,其中包括 chunk信息。

    Query Routers:前端路由

    客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用

    这里的两个shard(分片)相当于mongodb节点服务器,内部的块是将order集合再切割的结果,随着数据量的的增大,分片会分割和迁移,以满足数据的均匀分布。

    请求分流:通过路由节点将请求分发到对应的分片和块中

    数据分流:内部提供平衡器保证数据的均匀分布,这是数据平均分布式、请求平均分布的前提

    块的拆分:3.4版本块的最大容量为64M或者10w的数据,当到达这个阈值,触发块的拆分,一分为二

    块的迁移:为保证数据在分片节点服务器分片节点服务器均匀分布,块会在节点之间迁移。一般相差8个分块的时候触发

    MongoDB分片优势:

    减少单个分片需要处理的请求数,提高群集的存储容量和吞吐量 比如,当插入一条数据时,应用只需要访问存储这条数据的分片 减少单分片存储的数据,提高数据可用性,提高大型数据库查询服务的性能。 当MongoDB单点数据库服务器存储和性能成为瓶颈,或者需要部署大型应用以充分利用内存时,可以使用分片技术

    环境准备

    docker、docker-compose、lunux。版本问题应该不大,我用的是Docker version 18.09.0, build 4d60db4、docker-compose version 1.23.0-rc3, buildea3d406e、centos7.2。

    本套Mongodb搭建分片集群是基于mongodb4.0.5,直接从官方镜像仓库拉取docker pull mongo:4.0.5即可

    完成准备之后,docker images看一下,mongodb.镜像是否搞定了。

    技术分享图片
    3. 搭建

    编写yml文件,docker-compose.yml

    version: '2'
    services:
      shard1:
        image: mongo:4.0.5
        container_name: mongo_shard1
        # --shardsvr: 这个参数仅仅只是将默认的27017端口改为27018,如果指定--port参数,可用不需要这个参数
        # --directoryperdb:每个数据库使用单独的文件夹
        command: mongod --shardsvr --directoryperdb --replSet shard1
        volumes:
          - /etc/localtime:/etc/localtime
          - /data/base/fates/mongo/shard1:/data/db
        privileged: true 
        networks:
          - mongo   
    
      shard2:
        image: mongo:4.0.5
        container_name: mongo_shard2
        command: mongod --shardsvr --directoryperdb --replSet shard2
        volumes:
          - /etc/localtime:/etc/localtime
          - /data/base/fates/mongo/shard2:/data/db
        privileged: true 
        networks:
          - mongo
    
      shard3:
        image: mongo:4.0.5
        container_name: mongo_shard3
        command: mongod --shardsvr --directoryperdb --replSet shard3
        volumes:
          - /etc/localtime:/etc/localtime
          - /data/base/fates/mongo/shard3:/data/db
        privileged: true 
        networks:
          - mongo
    
      config1:
        image: mongo:4.0.5
        container_name: mongo_config1
        # --configsvr: 这个参数仅仅是将默认端口由27017改为27019, 如果指定--port可不添加该参数
        command: mongod --configsvr --directoryperdb --replSet fates-mongo-config --smallfiles
        volumes:
          - /etc/localtime:/etc/localtime
          - /data/base/fates/mongo/config1:/data/configdb
        networks:
          - mongo
    
      config2:
        image: mongo:4.0.5
        container_name: mongo_config2
        command: mongod --configsvr --directoryperdb --replSet fates-mongo-config --smallfiles
        volumes:
          - /etc/localtime:/etc/localtime
          - /data/base/fates/mongo/config2:/data/configdb
        networks:
          - mongo
    
      config3:
        image: mongo:4.0.5
        container_name: mongo_config3
        command: mongod --configsvr --directoryperdb --replSet fates-mongo-config --smallfiles
        volumes:
          - /etc/localtime:/etc/localtime
          - /data/base/fates/mongo/config3:/data/configdb
        networks:
          - mongo
    
      mongos:
        image: mongo:4.0.5
        container_name: mongo_mongos
        # mongo3.6版默认绑定IP为127.0.0.1,此处绑定0.0.0.0是允许其他容器或主机可以访问
        command: mongos --configdb fates-mongo-config/config1:27019,config2:27019,config3:27019 --bind_ip 0.0.0.0 --port 27017
        ports:
         - 27017:27017
        volumes:
          - /etc/localtime:/etc/localtime
        depends_on:
          - config1
          - config2
          - config3
        networks:
          - mongo    
    networks:
      mongo:
        driver: bridge
    

    编写deploy-and-start.sh脚本

    #!/bin/sh
    
    docker-compose  up -d
    
    #鐫$湢涓ゅ垎閽燂紝绛夊緟mongodb鎵€鏈夊ㄨ捣鏉ヤ箣鍚庡皢瀹冧滑閰嶇疆鍔犲叆鍒嗙墖
    sleep 30s
    
    docker-compose  exec config1 bash -c "echo 'rs.initiate({_id: \"fates-mongo-config\",configsvr: true, members: [{ _id : 0, host : \"config1:27019\" },{ _id : 1, host : \"config2:27019\" }, { _id : 2, host : \"config3:27019\" }]})' | mongo --port 27019"
    docker-compose  exec shard1 bash -c "echo 'rs.initiate({_id: \"shard1\",members: [{ _id : 0, host : \"shard1:27018\" }]})' | mongo --port 27018"
    docker-compose  exec shard2 bash -c "echo 'rs.initiate({_id: \"shard2\",members: [{ _id : 0, host : \"shard2:27018\" }]})' | mongo --port 27018"
    docker-compose  exec shard3 bash -c "echo 'rs.initiate({_id: \"shard3\",members: [{ _id : 0, host : \"shard3:27018\" }]})' | mongo --port 27018"
    docker-compose  exec mongos bash -c "echo 'sh.addShard(\"shard1/shard1:27018\")' | mongo"
    docker-compose  exec mongos bash -c "echo 'sh.addShard(\"shard2/shard2:27018\")' | mongo"
    docker-compose  exec mongos bash -c "echo 'sh.addShard(\"shard3/shard3:27018\")' | mongo"
    

    该脚本流程详细描述一下

    1)先启动mongodb分片容器组

    1. 睡眠30s等待容器全部完全启动(可能不需要30s)

    3)操作config1,配置config副本集,将config*容器组作为config角色,此时config1作为config副本集里的主节点

    4)操作shard1、shard2、shard3,将shard*容器组作为shard角色。

    5)将shard1、shard2、shard3加入分片集群组。

    执行脚本

    #提交进去使用notepad++ 修过为utf8格式 (mobaxterm 选择文件右击open with--选择notepad++打开 ) 
    #替换文本的换行等字符串 ,
    sed -i 's/\r$//'  deploy-and-start.sh
    
    sh deploy-and-start.sh
    

    # sh deploy-and-start.sh 等待脚本启动完成,如果出现connect fail报错,检查一下网络,再次启动一次脚本即可。

    到这里,单机版的mongodb的分片集群,就搭建好了,一般真正的运维环境,Mongodb集群应该分布在不同的机器,但是只要熟悉这套部署方案的机制,只要稍作修改,就可以实现。

    但是,Mongodb库默认是不会将你的表进行分片的,是需要手动配置的,如果不配置,数据都会全部存放在主节点,不会均匀分配到各个分片。

    现在手动将一个表做分片,就拿order表作为例子

    docker exec -it mongo_mongos bash
    mongo --host 192.168.3.202 --port 27017
    
    #数据库 启用 分片
    sh.enableSharding("mongodbDemo")
    # _id 字段进行哈希分片:
    sh.shardCollection("mongodbDemo.order", {"_id": "hashed" }) 
    #刷新路由
    db.adminCommand("flushRouterConfig")
    #让当前分片支持平衡
    sh.enableBalancing("mongodbDemo.order")
    #开启平衡
    sh.startBalancer() 
    #查看详细分片信息
    sh.status({"verbose":1})
    #插入数据
    use mongodbDemo
    for (i = 1;i <= 100;i=i+1){
    db.order.insert({'price': 1})
    }
    #查看该表分片数据信息
    db.order.getShardDistribution()
    
    4. 可视化工具
    docker run -it --rm \
        --name mongo-express \
        -p 8081:8081 \
        -e ME_CONFIG_OPTIONS_EDITORTHEME="ambiance" \
        -e ME_CONFIG_MONGODB_SERVER="192.168.3.202" \
      -e ME_CONFIG_MONGODB_PORT="27017"          \
        -e ME_CONFIG_BASICAUTH_USERNAME="admin" \
        -e ME_CONFIG_BASICAUTH_PASSWORD="admin" \
        mongo-express
    

    10. 索引

    db.c1.createIndex({age:1},{background:true,expireAfterSeconds:

    150,name:"ix_age",unique:true});

    db.c1.find({age:18}).explain('executionStats'); //查看执行计划

    db.c1.getIndexes()

    COLLSCAN 全表扫描 最慢的,没有走索引

    IXSCAN 索引扫描

    FETCH 根据索引去检索指定document

    11. 注意

    1. redis与mongodb对比,redis速度要快,redis的纯内存的。

    12. 扩展

    MongoDB处理的数据是小于10亿的.

    当达到了PB级别的数据:使用的NoSql是:hbase .
    大数据本质:让数据又价值值钱,而让数据有意义首先经过以下步骤: 1:收集数据(各式各样的数据)->2.清洗数据(格式化处理数据)->3.分析统计.

    hbase:列数据库:k:v
    1.对数据随机读写,数据增删改查
    2.高并发操作,一秒能对PB级别的数据,进行上千次不等操作
    3.读和写都是简单的操作。

    松散存储--nosql---列族 (一个列族里面可以存放多个字段)
    创建数据库的时候,只要把列族创建好就行,至于你这列族里面几个字段,随便你。。

    高效查询只能通过rowkey:1001 这个,要么直接=rowkey,要么给rowkey范围非常重要,设计者rowkey(包含了使用了它的60%思想思维)

    hbase,分片是根据rowkey来分配

    1. 尽量让我们的rowkey分配均匀,还有松散, 可以利用多个节点.

    2. 业务有时候,需要查询的时候,可能需要尽量让查询的这个数据在一个节点上面。

      例如: 具体的业务具体分析:保存(123123412340) 保存到30 不同的分片上去,并且做到均衡,可以对(12312341234 + 202101 (年与月))%30保存

    HBase安装:

    docker run -d -h myhbase -p 2181:2181 -p 8080:8080 -p 8085:8085 -p 9090:9090 -p 9095:9095 -p 16000:16000 -p 16010:16016 -p 16201:16201 -p 16301:16301 --name hbase1.3 harisekhon/hbase:1.3

    访问地址:[http://IP:16010/master-status](http://IP:16010/master-status)
    
    //进入
    docker exec -it 容器id /bin/bash
    hbase shell
    //查看list 
    list
    //创建list 
    create 'table' 'info1' 'info2'
    //插入数据
    put 'student','1001','info1:name','sss'
    //查看数据
    scan 'student'
    
    

    相关文章

      网友评论

          本文标题:MongoDB

          本文链接:https://www.haomeiwen.com/subject/mqpreltx.html