MongoDB企业应用实战

1. MongoDB 复制集特性

1.1 复制集群的架构

mongo-cluster-architecture

1.2 复制集群搭建基础示例

  • 假设有3台服务器,IP分别为 10.211.55.910.211.55.1010.211.55.11

  • 主节点配置

dbpath=data
port=27017
fork=true
bind_ip=0.0.0.0
logpath=logs/master.log
replSet=tyrival
  • 从节点1配置
dbpath=data
port=27018
fork=true
bind_ip=0.0.0.0
logpath=logs/slave1.log
replSet=tyrival
  • 从节点2配置
dbpath=data
port=27019
fork=true
bind_ip=0.0.0.0
logpath=logs/slave2.log
replSet=tyrival
  • 分别启动三个节点
./bin/mongod -f conf/master.conf
./bin/mongod -f conf/slave1.conf
./bin/mongod -f conf/slave2.conf
  • 进入其中一个节点
# 假设进入10.211.55.9
./bin/mongo

# 可以查看集群状态,可以看到"errmsg" : "not master and slaveOk=false"
show dbs;
  • 集群复制配置管理
// 查看复制集群的帮助方法
rs.help()
  • 添加配置
// 声明配置变量
var cfg = {
      "_id": "tyrival",
    "members": [
        {"_id": 1, "host": "10.211.55.9:27017"},
        {"_id": 2, "host": "10.211.55.10:27018"}
    ]
}
// 初始化配置
rs.initiate(cfg)
/*
  可以看到如下信息:
  {
    "ok" : 1,
    "$clusterTime" : {
      "clusterTime" : Timestamp(1597996780, 1),
      "signature" : {
        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
        "keyId" : NumberLong(0)
      }
    },
    "operationTime" : Timestamp(1597996780, 1)
  }
 */

// 查看集群状态,可以看到集群已建立完成
rs.status()
  • 变更节点示例
// 插入新的复制节点
rs.add("10.211.55.11:27019")
// 删除slave 节点
rs.remove("10.211.55.11:27019")
  • 演示复制状态

    • 进入主节点客户端

      ./bin/mongo
      
    • 插入数据

      // 新建数据库
      use tyrival;
      
      // 插入数据
      db.emp.insert([
        {_id:1101,name:'鲁班',job:'讲师',dep:'讲师部',salary:10000},
        {_id:1102,name:'悟空',job:'讲师',dep:'讲师部',salary:10000},
        {_id:1103,name:'诸葛',job:'讲师',dep:'讲师部',salary:10000},
        {_id:1105,name:'赵云',job:'讲师',dep:'讲师部',salary:8000},
        {_id:1106,name:'韩信',job:'校长',dep:'校办',salary:20000},
        {_id:1107,name:'貂蝉',job:'班主任',dep:'客服部',salary:8000},
        {_id:1108,name:'安其',job:'班主任',dep:'客服部',salary:8000},
        {_id:1109,name:'李白',job:'教务',dep:'教务处',salary:8000},
        {_id:1110,name:'默子',job:'教务',dep:'教务处',salary:8000},
        {_id:1111,name:'大乔',job:'助教',dep:'客服部',salary:5000},
        {_id:1112,name:'小乔',job:'助教',dep:'客服部',salary:3000},
      ]);
      
    • 进入从节点查看数据

      // 需要先确认从节点准备完成,然后才可查看数据
      rs.slaveOk()
      
      // 切换数据库
      use tyrival;
      
      // 查看数据,可以查到上面插入的数据
      db.emp.find();
      

      注意

      第一次进入从节点,不能读取数据,需要执行 rs.slaveOk()

    • 尝试在从节点下插入数据

      // 会提示如下信息,无法插入数据
      {
      ...
      "ok" : 0,
        "errmsg" : "not master",
      ...
      }
      

1.3 复制集群选举操作

为了保证高可用,在集群当中如果主节点挂掉后,会自动 在从节点中选举一个 重新做为主节点。

  • 演示节点的切换操作
  • kill 主节点的mongodb进程
  • 进入从节点查看集群状态 rs.status()
{
    "set" : "tyrival",
    "members" : [
        {
            "_id" : 1,
      // 节点不可达
            "stateStr" : "(not reachable/healthy)",
        },
        {
            "_id" : 2,
      // 成为主节点
            "stateStr" : "PRIMARY",
        },
        {
            "_id" : 3,
            "stateStr" : "SECONDARY",
        }
    ]
}
  • 重新启动之前的27017节点,可以看到这个节点变成了从节点
{
  "_id" : 1,
  "name" : "10.211.55.9:27017",
  "stateStr" : "SECONDARY",
}
选举的原理

在 mongodb 中通过在 集群配置中的 priority 属性值大小来决定选举谁做为主节点,通时也可以设置 arbiterOnlytrue 表示 做为裁判节点用于执行选举操作,该配置下的节点永远不会被选举为主节点和从节点。

// 重新配置节点
var cfg = {
      "_id": "tyrival",
    "protocolVersion": 1,
    "members": [
        {"_id": 1, "host": "10.211.55.9:27017", "priority": 10},
        {"_id": 2, "host": "10.211.55.10:27018", "priority": 5},
          // 权重为0的节点,永远不会参与选举
        {"_id": 3, "host": "10.211.55.11:27019", "priority": 0},
          // 仲裁节点,不参与选举,也不参与工作
        {"_id": 4, "host": "10.211.55.12:27020", "arbiterOnly": true}
    ]
}

// 重新装载配置,并重新生成集群节点。
rs.reconfig(cfg)

// 重新查看集群状态
rs.isMaster()

/*
    可以看到如下状态,27019节点不参与选举,27020节点为仲裁节点
  {
    "hosts" : [
      "10.211.55.9:27017",
      "10.211.55.10:27018"
    ],
    "passives" : [
      "10.211.55.11:27019"
    ],
    "arbiters" : [
      "10.211.55.12:27020"
    ],
  }
 */

注意

如果对节点进行权重配置时,27019节点为主节点,则无法将其权重设置为0。

{
    "ok" : 0,
    "errmsg" : "This node, 10.211.55.10:27019, with _id MemberId(3) is not electable under the new configuration with {version: 2, term: 2} for replica set tyrival",
}
节点说明
  • PRIMARY 节点:可以查询和新增数据;

  • SECONDARY 节点:只能查询不能新增,基于priority权重可以被选为主节点;

  • RBITER 节点:不能查询数据和新增数据 ,不能变成主节点。

2. MongoDB 分片操作

2.1 分片的概念

随着数据的增长,单机实例的瓶颈是很明显的。可以通过复制的机制应对压力,但mongodb中单个集群的 节点数量限制到了12个以内,所以需要通过分片进一步横向扩展。此外分片也可节约磁盘的存储。

2.2 mongodb 中的分片架构

mongo-shard-architecture

分片中的节点说明
  • 路由节点(mongos):用于分发用户的请求,起到反向代理的作用。
  • 配置节点(config):用于存储分片的元数据信息,路由节基于元数据信息 决定把请求发给哪个分片。(3.4版本之后,该节点必须使用复制集。)
  • 分片节点(shard):用于实际存储的节点,其每个数据块默认为64M,满了之后就会产生新的数据库。

2.3 分片示例流程

  • 准备3台服务器
    • 10.211.55.9 路由器 27017,分片节点2-primary 57017
    • 10.211.55.10 配置节点1 37017,分片节点1-primary 47017
    • 10.211.55.11 配置节点2 37018,分片节点2-secondary 47018
  • 配置并启动config节点集群
## 节点1
# config-37017.conf
dbpath=data/config1
port=37017
fork=true
bind_ip=0.0.0.0
logpath=logs/config1.log
replSet=configCluster
configsvr=true

# 10.211.55.10
./bin/mongod -f conf/config-37017.conf

## 节点2
# config-37018.conf
dbpath=data/config2
port=37018
fork=true
bind_ip=0.0.0.0
logpath=logs/config2.log
replSet=configCluster
configsvr=true

# 10.211.55.11
./bin/mongod -f conf/config-37018.conf
  • 进入shell,并添加config集群配置:
./bin/mongo --port 37017

// 添加配置
var cfg ={
      "_id": "configCluster",
    "protocolVersion": 1,
    "members": [
        {"_id": 0, "host": "10.211.55.10:37017"},
        {"_id": 1, "host": "10.211.55.11:37018"}
    ]
}
// 重新装载配置,并重新生成集群。
rs.initiate(cfg)
  • 配置路由节点mongos
# 节点 route-27017.conf
port=27017
bind_ip=0.0.0.0
fork=true
logpath=logs/route.log
configdb=conf/10.211.55.10:37017,10.211.55.11:37018

# 10.211.55.9
./bin/mongos -f conf/route-27017.conf
  • 配置shard1节点集群
## 启动节点
# 节点1 shard1-47017.conf
dbpath=data/shard47017
port=47017
fork=true
bind_ip=0.0.0.0
logpath=logs/shard47017.log
shardsvr=true
replSet=shard1
# 10.211.55.10
./bin/mongod -f conf/shard1-47017.conf

# 节点2 shard1-47018.conf
dbpath=data/shard47018
port=47018
fork=true
bind_ip=0.0.0.0
logpath=logs/shard47018.log
shardsvr=true
replSet=shard1
# 10.211.55.11
./bin/mongod -f conf/shard1-47018.conf


## 配置集群
# 10.211.55.10
./bin/mongo --port 47017

# 配置集群shard1
var cfg = {
      "_id": "shard1",
    "members": [
        {"_id": 1, "host": "10.211.55.10:47017"},
        {"_id": 2, "host": "10.211.55.11:47018"}
    ]
}
rs.initiate(cfg)
  • 配置shard2集群
## 启动节点
# 节点2 shard2-57017.conf
dbpath=data/shard57017
port=57017
fork=true
bind_ip=0.0.0.0
logpath=logs/shard57017.log
shardsvr=true
replSet=shard2
# 10.211.55.9
./bin/mongod -f conf/shard2-57017.conf


## 配置集群
# 10.211.55.9
./bin/mongo --port 57017
# 配置集群shard2
var cfg = {
      "_id": "shard2",
    "members": [
        {"_id": 1, "host": "10.211.55.9:57017"}
    ]
}
rs.initiate(cfg)
  • 添加分片节点
// 10.211.55.9 进入路由节点
./bin/mongo --port 27017

// 添加分片节点,添加格式为 集群名/IP:PORT,...
sh.addShard("shard1/10.211.55.10:47017,10.211.55.11:47018");
sh.addShard("shard2/10.211.55.9:57017");

// 查看状态
sh.status()
/*
  shards:
        {  "_id" : "shard1",  "host" : "shard1/10.211.55.10:47017,10.211.55.11:47018",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/10.211.55.9:57017",  "state" : 1 }
  ...
 */
  • 为数据库开启分片功能
// 为数据库tyrival开启分片
sh.enableSharding("tyrival")
  • 为指定集合开启分片功能
sh.shardCollection("tyrival.emp",{"_id":1})
  • 修改分片大小
use config
db.settings.find()
db.settings.save({_id:"chunksize",value:1})
  • 尝试插入10万条数据:
for(var i = 1; i <= 100000; i++){
        db.emp.insert({"_id":i,"name":"copy"+i});
}

// 查看分片情况
sh.status()

注意

使用_id、时间等进行分片,优点是可以无限分下去,缺点是热点数据将会进入一个分片,导致数据分布不均匀;

解决办法:使用 _id hash创建索引

db.emp.createIndex({_id: 'hashed'})

3. MongoDB用户权限管理

  • 创建管理员用户
use admin;
db.createUser({"user":"admin","pwd":"123456","roles":["root"]})
// 验证用户信息
db.auth("admin","123456")
// 查看用户信息
db.getUsers() 
// 修改密码
db.changeUserPassword("admin","123456")
  • 以auth方式启动mongod
// 以auth方向启动mongod,也可以在mongo.conf中添加auth=true参数,mongdb的权限体系才会起作用
./bin/mongod -f conf/mongo.conf --auth

// 验证用户
use admin;
db.auth("admin","123456")
  • 创建只读用户
db.createUser({"user":"dev","pwd":"123456","roles":["read"]})
  • 重新登陆 验证用户权限
use luban;
db.auth("dev","123456")

4. Docker搭建集群

4.1 docker-compose.yml

version: '3'
services:
  mongo_cs1:
    image: mongo
    container_name: mongo_cs1
    networks:
      mongo:
        ipv4_address: 172.20.0.12
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data/config01/db:/data/db
      - ./data/config01/configdb:/data/configdb
      - ./data/config01/backup:/data/backup
    ports:
      - 27019:27019
    command: --configsvr --replSet "rs_configsvr" --bind_ip_all
    restart: always
  mongo_cs2:
    image: mongo
    container_name: mongo_cs2
    networks:
      mongo:
        ipv4_address: 172.20.0.13
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data/config02/db:/data/db
      - ./data/config02/configdb:/data/configdb
      - ./data/config02/backup:/data/backup
    ports:
      - 27029:27019
    command: --configsvr --replSet "rs_configsvr" --bind_ip_all
    restart: always
  mongo_cs3:
    image: mongo
    container_name: mongo_cs3
    networks:
      mongo:
        ipv4_address: 172.20.0.14
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data/config03/db:/data/db
      - ./data/config03/configdb:/data/configdb
      - ./data/config03/backup:/data/backup
    ports:
      - 27039:27019
    command: --configsvr --replSet "rs_configsvr" --bind_ip_all
    restart: always

  mongo_sh01:
    image: mongo
    container_name: mongo_sh01
    networks:
      mongo:
        ipv4_address: 172.20.0.15
    ports:
      - 27018:27018
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data/shard01/db:/data/db
      - ./data/shard01/configdb:/data/configdb
      - ./data/shard01/backup:/data/backup
    command: --shardsvr --replSet "rs_shardsvr0" --bind_ip_all
    restart: always
    depends_on:
      - mongo_cs1
      - mongo_cs2
      - mongo_cs3
  mongo_sh02:
    image: mongo
    container_name: mongo_sh02
    networks:
      mongo:
        ipv4_address: 172.20.0.16
    ports:
      - 27028:27018
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data/shard02/db:/data/db
      - ./data/shard02/configdb:/data/configdb
      - ./data/shard02/backup:/data/backup
    command: --shardsvr --replSet "rs_shardsvr0" --bind_ip_all
    restart: always
    depends_on:
      - mongo_cs1
      - mongo_cs2
      - mongo_cs3

  mongo_sh11:
    image: mongo
    container_name: mongo_sh11
    networks:
      mongo:
        ipv4_address: 172.20.0.17
    ports:
      - 27038:27018
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data/shard11/db:/data/db
      - ./data/shard11/configdb:/data/configdb
      - ./data/shard11/backup:/data/backup
    command: --shardsvr --replSet "rs_shardsvr1" --bind_ip_all
    restart: always
    depends_on:
      - mongo_cs1
      - mongo_cs2
      - mongo_cs3
  mongo_sh12:
    image: mongo
    container_name: mongo_sh12
    networks:
      mongo:
        ipv4_address: 172.20.0.18
    ports:
      - 27048:27018
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data/shard12/db:/data/db
      - ./data/shard12/configdb:/data/configdb
      - ./data/shard12/backup:/data/backup
    command: --shardsvr --replSet "rs_shardsvr1" --bind_ip_all
    restart: always
    depends_on:
      - mongo_cs1
      - mongo_cs2
      - mongo_cs3


  mongos:
    image: mongo
    container_name: mongos
    networks:
      mongo:
        ipv4_address: 172.20.0.11
    ports:
      - 27017:27017
    volumes:
      - /etc/localtime:/etc/localtime
      - ./data/mongos/db:/data/db
      - ./data/mongos/configdb:/data/configdb
    entrypoint: mongos
    command: --configdb rs_configsvr/172.20.0.12:27019,172.20.0.13:27019,172.20.0.14:27019 --bind_ip_all
    restart: always
    depends_on:
        - mongo_sh01
        - mongo_sh02
        - mongo_sh11
        - mongo_sh12

networks:
  mongo:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.10/24

4.2 配置

配置config集群

进入任意config容器

docker exec -it mongo_cs1 /bin/bash

连接mongodb

mongo --host 172.20.0.12 --port 27019

初始化集群

rs.initiate(
  {
    _id: "rs_configsvr",
    configsvr: true,
    members: [
      { _id : 0, host : "172.20.0.12:27019" },
      { _id : 1, host : "172.20.0.13:27019" },
      { _id : 2, host : "172.20.0.14:27019" }
    ]
  }
)

配置分片集群

进入任意分片容器

docker exec -it mongo_sh01 /bin/bash

连接mongodb

mongo --host 172.20.0.15 --port 27018

初始化集群

rs.initiate(
  {
    _id : "rs_shardsvr0",
    members: [
      { _id : 0, host : "172.20.0.15:27018" },
      { _id : 1, host : "172.20.0.16:27018" }
    ]
  }
)

同样的方法配置分片集群2

docker exec -it mongo_sh11 /bin/bash

mongo --host 172.20.0.17 --port 27018

rs.initiate(
  {
    _id : "rs_shardsvr1",
    members: [
      { _id : 0, host : "172.20.0.17:27018" },
      { _id : 1, host : "172.20.0.18:27018" }
    ]
  }
)

配置路由节点

进入路由节点容器

docker exec -it mongos /bin/bash

连接mongodb

mongo --host 172.20.0.11 --port 27017

将分片服务器添加到路由节点

sh.addShard("rs_shardsvr0/172.20.0.15:27018,172.20.0.16:27018")
sh.addShard("rs_shardsvr1/172.20.0.17:27018,172.20.0.18:27018")

4.3 验证

连接到路由节点mongos

docker exec -it mongos /bin/bash
mongo --host 172.20.0.11 --port 27017

配置一个数据库并启用分片,设置分片规则为hashed

sh.enableSharding("tyrival")
sh.shardCollection("tyrival.order", {"_id": "hashed" })

切换数据库,并添加数据

use tyrival

for (i = 1; i <= 1000; i=i+1){
    db.order.insert({'price': 1})
}

查看数据添加情况

db.order.find().count()
// 1000

进入任意分片节点,并连接数据库

docker exec -it mongo_sh01 bash
mongo --host 172.20.0.15 --port 27018

查看分片中的数据量

use tyrival
db.order.find().count()
// 485

results matching ""

    No results matching ""