MongoDB

1.Mongodb介紹

Mongodb:是一個nosql的數(shù)據(jù)庫的一種數(shù)據(jù)庫余舶,他是介于關(guān)系型數(shù)據(jù)庫與非關(guān)系型數(shù)據(jù)庫之間的一種數(shù)據(jù)庫试幽,也可以理解為它是介于Redis與Mysql之間的一種數(shù)據(jù)庫淮韭。它是由大數(shù)據(jù)時代的3V裁僧,與互聯(lián)網(wǎng)需求的三高而產(chǎn)生出來的财搁。

  1. 3V:海量Volume 蘸炸,多樣Variety ,實時Velocity
  2. 高并發(fā)尖奔,高可擴搭儒,高性能

1. 與傳統(tǒng)數(shù)據(jù)庫的對比

統(tǒng)數(shù)據(jù)庫 Mongodb
datebase(數(shù)據(jù)庫) datebase
table(表) collecttion(集合)可以理解成泛型集合
row(行) document(文檔)可理解為對象序列化的結(jié)果

注:

  1. Mongodb 數(shù)據(jù)10億加的數(shù)據(jù)處理不了了,的使用HBASE提茁。
  2. 是關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫的一種淹禾。

2. Docker 下的安裝

docker run -d --name mongo1 -p 27017:27017 mongo

進入:docker exec -it  容器id /bin/bash

2.數(shù)據(jù)操作

1. 基本操作

查詢數(shù)據(jù)庫
show databases
切換數(shù)據(jù)庫
use test
查詢當前數(shù)據(jù)庫下面的集合
show  collections
創(chuàng)建集合
db.createCollection("集合名稱")
刪除集合
db.集合名稱.drop()
刪除數(shù)據(jù)庫
db.dropDatabase() //首先要通過use切換到當前的數(shù)據(jù)庫

2. Mongodb增刪改查(CURD)

id 系統(tǒng)會自動加一個

時間戳+機器碼 生成

1. 增(insert)
新增一條
db.userinfo.insert({name:"賈寶玉",age:25,gander:"男",address:'賈府'})
新增多條
db.userinfo.insert([{name:"賈寶玉",age:25,gander:"男",address:'賈府'}
,{name:"林黛玉",age:16,gander:"女",address:'林府'}])
可不可以快速插入10條數(shù)據(jù)
for(var i=1;i<=10;i++)
{
 db.userinfo.insert({name:"clay"+i,age:i})
 } 
2. 查(find)
查詢所有的數(shù)據(jù)
db.集合名稱.find({})
查詢top條數(shù)
db.集合名稱.find({}).limit(條數(shù))
條件查詢
 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(條件, 新數(shù)據(jù)) {修改器: {鍵:值}}

修改器 作用
$inc 遞增
$rename 重命名列
$set 修改列值
$unset 刪除列
 準備數(shù)據(jù)
 
 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. 聚合查詢

顧名思義就是把數(shù)據(jù)聚起來茴扁,然后統(tǒng)計

語法

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

常用管道

$group 將集合中的文檔分組稀拐,用于統(tǒng)計結(jié)果
$match 過濾數(shù)據(jù),只要輸出符合條件的文檔
$sort  聚合數(shù)據(jù)進一步排序
$skip  跳過指定文檔數(shù)
$limit 限制集合數(shù)據(jù)返回文檔數(shù)
....

常用表達式

$sum  總和  $sum:1同count表示統(tǒng)計
$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})

練習(xí)

  • 統(tǒng)計男生丹弱、女生的總年齡
db.c1.aggregate([
{
  $group:{
_id: "$sex",
rs: {$sum: "$age"}
  }
}
])
  • 統(tǒng)計男生德撬、女生的總?cè)藬?shù)
db.c1.aggregate([
{
  $group:{
_id: "$sex",
rs: {$sum:1}
  }
}
])
  • 求學(xué)生總數(shù)和平均年齡
db.c1.aggregate([
{
  $group:{
_id: null,
total_num: {$sum:1},
total_avg: {$avg: "$age"}
  }
}
])
  • 查詢男生、女生人數(shù)躲胳,按人數(shù)升序
db.c1.aggregate([
{$group:{_id: "$sex",rs: {$sum: 1}}},
{$sort:{rs: -1}}
])

3. 事務(wù)

4.X版本之后蜓洪,支持了事務(wù),支持事務(wù)之前坯苹,必須要搭建集群隆檀,如果不是集群,則也不會支持事務(wù)粹湃。

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. 創(chuàng)建 Javascript 函數(shù)

現(xiàn)在恐仑,我們創(chuàng)建函數(shù) 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 函數(shù)

接下來我們將使用 getNextSequenceValue 函數(shù)創(chuàng)建一個新的文檔, 并設(shè)置文檔 _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 函數(shù)來設(shè)置 _id 字段歧斟。

為了驗證函數(shù)是否有效纯丸,我們可以使用以下命令讀取文檔:

>db.products.find()

以上命令將返回以下結(jié)果,我們發(fā)現(xiàn) _id 字段是自增長的:

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

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

5. 自增ID

#創(chuàng)建序列
db.counters.insert({_id:"productid",sequence_value:0})

3. 代碼對接

1. 使用驅(qū)動包

MongoDB.Driver

連接數(shù)量可以通過配置文件修改

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()
    {
        //創(chuàng)建連接
        //var client = new MongoClient("mongodb://host:27017,host2:27017/?replicaSet=rs0");//集群的連接方式
        client = new MongoClient("mongodb://81.70.91.63:27017");
        //創(chuàng)建數(shù)據(jù)庫
        database = client.GetDatabase("test");
        //創(chuàng)建集合
        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静袖,可以創(chuàng)建一個map觉鼻。net代碼寫的類要和map里面對字段類型要一致,如果多了一個字段或者少一個字段又或者類型不對队橙,則寫入失敗坠陈。
  2. 與mysql區(qū)別,mongodb它不能做兩和集合連查捐康〕鸱可以通過數(shù)據(jù)冗余的辦法,實現(xiàn)連查的效果吹由。如果一個文檔(json)16M會報錯若未。如果超過把數(shù)據(jù)存在多個文檔,然后根據(jù)一個關(guān)聯(lián)字段去關(guān)聯(lián)就行倾鲫。比較特殊一般的業(yè)務(wù)完全夠用粗合。
  3. 多表關(guān)聯(lián):僅僅支持Left Outer Join
  4. SQL 語句支持:查詢?yōu)橹?部分支持
  5. 多表原子事務(wù):不支持
  6. 多文檔原子事務(wù):不支持
  7. 16MB 大小限制超過報錯,不支持中文排序乌昔,服務(wù)端JavaScript性能不好

5. 使用條件與場景###

  • 使用條件

    1. 使用數(shù)據(jù)有億萬級或者需要不斷的擴容隙疚。
    2. 需要2000-3000的每秒讀寫速度。
    3. 新應(yīng)用需求會變磕道,數(shù)據(jù)模型無法確定供屉。
    4. 需要整合多個外部數(shù)據(jù)源。
    5. 系統(tǒng)需要99.999的高可用溺蕉。
    6. 系統(tǒng)需要大量的地理位置查詢伶丐。
    7. 系統(tǒng)需要提供最小的延遲。
    8. 管理的主要對象小于10疯特。
  • 使用場景

    1. 日志收集
    2. 傳感器
    3. 網(wǎng)絡(luò)爬蟲

6. 慎用場景

慎用場景 原因
PB數(shù)據(jù)持久存儲大數(shù)據(jù)分析數(shù)據(jù)湖 Hadoop哗魂、Spark提供更多分析運算功能和工具,并行計算能力更強 Mongodb+hadoop /Spark
搜素場景:文檔有十幾個字段漓雅,需要按照任意字段搜索并排序限制等 不建索引查詢太慢录别,索引太多影響寫入和更新操作
ERP、CRM或者類似負責應(yīng)用邻吞,幾十上百個對象互相關(guān)聯(lián) 關(guān)聯(lián)支持較弱组题,事務(wù)較弱
需要參與遠程事務(wù),需要跨表抱冷、跨文檔原子性更新 MongoDB 事務(wù)支持僅限于本機的單文檔事務(wù)
100%寫可用:任何時間寫入不能停 MongoDB 換主節(jié)點時候會有短暫的不可寫設(shè)計所限

7. 原理

1.png

8. 持久化

2.png

NOSQL:特性速度寫入速度快 崔列,使用的是刷盤機制。寫數(shù)據(jù)的時候徘层,先把數(shù)據(jù)寫入到內(nèi)存峻呕,然后根據(jù)異步刷盤機制把數(shù)據(jù)存放到硬盤利职。

刷盤機制:先把數(shù)據(jù)保存到緩沖中趣效,然后在保存到日志文件中瘦癌,最后通過日志文件保存到磁盤中。如果直接由緩存保存到磁盤會造成隨機性跷敬,消耗性能讯私。

當從新打開mongodb的時候,程序先從Datafile 中讀入數(shù)據(jù)西傀,然后再從 log 中讀入數(shù)據(jù)斤寇。

注意:

  1. mongodb 也會造成數(shù)據(jù)的丟失,丟失數(shù)據(jù)的原因再緩存區(qū)的數(shù)據(jù)沒有來的及寫入log 中拥褂,最大丟失數(shù)據(jù)的時間是99 ms之內(nèi)產(chǎn)生的娘锁。
  2. 增加緩存區(qū),增大刷盤時間可以提高mongodb 的處理數(shù)據(jù)速度饺鹃。

9. 集群

1. 副本集

一個主庫兩個從庫組成莫秆,主庫把數(shù)據(jù)備份到從庫中,從庫之間有心跳悔详,主庫宕機時镊屎,這兩個從庫自動競爭選為主庫,實現(xiàn)為高可用茄螃。主從架構(gòu):主節(jié)點負責寫缝驳,從節(jié)點負責讀。

1. 老版本搭建

結(jié)構(gòu)如圖:

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
    //進入主節(jié)點測試
    docker-compose exec master mongo
    //創(chuàng)建數(shù)據(jù)庫
    use test
    //插入數(shù)據(jù)
    db.test.insert({msg: "this message is from master", ts: new Date()})
    
    //進入從節(jié)點
    docker-compose exec slaver mongo
    
    rs.slaveOk():
    //查詢數(shù)據(jù)
    use test
    db.test.find();
    //獲得結(jié)果:證明主從架構(gòu)建立完成归苍。
    
    

    注意:

    1. 一主一叢用狱,當主節(jié)點宕機,不會把從節(jié)點切換成主節(jié)點拼弃。只能實現(xiàn)數(shù)據(jù)的備份夏伊,不能實現(xiàn)高可用。
  2. 一主多從

    架構(gòu)如圖:


    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')
//查看副本狀態(tài)
rs.conf() 
rs.status() 

//插入信息到主節(jié)點
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. 當主節(jié)點宕機后肴敛,從節(jié)點主節(jié)點署海,一個主多個從是可以實現(xiàn)高可用。
  2. 宕機后的主節(jié)點回來后医男,變成了新的主節(jié)點砸狞。
6.png
  1. 問題:一主多從,其中一主宕機后镀梭,從節(jié)點之間的數(shù)據(jù)交流只是心跳刀森,沒有數(shù)據(jù)的轉(zhuǎn)移,所有從中選擇出主節(jié)點报账,可能是主觀的研底,但是這樣不是我們想要的埠偿,所有又提供了一種新的方式:一主一從一仲裁。

  2. 一主一從一仲裁

    仲裁的一方記錄副本的:網(wǎng)絡(luò)榜晦,備份數(shù)據(jù)如何冠蒋,計算機性能等,這都是從節(jié)點競爭主節(jié)點的依據(jù)乾胶。當存在仲裁arbiter節(jié)點(只是仲裁抖剿,不存儲數(shù)據(jù))。仲裁可以搭建一個或是多個识窿。

架構(gòu)如圖:


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
//設(shè)置
rs.initiate()
rs.add('slave:27017')//slave 可以是IP
rs.add('myarbiter:27017',true)//設(shè)置為仲裁節(jié)點

//查看配置
rs.conf() 
rs.status() 
//插入信息到主節(jié)點:
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()



主節(jié)點宕機處理:


8.png
2. 新版本搭建

推介使用最新的

  1. 加入仲裁的

    #三臺節(jié)點安裝實例
    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"}]})
    # 加這個字段,說明該節(jié)點就是仲裁不存放數(shù)據(jù)
    "arbiterOnly":true
    
    #查看副本集配置信息
    rs.conf()
    
    #從節(jié)點開啟讀數(shù)據(jù)模式
    db.getMongo().setSlaveOk(); 
    
    //代碼測試
    MongoClient client = new MongoClient("mongodb://39.96.34.52:27017,47.95.2.2:27017,39.96.82.51:27017");
    //事務(wù)
    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("取消事務(wù)");
    
        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是啟動節(jié)點的時候--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"}]})
    # 加這個字段,說明該節(jié)點就是仲裁不存放數(shù)據(jù)
    "arbiterOnly":true
    
    #查看副本集配置信息
    rs.conf()
    
    #從節(jié)點開啟讀數(shù)據(jù)模式
    db.getMongo().setSlaveOk(); 
    

2. 分片

1. 概念

分片(sharding)是指將數(shù)據(jù)庫拆分斩郎,將其分散在不同的機器上的過程。將數(shù)據(jù)分散到不同的機器上喻频,不需要功能強大的服務(wù)器就可以存儲更多的數(shù)據(jù)和處理更大的負載缩宜。基本思想就是將集合切成小塊甥温,這些塊分散到若干片里锻煌,每個片只負責總數(shù)據(jù)的一部分,最后通過一個均衡器來對各個分片進行均衡(數(shù)據(jù)遷移)窿侈。通過一個名為mongos的路由進程進行操作炼幔,mongos知道數(shù)據(jù)和片的對應(yīng)關(guān)系(通過配置服務(wù)器)。大部分使用場景都是解決磁盤空間的問題史简,對于寫入有可能會變差乃秀,查詢則盡量避免跨分片查詢逸吵。使用分片的時機:

1窑睁,機器的磁盤不夠用了。使用分片解決磁盤空間的問題嘁圈。

2殉农,單個mongod已經(jīng)不能滿足寫數(shù)據(jù)的性能要求刀脏。通過分片讓寫壓力分散到各個分片上面,使用分片服務(wù)器自身的資源超凳。

3愈污,想把大量數(shù)據(jù)放到內(nèi)存里提高性能。和上面一樣轮傍,通過分片使用分片服務(wù)器自身的資源暂雹。

2. 分片集群架構(gòu)
3.png

上圖中主要有如下所述三個主要組件:

Shard:分片服務(wù)器

用于存儲實際的數(shù)據(jù)塊,實際生產(chǎn)環(huán)境中一個shard server角色可由幾臺機器組個一個replica set承擔创夜,防止主機單點故障

Config Server:配置服務(wù)器

mongod實例杭跪,存儲了整個 分片群集的配置信息,其中包括 chunk信息。

Query Routers:前端路由

客戶端由此接入涧尿,且讓整個集群看上去像單一數(shù)據(jù)庫系奉,前端應(yīng)用可以透明使用

這里的兩個shard(分片)相當于mongodb節(jié)點服務(wù)器,內(nèi)部的塊是將order集合再切割的結(jié)果姑廉,隨著數(shù)據(jù)量的的增大,分片會分割和遷移,以滿足數(shù)據(jù)的均勻分布缺亮。

請求分流:通過路由節(jié)點將請求分發(fā)到對應(yīng)的分片和塊中

數(shù)據(jù)分流:內(nèi)部提供平衡器保證數(shù)據(jù)的均勻分布,這是數(shù)據(jù)平均分布式庄蹋、請求平均分布的前提

塊的拆分:3.4版本塊的最大容量為64M或者10w的數(shù)據(jù)瞬内,當?shù)竭_這個閾值迷雪,觸發(fā)塊的拆分限书,一分為二

塊的遷移:為保證數(shù)據(jù)在分片節(jié)點服務(wù)器分片節(jié)點服務(wù)器均勻分布,塊會在節(jié)點之間遷移章咧。一般相差8個分塊的時候觸發(fā)

MongoDB分片優(yōu)勢:

減少單個分片需要處理的請求數(shù)倦西,提高群集的存儲容量和吞吐量 比如,當插入一條數(shù)據(jù)時赁严,應(yīng)用只需要訪問存儲這條數(shù)據(jù)的分片 減少單分片存儲的數(shù)據(jù)扰柠,提高數(shù)據(jù)可用性,提高大型數(shù)據(jù)庫查詢服務(wù)的性能疼约。 當MongoDB單點數(shù)據(jù)庫服務(wù)器存儲和性能成為瓶頸卤档,或者需要部署大型應(yīng)用以充分利用內(nèi)存時,可以使用分片技術(shù)

環(huán)境準備

docker程剥、docker-compose劝枣、lunux。版本問題應(yīng)該不大织鲸,我用的是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.鏡像是否搞定了瀑踢。

技術(shù)分享圖片
3. 搭建

編寫yml文件扳还,docker-compose.yml

version: '2'
services:
  shard1:
    image: mongo:4.0.5
    container_name: mongo_shard1
    # --shardsvr: 這個參數(shù)僅僅只是將默認的27017端口改為27018,如果指定--port參數(shù),可用不需要這個參數(shù)
    # --directoryperdb:每個數(shù)據(jù)庫使用單獨的文件夾
    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: 這個參數(shù)僅僅是將默認端口由27017改為27019, 如果指定--port可不添加該參數(shù)
    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副本集里的主節(jié)點

4)操作shard1徘钥、shard2衔蹲、shard3,將shard*容器組作為shard角色。

5)將shard1舆驶、shard2橱健、shard3加入分片集群組。

執(zhí)行腳本

#提交進去使用notepad++ 修過為utf8格式 (mobaxterm 選擇文件右擊open with--選擇notepad++打開 ) 
#替換文本的換行等字符串 沙廉,
sed -i 's/\r$//'  deploy-and-start.sh

sh deploy-and-start.sh

# sh deploy-and-start.sh 等待腳本啟動完成拘荡,如果出現(xiàn)connect fail報錯,檢查一下網(wǎng)絡(luò)撬陵,再次啟動一次腳本即可珊皿。

到這里,單機版的mongodb的分片集群巨税,就搭建好了蟋定,一般真正的運維環(huán)境,Mongodb集群應(yīng)該分布在不同的機器草添,但是只要熟悉這套部署方案的機制驶兜,只要稍作修改,就可以實現(xiàn)远寸。

但是抄淑,Mongodb庫默認是不會將你的表進行分片的,是需要手動配置的驰后,如果不配置肆资,數(shù)據(jù)都會全部存放在主節(jié)點,不會均勻分配到各個分片灶芝。

現(xiàn)在手動將一個表做分片郑原,就拿order表作為例子

docker exec -it mongo_mongos bash
mongo --host 192.168.3.202 --port 27017

#數(shù)據(jù)庫 啟用 分片
sh.enableSharding("mongodbDemo")
# _id 字段進行哈希分片:
sh.shardCollection("mongodbDemo.order", {"_id": "hashed" }) 
#刷新路由
db.adminCommand("flushRouterConfig")
#讓當前分片支持平衡
sh.enableBalancing("mongodbDemo.order")
#開啟平衡
sh.startBalancer() 
#查看詳細分片信息
sh.status({"verbose":1})
#插入數(shù)據(jù)
use mongodbDemo
for (i = 1;i <= 100;i=i+1){
db.order.insert({'price': 1})
}
#查看該表分片數(shù)據(jù)信息
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'); //查看執(zhí)行計劃

db.c1.getIndexes()

COLLSCAN 全表掃描 最慢的,沒有走索引

IXSCAN 索引掃描

FETCH 根據(jù)索引去檢索指定document

11. 注意

  1. redis與mongodb對比监署,redis速度要快颤专,redis的純內(nèi)存的。

12. 擴展

MongoDB處理的數(shù)據(jù)是小于10億的.

當達到了PB級別的數(shù)據(jù):使用的NoSql是:hbase .
大數(shù)據(jù)本質(zhì):讓數(shù)據(jù)又價值值錢,而讓數(shù)據(jù)有意義首先經(jīng)過以下步驟: 1:收集數(shù)據(jù)(各式各樣的數(shù)據(jù))->2.清洗數(shù)據(jù)(格式化處理數(shù)據(jù))->3.分析統(tǒng)計.

hbase:列數(shù)據(jù)庫:k:v
1.對數(shù)據(jù)隨機讀寫钠乏,數(shù)據(jù)增刪改查
2.高并發(fā)操作栖秕,一秒能對PB級別的數(shù)據(jù),進行上千次不等操作
3.讀和寫都是簡單的操作晓避。

松散存儲--nosql---列族 (一個列族里面可以存放多個字段)
創(chuàng)建數(shù)據(jù)庫的時候簇捍,只要把列族創(chuàng)建好就行,至于你這列族里面幾個字段俏拱,隨便你暑塑。。

高效查詢只能通過rowkey:1001 這個锅必,要么直接=rowkey,要么給rowkey范圍非常重要事格,設(shè)計者rowkey(包含了使用了它的60%思想思維)

hbase,分片是根據(jù)rowkey來分配

  1. 盡量讓我們的rowkey分配均勻惕艳,還有松散, 可以利用多個節(jié)點.

  2. 業(yè)務(wù)有時候驹愚,需要查詢的時候远搪,可能需要盡量讓查詢的這個數(shù)據(jù)在一個節(jié)點上面。

    例如: 具體的業(yè)務(wù)具體分析:保存(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
//創(chuàng)建list 
create 'table' 'info1' 'info2'
//插入數(shù)據(jù)
put 'student','1001','info1:name','sss'
//查看數(shù)據(jù)
scan 'student'

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逢捺,一起剝皮案震驚了整個濱河市谁鳍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌劫瞳,老刑警劉巖倘潜,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異志于,居然都是意外死亡涮因,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門恨憎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蕊退,“玉大人,你說我怎么就攤上這事憔恳。” “怎么了净蚤?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵钥组,是天一觀的道長。 經(jīng)常有香客問我今瀑,道長程梦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任橘荠,我火速辦了婚禮屿附,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哥童。我一直安慰自己挺份,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布贮懈。 她就那樣靜靜地躺著匀泊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪朵你。 梳的紋絲不亂的頭發(fā)上各聘,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音抡医,去河邊找鬼躲因。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的大脉。 我是一名探鬼主播搁嗓,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼箱靴!你這毒婦竟也來了腺逛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤衡怀,失蹤者是張志新(化名)和其女友劉穎棍矛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抛杨,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡够委,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了怖现。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茁帽。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖屈嗤,靈堂內(nèi)的尸體忽然破棺而出潘拨,到底是詐尸還是另有隱情,我是刑警寧澤饶号,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布铁追,位于F島的核電站,受9級特大地震影響茫船,放射性物質(zhì)發(fā)生泄漏琅束。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一算谈、第九天 我趴在偏房一處隱蔽的房頂上張望涩禀。 院中可真熱鬧,春花似錦然眼、人聲如沸艾船。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丽声。三九已至,卻和暖如春觉义,著一層夾襖步出監(jiān)牢的瞬間雁社,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工晒骇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留霉撵,地道東北人磺浙。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像徒坡,于是被迫代替她去往敵國和親撕氧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容