MongoDB

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'

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容