前言

照理来说我是没有什么资格讲关于数据库相关的一些知识的,我不是专门搞数据库的,只是平时工作重点涉及到运维平台开发,而运维平台开发中经常会用到mongo,基本实现了业务逻辑代码和配置数据分离,那么就让我们通过这篇关于mongo学习笔记快速入门吧


MongoDB的简介

mongo音译是不是觉得跟芒果有些相似呢,但其实不是芒果的意思,而是英文Humongous 翻译:巨大的  缩写


MongoDB趋势及未来展望

MongoDB从入门到进阶_数据库


对于MongoDB的认识Q&A

Q

A

什么是MongoDB?

一个以JSON为数据模型的文档数据库

为什么叫文档数据库?

文档来自于"JSON Document",并非我们一般理解的PDF,WORD

谁开发MongoDB?

上市公司MongoDB Inc,总部位于美国纽约。

主要用途有那些?

OLTP\OLA数据库,类似Oracle,MySQL,海量数据处理

,数据平台


主要特点是什么?

无模式或可选.友好的JSON数据模型,开发方便.

MongoDB是免费的

MongoDB有两个发布:社区版和企业版.企业版基于商业协议,需付费


MongoDB版本变迁

MongoDB从入门到进阶_mongodb_02


MongoDB vs. RDBMS

功能

MongoDB

RDBMS(关系数据库系统)

数据模型

JSON

Relational

数据库类型

OLTP/OLAP

OLTP/OLAP

CRUD操作

MQL/SQL

SQL/SQLX

高可用

原生Replica-Set

Cluster,中间

横向扩展能力

原生MSC

分片,中间件

索引支持

B-Tree,F-text,GIS,Multikey,HASH,TTL,

B-Tree

开发难度

easy

hard

数据容量

无理论上限

千万,亿

扩展方式

垂直扩展+水平扩展

垂直扩展

MongoDB从入门到进阶_mongodb_03


MongoDB vs. MySQL逻辑结构对比

MySQL

MongoDB

database

database

table

collection

row

document

MongoDB从入门到进阶_数据库_04


MongoDB特色及优势

MongoDB优势:面向开发者的易用+高效数据库

MongoDB从入门到进阶_mongodb_05


我们用一张很形象的图来表达

MongoDB从入门到进阶_数据库_06


快速响应业务变化

a.多类型:
同一个Collection中,可以包含不同字段(类型)的文档对象
b.更灵活:
线上修改结构,应用与数据库均无须下线.

MongoDB从入门到进阶_数据库_07


简洁的开发模式

a. 数据库引擎只需要再一个存储区读写.
b. 反范式,无关联的组织极大优化查询速度
c. 程序API自然,开发快速


MongoDB优势:原生的高可用和横向扩展能力

高可用能力

a. Replica Set - 2 to 50个成员
b. 自恢复
c. 多中心容灾能力
d. 滚动服务 - 最小化服务终端

MongoDB从入门到进阶_mongodb_08


横向扩展能力

a. 需要的时候无缝扩展
b. 应用全透明
c. 多种数据分布策略
d. 轻松支持TB-PB数量级

MongoDB从入门到进阶_数据库_09


MongoDB技术优势总结

a. JSON 结构和对象模型接近,开发代码量低
b. JSON 的动态模型意味着更容易响应新的业务需求
c. 复制集提供99.999%高可用
d. 分片架构支持海量数据和无缝扩容


MongoDB的获取和安装

获取MongoDB

https://www.mongodb.com/try/download/community

安装MongoDB

上传软件并解压

安装包我这里用的式4.2.24版本的

MongoDB从入门到进阶_数据库_10

tar xf mongodb-linux-x86_64-rhel70-4.2.24.tgz
mv mongodb-linux-x86_64-rhel70-4.2.24 /usr/local/mongodb

添加环境变量

编辑/etc/profile,末尾加上export PATH=$PATH:/usr/local/mongodb/bin

然后执行source /etc/profile,执行mongo命令看到有版本号出来了就代表安装已经成功了

MongoDB从入门到进阶_mongodb_11

优化

# 关闭THP
root用户下
在vim /etc/rc.local最后添加如下代码
if test -f /sys/kernel/mm/transparent_hugepage/enable;then
	echo  never > /sys/kernel/mm/transparent_hugepage/enable
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag;then
	echo  never > /sys/kernel/mm/transparent_hugepage/defrag
fi

[root@master ~]# cat /sys/kernel/mm/transparent_hugepage/enabled 
[always] madvise never
[root@master ~]# cat /sys/kernel/mm/transparent_hugepage/defrag 
[always] madvise never

这是centos配置方式,其他系统关闭参考官方文档:
https://docs.mongodb.com/manual/tutorial/transparent-huge-pages/

为什么要关闭?
内存碎片:启用 THP 可能会导致内存碎片,这会增加数据库的内存使用量并可能降低性能。

延迟:THP 需要额外的 CPU 和内存资源来合并和拆分页面,这可能会导致延迟,尤其是在内存紧张的情况下。

不稳定性:在一些情况下,启用 THP 可能导致 MongoDB 进程不稳定或崩溃。


环境准备

(1)创建所需用户和组
useradd mongod
passwd mongod
(2)创建mongodb所需目录结构
mkdir -p /mongodb/conf
mkdir -p /mongodb/log
mkdir -p /mongodb/data
(3)修改权限
chown -R mongod:mongod /mongodb
(4)切换用户并设置环境变量
su - mongod
vim .bash_profile
export PATH=/usr/local/mongodb/bin:$PATH
source .bash_profile


启动数据库并初始化数据

su - mongod
mongod --dbpath=/mongodb/data --logpath=/mongodb/log/mongodb.log --port=27017 --logappend --fork


#--dbpath mongo数据存储位置
# --logpath mongo日志存储位置
# --port 监听端口
# --logappend 日志追加模式
# --fork  后台运行

# 登陆mongodb
$ mongo


使用配置文件

# 普通配置文件应用
vim /mongodb/conf/mongodb.conf
logpath=/mongodb/log/mongodb.log
dbpath=/mongodb/data
port=27017
logappend=true
fork=true

# 关闭mongodb
mongod -f /mongodb/conf/mongodb.conf --shutdown
# 使用配置文件启动mongodb
mongod -f /mongodb/conf/mongodb.conf

# YAML 配置文件应用
--
NOTE:
YAML dose not support tab characters for indentaion: use spaces instead.

--系统日志有关
systemLog:
   destination: file
   path: "/mongodb/log/mongodb.log"   --日志位置
   logAppend: true                    --日志以追加模式记录

--数据存储有关
storage:
   journal:
   	enabled: true
   dbPath: "/mongodb/data"            --数据路径的位置

-- 进程控制
processManagement:
   fork: true                         --后台守护进程
   pidFilePath: <string>              --pid文件的位置,一般不用配置,可以去掉这行,自动生成到data中

-- 网络配置有关
net:
	bindIp: <ip>                       --监听地址,如果不配置这行是监听在localhost
   port: <port>                       --端口号,默认不配置端口,默认是27017
 
 --安全验证有关配置
 security:
 	authorization: enabled             --是否打开用户密码验证
 
 ————————————————————————--以下是复制集与分片集群有关------------------------
 replication:
 	oplogSizeMB: <NUM>
   replSetName: "<REPSETNAME>"
   secondaryIndexPrefetch: "all"
   
 
 sharding:
 	clusterRole: <string>
   archiveMoveChunks: <boolean>
   
  
 ---for mongos only
 replication:
 	localPingThresholdMs: <int>
 
 sharding:
 	configDB: <string>
---
..........


用户基本管理

a.注意事项

用户管理 ******
注意:
验证库,建立用户时use到的库,在使用用户时,要加上验证库才能登陆.
对于管理员用户,必须在admin下创建.
1.建用户时,use到库,就是此用户的验证库
2.登录时,必须明确指定验证库才能登录
3.通常,管理员用的验证库是admin,普通用户的验证库一般是所管理的库设置为验证库
4.如果直接登录到数据库,不进行use,默认的验证库是test,不是我们生产建议的.

b.基本语法

use admin
db.createUser
{
	user: "<name>",
  pwd: "<cleartext password>",
  roles: [
  	{role: "<role>", 
    db: "<database>"} | "<role>",
    ...
  ]
}

基本语法说明:
user: 用户名
pwd: 密码
roles:
	role:角色名
  db:作用对象
role: root, readWrite, read

验证数据库:
mongo -u test -p 123 10.0.0.65/test


c.用户实例

(1) --创建超级管理员:管理所有数据库(必须先use admin再去创建)
$ mongo
use admin
db.createUser(
	{
		user: "root",
		pwd: "root123",
		roles: [{role: "root", db: "admin"}]
	}
)

验证用户
db.auth('root', 'root123')

配置文件中,加入以下配置
security:
	authorization: enabled

重启mongodb
mongod -f /mongodb/conf/mongodb.conf --shutdown
mongod -f /mongodb/conf/mongodb.conf

登陆验证
mongo -uroot -proot123 admin
mongo -uroot -proot123 10.0.0.65/admim

查看用户:
use admin
db.system.users.find().pretty()

创建对app数据库,读,写权限的用户app01:
(1)超级管理员用户登陆
mongo -uroot -proot123 admin
(2)选择一个验证库
use app
(3)创建用户
db.createUser(
	{
  	user: "app01",
    pwd: "app01",
    roles: [{role: "readWrite", db: "app"}]
  }
)

mongo -uapp01 -papp01 app

创建app数据库读写权限的用户并对test数据具有读权限:
mongo -uroot -proot123 localhost/admin
use app
db.createUser(
{
	user: "app03", 
  pwd: "app03", 
  roles: [{role: "readWrite", db: "app"}, {role: "read", db: "test"}
  ]
}
)

查询mongodb中的用户信息
mongo -uroot -proot123 localhost/admin
db.system.users.find().pretty()

删除用户(root身份登录,use到验证库)
mongo -uroot -proot123 localhost/admin
use app
db.dropUser("app03")


d.角色关系

MongoDB从入门到进阶_数据库_12

MongoDB基本CRUD

通用方法和帮助获取

a.获取帮助
>help
>db.help()
>db.t1.help()
>db.[TAB][TAB]
>db.t1[TAB][TAB]
使用mongo获取命令帮助我们可以使用mongo自带的tab键自动补全功能

b.常用操作
//查看当前db版本
test> db.version()
//显示当前数据库
test>db
//查询所有数据库
test> show dbs
//切换数据库
> use local
//显示当前数据库状态
查看local数据
test> use local
local> db.stats()
查看当前数据库的连接机器地址
>db.getMongo()

//指定数据库进行连接:(默认连接本机test数据库)
# mongo -uroot -proot123 localhost/admin

c.库和表的操作
//建库
> use test
//删除
> db.dropDatabase()
{"dropped": "test", "ok" : 1 }

// 创建集合(表)
方法1
admin> use app
app> db.createCollection('a')
app> db.createCollection('b')

方法2:当插入一个文档的时候,一个集合就会自动创建.
admin> use app
switched to db app
app> db.c.insert({username: "mongodb"})
WriteResult({ "nInserted" : 1 })
app> show collections

app> db.c.find()
{ "_id" : ObjectId("64ccbf1dee80662b77a8cbe5"), "username" : "mongodb" }

//删除集合
app> use app
app> db.log.drop() //删除集合
app> show collections

// 重命名集合
app> db.a.renameCollection("log")
app> show collections


使用insert完成插入操作

操作格式:
db.<集合>.insertOne(<JSON对象>)
db.<集合>.insertMany(<JSON 1>, <JSON 2>, ...<JSON n>)
示例:
db.fruit.insertOne({name: "apple"})
db.fruit.insertMany([
	{name: "apple"},
  {name: "pear"},
  {name: "orange"}
])
批量插入数据:
 for(i=0;i<100;i++) { db.log.insert({"uid": i, "name": "mongodb", "age": 6, "date": new Date()}) }
 
 db.log.find().pretty()


使用find查询文档

# 关于find:
find是MongoDB中查询数据的基本指令,相当于SQL中的select
find返回的是游标.
# find示例:
db.movies.find({"year": 1975}) //单条件查询
db.movies.find({"year": 1989, "title": "Batman"}) //多条件and查询
db.movies.find($and: [{"title": "Batman"}, {"category": "action"}]) //and的另外一种形式
一种形式
db.movies.find({$or: [{"year": 1989}, {"title": "Batman"}]}) //多条件or查询
db.movies.find({"title": /^B/}) //按正则表达式查找

查询条件对照表

SQL

MQL

 a = 1

{a:1}

a <> 1

{a: {$ne: 1}}

a > 1

{a: {$gt: 1}}

a >= 1

{a: {$gte: 1}}

a < 1

{a: {$lt: 1}}

a <= 1

{a: {$lte: 1}}

查询逻辑对照表

SQL

MQL

a = 1 AND b = 1

{a: 1, b: 1}或{$and: [{a: 1}, {b: 1}]}

a = 1 OR b = 1

{$or: [{a: 1}, {b: 1}]}

a IS NULL

{a: {$exists: false}}

a IN (1, 2, 3)

{a: {$in: [1, 2 ,3]}}

查看逻辑运算符

$lt: 存在并小于
$lte: 存在并小于等于
$gt: 存在并大于等于
$ne: 不存在或存在但不等于
$in: 存在并在指定数组中
$nine: 不存在或不在指定数组中
$or: 匹配两个多多个条件中的一个
$and: 匹配全部条件

使用find搜索子文档

find支持使用"field.sub_field"的形式查询子文档,假设有一个文档
db.fruit.insertOne({
	name: "apple",
  from: {
  	country: "China",
    province: "Guangdong"
  }
})

正确写法:
db.fruit.find({"from.country": "China"})

使用find搜索数组

find支持对数组中的元素进行搜索,假设有一个文档:
db.fruit.insert([
 {"name": "Apple", color: ["red", "green"]}, 
 {"name": "Mongo", color: ["yellow", "green"]}
])

查看单个条件:
db.fruit.find({color: "red"})
查询多个条件:
db.fruit.find({$or: [{color: "red"}, {color: "yellow"}]})

使用find搜索数组中的对象

考虑以下文档,在其中搜索
db.movies.insertOne({
"title": "Raiders of the Lost Ark",
"filming_locations":[
{"city": "Los Angeles", "state": "CA", "country": "USA"},
{"city": "Rome", "state": "Lazio", "country": "Italy"},
{"city": "Florence", "state": "SC", "country": "USA"}
]
})
//查找城市是Rom的记录
db.movies.find({"filming_locations.city": "Rome"})

使用find搜索数组中的对象

在数组中搜索子对象的多个字段时,如果使用$elemMatch,它表示必须式同一个子对象满足多个条件
考虑以下两个查询:
db.getCollection("movies").find({
  "filming_locations": {
    $elemMatch: { "city": "Rome", "country": "Italy" }
  }
})

控制find返回的字段

find可以指定只返回指定的字段
_id字段必须明确指明返回,否则默认返回
在MongoDB中我们称这为投影
db.movies.find({},{"_id": 0, title:1}) # 不返回_id,返回title

使用remove删除文档

remove命令需要配置查询条件使用
匹配查询条件的文档会被删除
指定一个空文档条件会删除所有文档
以下示例:
db.testcol.remove({a: 1}) //删除a等于1的记录
db.testcol.remove({a: {$lt:5}}) //删除a小于5的记录
db.testcol.remove({})  //删除所有记录
db.testcol.remove() //报错

使用update更新文档

update 操作执行格式:db.<集合>.update(<查询条件>, <更新字段>)
以以下数据为例:
db.fruit.insertMany([
{name: "apple"}, 
{name: "pear"},
{name: "orange"}
])
db.fruit.updateOne({name: "apple"}, {$set: {form: "China"}})
查询name为apple的记录,将找到的记录的form设置为C

使用update更新文档

使用updateOne表示无论条件匹配多少条记录,始终只更新一条
使用updateMany 表示条件匹配多少条就更新多少条
updateOne/updateMany方法要求更新条件部分必须具有以下之一,否则将报错
$set/$unset
$push/$pushAll/$pop
$pull/$pushAll
$addToSet

//报错
db.fruit.updateOne({name: "apple"}, {from: "China"})

使用update更新数组

$push: 增加一个对象到数组底部
$pushAll: 增加多个对象到数组底部
$pop: 从数组底部删除一个对象
$pullL 如果匹配指定的值,从数组中删除相应的对象
$pullAll: 如果匹配任意的值,从数据中删除相应的对象
$addToSet:如果不存在则增加一个值到数组

使用drop删除集合

使用db.<集合>.drop() 来删除一个集合
集合中的全部文档都会被删除
集合相关的索引也会被删除
db.colToBeDropped.drop()

使用dropDatabase删除数据库

使用db.dropDatabase()来删除数据库
数据库相应的文件也会被删除,磁盘空间将被释放
use tempDB
db.dropDatabase()
show collections //No collections
show dbs  //The db is

通过python操作mongodb

使用python中的pymongo模块操作mongodb数据库实战代码

import pymongo


class OptMango:
    def __init__(self, mongobase='mongodb://xx.xx.xx.x:8901',
                 user='xxx', passwd='xxx', authdb='xxx', db='xxx'):
        """
        与DBinfo类似,正式测试不同配置,上线过程需要注意
        """
        # 线上代码 千年不动
        self.mongobase = mongobase
        self.user = user
        self.passwd = passwd
        self.authdb = authdb
        self.db = db
        self.con = self.make_con()

    def make_con(self):
        return pymongo.MongoClient(self.mongobase, username=self.user, password=self.passwd
                                   , authSource=self.authdb)

    def get_DB_COL(self, col):
        """
        为了方便操作。返回DB对象与COL对象
        :param col:
        :return:
        """
        DB = self.con[self.db]
        COL = col[col]
        return DB, COL

    def update_one_rec(self, col, f, data):
        """
        更新数据库,无匹配则插入
        :param col:
        :param f:
        :param data:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        try:
            r = COL.update_one(f, {'$set': data}, upsert=True)
        except Exception as E:
            return {'success': False, 'msg': str(E)}
        return {'success': True, 'msg': r}

    def insert_one_rec(self, col, data):
        """
        向db库col集合中添加数。
        :param col:
        :param data:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        try:
            r = COL.insert_one(data)
        except Exception as E:
            return {'success': False, 'msg': str(E)}
        return {'success': True, 'msg': r}

    def delete_one_rec(self, col, f):
        """
        根据过滤器f删除db库col集合中命中的第一个数据
        :param col:
        :param f:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        r = COL.find_one_and_delete(f)
        return r

    def delete_rec(self, col, f):
        """
        根据过滤器f删除db库col集合中命中的数据
        :param col:
        :param f:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        r = COL.delete_many(f)
        return r

    def find_rec(self, col, f):
        """
        根据过滤器f获取db库col集合中的数据
        :param col:
        :param f:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        r = COL.find_one(f)
        return r

    def find_rec_one(self, col, f):
        """
        根据过滤器f获取db库col集合中的数据 只取一个
        :param db:
        :param col:
        :param f:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        r = COL.find_one(f)
        return r

    def find_rec_all(self, col, f):
        """
        根据过滤器f获取db库col集合中的数据
        :param db:
        :param col:
        :param f:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        r = COL.find(f)
        return r

    def find_rec_page(self, col, f, page_size, page_no):
        """
        根据过滤器f以及分页数据获取db库col集合中的数据
        :param db:
        :param col:
        :param f:
        :param page_size:
        :param page_no:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        skip = page_size * (page_no - 1)
        r = COL.find(f).limit(page_size).skip(skip)
        return r

    def rec_count(self, col, f=None):
        """
        返回db库col集合中数据条目数
        :param col:
        :param f:
        :return:
        """
        if f is None:
            f = {}
        DB = self.con[self.db]
        COL = DB[col]
        r = COL.count_documents(f)
        return r

    def creat_index(self, col, index):
        """
        添加索引
        :param col:
        :param index:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        r = COL.create_index([(index, pymongo.ASCENDING)], unique=True)  # 加上唯一索引避免重复
        return r

    def drop_index(self, col, index):
        """
        删除索引
        :param col:
        :param index:
        :return:
        """
        DB = self.con[self.db]
        COL = DB[col]
        r = COL.drop_index(index)  # 删除索引
        return r

    def close_con(self):
        """
        关闭连接
        :return:
        """
        r = self.con.close()
        return r

MongoDB RS(Replica Set)

复制集的作用

MongoDB 复制集的主要意义在于实现服务高可用,它的实现依赖于两个方面的功能:
数据写入时将数据迅速复制到另一个独立节点上
在接受写入的节点发生故障时自动选举出一个新的替代节点

在实现高可用的同时,复制集实现了其他几个附加作用:
数据分发:将数据从一个区域复制到另外一个区域,减少另一个区域的读延迟
读写分离:不同类型的压力分别在不同的节点上执行
异地容灾:在数据重故障时候快速切换到异地

典型复制集结构

一个典型的复制集由3个以上具有投票权的节点组成,包括:
一个主节点(PRIAMARY):接受写入操作和选举时投票
两个(或多个)从节点(SECONDARY):复制主节点上的新数据和选举时投票
Arbiter(投票节点)

MongoDB从入门到进阶_数据库_13

数据是如何复制的

当一个修改操作,无论是插入,更新或删除,到达主节点时,它对数据的操作将被记录下来(经过一些必要的转换)
这些记录成为oplog
从节点通过在主节点上打开一个tailable游标不断获取新接入主节点的oplog,并在自己的数据上回放
以此保证跟主节点的数据一致

通过选举完成故障恢复

具有投票权的节点之间两两互相发送心跳;
当5次心跳未收到时判断为节点失联
如果失联的是主节点,从节点会发起选举,选出新的主节点
如果失联的是从节点则不会产生新的选举
选举基于RAFT一致性算法实现,选举成功的必要条件是大多数投票节点存活

MongoDB从入门到进阶_mongodb_14

影响选举的因素

整个集群必须有大多数节点存活,被选举为主节点的节点必须
能够与多数节点建立连接
具有较新的oplog
具有较高的优先级(如果有配置)

复制集注意事项

关于硬件
	因为正常的复制集节点都有可能成为主节点,它们的地位是一样的,因此硬件配置上必须一致。
  为了保证节点不会同时宕机,各节点使用的硬件必须具有独立性	
关于软件
  复制集各节点如那件版本必须一致,以便面出现不可预知的问题.
增加节点不会增加系统写性能!

Replcation Set 配置过程详解

接下来为了方便演示,我们使用mongo多实例来测试复制集

a.规划

三个以上的mongodb节点(或多实例)
多实例:
	(1)多个端口:28017,28018,28019,28020
  (2)多套目录:
  su - mongod
  mkdir -p /mongodb/28017/conf /mongodb/28017/data /mongodb/28017/log
  mkdir -p /mongodb/28018/conf /mongodb/28018/data /mongodb/28018/log
  mkdir -p /mongodb/28019/conf /mongodb/28019/data /mongodb/28019/log
  mkdir -p /mongodb/28020/conf /mongodb/28020/data /mongodb/28020/log
  (3)配置文件内容
cat > /mongodb/28017/conf/mongod.conf <<EOF
systemLog:
  destination: file
  path: /mongodb/28017/log/mongodb.log
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28017/data
  directoryPerDB: true
  # engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 0.5
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
processManagement:
  fork: true
net:
  port: 28017
  bindIp: 10.0.0.65,127.0.0.1
replication:
  oplogSizeMB: 2048
  replSetName: my_repl
EOF
\cp /mongodb/28017/conf/mongod.conf /mongodb/28018/conf/
\cp /mongodb/28017/conf/mongod.conf /mongodb/28019/conf/
\cp /mongodb/28017/conf/mongod.conf /mongodb/28020/conf/
sed 's#28017#28018#g' /mongodb/28018/conf/mongod.conf -i
sed 's#28017#28019#g' /mongodb/28019/conf/mongod.conf -i
sed 's#28017#28020#g' /mongodb/28020/conf/mongod.conf -i
(4)启动多个实例备用
mongod -f /mongodb/28017/conf/mongod.conf
mongod -f /mongodb/28018/conf/mongod.conf
mongod -f /mongodb/28019/conf/mongod.conf
mongod -f /mongodb/28020/conf/mongod.conf

b.配置复制集

(1)1主2从,从库普通从库(PSS)
config = {_id: 'my_repl', members: [
	{_id: 0, host: '10.0.0.65:28017'},
  {_id: 1, host: '10.0.0.65:28018'},
  {_id: 2, host: '10.0.0.65:28019'}
]}
rs.initiate(config)

(2)1主1从1个arbiter(PSA)
config = {_id: 'my_repl', members: [
	{_id: 0, host: '10.0.0.65:28017'},
  {_id: 1, host: '10.0.0.65:28018'},
  {_id: 2, host: '10.0.0.65:28019', "arbiterOnly": true}
]}
rs.initiate(config)

c.复制集测试

my_repl:PRIMARY> db.movies.insert([{"title": "Jaws", "year": 1975, "imdb_rating": 8.1}, 
... {"title": "Batman", "year": 1989, "imdb_rating": 7.6},
... ]);
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 2,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})

my_repl:PRIMARY> db.movies.find().pretty();
注: 在mongodb复制集当中,默认从库不允许读写。
my_repl:SECONDARY> rs.secondaryOk();
my_repl:SECONDARY> db.movies.find().pretty()

d.复制集管理操作

(1)查看复制集状态:
rs.status(); //查看整体复制集状态
rs.isMaster(); //查看当前是否是主节点

(2)添加删除节点
rs.add("ip:port"); //新增从节点
rs.addArb("ip:port"); //新增仲裁节点
rs.remove("ip:port"); //删除一个节点

(3)特殊从节点的配置
优先级(priority 参数:0-1000)
优先级越高的节点越优先成为主节点.
优先级为0的节点无法成为主节点
	隐藏(hidden 参数):复制数据,但对应用不可见.隐藏节点可以具有投票权,但优先级必须为0,
  延迟(slaveDelay 参数): 复制n秒之前的数据,保持与主节点的时间差.
  
配置延时节点(一般延时节点也配置hidden)
cfg=rs.conf()
cfg.members[1].priority=0
cfg.members[1].slaveDelay=120
cfg.members[1].hidden=true
rs.reconfig(cfg)

改回来:
cfg=rs.conf()
cfg.members[1].priority=1
cfg.members[1].slaveDelay=0
cfg.members[1].hidden=0
rs.reconfig(cfg)
配置成功后,通过以下命令查询配置后的属性
rs.conf();

e.副本集其他操作命令:

--查看副本集的配置信息
admin> rs.config()
--查看副本集各成员的状态
admin> rs.status()
--副本集角色切换(不要人为顺便操作,有风险)
admin> rs.stepDown()
注:
admin> rs.freeze(300) //锁定从,使其不会转变成主动
freeze()和stepDown单位都是秒

--设置副本节点可读:在副本节点上执行
admin> rs.secondaryOk();

--查看副本节点
admin>rs.printSlaveReplicationInfo()


MongoDB高级运维

MongoDB常见架构

MongoDB从入门到进阶_数据库_15

分片集群机制及原理

为什么使用分片集群

数据容量日益增大,访问性能日渐降低,怎么破?
新品上线异常火爆,如何支撑更多的并发用户?
单库已有10TB数据,恢复需要1-2天,如何加速?
地理分布数据

如何解决以上问题

原始结构
银行交易单表内10亿笔资料
超负荷运转

MongoDB从入门到进阶_mongodb_16

把数据分成两半

MongoDB从入门到进阶_数据库_17

把数据分成4部分

MongoDB从入门到进阶_mongodb_18


分片架构介绍

MongoDB从入门到进阶_数据库_19

Mongos 路由节点
提供集群单一入口
转发应用端请求
选择合适数据节点进行读写
合并多个数据节点的返回
无状态
建议至少2个

Config Servers配置节点
提供集群元数据存储
分片数据分布的映射
Shards 数据节点
以复制集为单位
横向扩展
最大1024分片
分片之间数据不重复
所有分片在一起才可完整工作

MongDB分片集群特点

应用全透明,无特殊处理
数据自动均衡
动态扩容,无须下线
提供三种分片方式

分片集群数据分布方式

基于范围
基于Hash
基于zone/tag
分片集群数据分布方式-基于范围

MongoDB从入门到进阶_mongodb_20

分片集群数据分布方式-基于哈希

MongoDB从入门到进阶_数据库_21

分片集群数据分布方式-自定义Zone


分片集群搭建及扩容

分片规划

10个实例: 38017-38026
(1)configserver:
3台构成的复制集(1主两从,不支持arbiter)38018-38020
(2)shard节点
sh1:38021-23  (1主两从,其中一个节点为arbiter,复制集名字sha1)
sh2:38024-26  (1主两从,其中一个节点为arbiter,复制集名字sha2)
(3)mongos
38017

配置过程

a.shard复制集配置:
1.创建:
mkdir -p /mongodb/38021/conf /mongodb/38021/log /mongodb/38021/data
mkdir -p /mongodb/38022/conf /mongodb/38022/log /mongodb/38022/data
mkdir -p /mongodb/38023/conf /mongodb/38023/log /mongodb/38023/data
mkdir -p /mongodb/38024/conf /mongodb/38024/log /mongodb/38024/data
mkdir -p /mongodb/38025/conf /mongodb/38025/log /mongodb/38025/data
mkdir -p /mongodb/38026/conf /mongodb/38026/log /mongodb/38026/data
2.配置文件:
sh1:
vim /mongodb/38021/conf/mongodb.conf
================
根据需求修改相应参数:
systemLog:
  destination: file
  path: /mongodb/38021/log/mongodb.log
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/38021/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 0.25
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 10.0.0.65,127.0.0.1
  port: 38021
replication:
  oplogSizeMB: 1024
  replSetName: sh1
sharding:
  clusterRole: shardsvr
processManagement:
  fork: true
  
=====================
cp /mongodb/38021/conf/mongodb.conf /mongodb/38022/conf
cp /mongodb/38021/conf/mongodb.conf /mongodb/38023/conf

sed 's#38021#38022#g' /mongodb/38022/conf/mongodb.conf -i
sed 's#38021#38023#g' /mongodb/38023/conf/mongodb.conf -i

sh2:
vim /mongodb/38024/conf/mongodb.conf
================
根据需求修改相应参数:
systemLog:
  destination: file
  path: /mongodb/38024/log/mongodb.log
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/38024/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 0.25
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 10.0.0.65,127.0.0.1
  port: 38024
replication:
  oplogSizeMB: 1024
  replSetName: sh2
sharding:
  clusterRole: shardsvr
processManagement:
  fork: true
  
=====================
cp /mongodb/38024/conf/mongodb.conf /mongodb/38025/conf
cp /mongodb/38024/conf/mongodb.conf /mongodb/38026/conf
sed 's#38024#38025#g' /mongodb/38025/conf/mongodb.conf -i
sed 's#38024#38026#g' /mongodb/38026/conf/mongodb.conf -i

3,所有节点,并搭建复制集:
mongod -f /mongodb/38021/conf/mongodb.conf
mongod -f /mongodb/38022/conf/mongodb.conf
mongod -f /mongodb/38023/conf/mongodb.conf
mongod -f /mongodb/38024/conf/mongodb.conf
mongod -f /mongodb/38025/conf/mongodb.conf
mongod -f /mongodb/38026/conf/mongodb.conf

mongo --port 38021
use admin
config = {_id: 'sh1', members: [
	{_id:0, host: '10.0.0.65:38021'},
  {_id:1, host: '10.0.0.65:38022'},
  {_id:2, host: '10.0.0.65:38023', "arbiterOnly": true}
]}
rs.initiate(config)

mongo --port 38024
use admin
config = {_id: 'sh2', members: [
	{_id:0, host: '10.0.0.65:38024'},
  {_id:1, host: '10.0.0.65:38025'},
  {_id:2, host: '10.0.0.65:38026', "arbiterOnly": true},
]}
rs.initiate(config)

==================================
b.config server节点配置

1.目录创建
mkdir -p /mongodb/38018/conf /mongodb/38018/log /mongodb/38018/data
mkdir -p /mongodb/38019/conf /mongodb/38019/log /mongodb/38019/data
mkdir -p /mongodb/38020/conf /mongodb/38020/log /mongodb/38020/data
2.修改配置文件:
vim /mongodb/38018/conf/mongodb.conf

systemLog:
  destination: file
  path: /mongodb/38018/log/mongodb.log
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/38018/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 0.25
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 10.0.0.65,127.0.0.1
  port: 38018
replication:
  oplogSizeMB: 1024
  replSetName: configReplSet
sharding:
  clusterRole: configsvr
processManagement:
  fork: true


cp /mongodb/38018/conf/mongodb.conf /mongodb/38019/conf
cp /mongodb/38018/conf/mongodb.conf /mongodb/38020/conf
sed 's#38018#38019#g' /mongodb/38019/conf/mongodb.conf -i
sed 's#38018#38020#g' /mongodb/38020/conf/mongodb.conf -i
3,启动节点,并配置复制集
mongod -f /mongodb/38018/conf/mongodb.conf
mongod -f /mongodb/38019/conf/mongodb.conf
mongod -f /mongodb/38020/conf/mongodb.conf

mongo --port 38018
use admin
config = {_id: 'configReplSet', members: [
		{_id: 0, host: '10.0.0.65:38018'},
    {_id: 1, host: '10.0.0.65:38019'},
    {_id: 2, host: '10.0.0.65:38020'}
]}
rs.initiate(config)

注:configserver 可以是一个节点,关键建议复制集.configserver不能有arbiter.
新版本中,要求必须是复制集.
注:mongodb 3.4之后,虽然要求config server为replica set,但不支持arbiter

===========================================
c. mongos节点配置
1.创建目录:
mkdir -p /mongodb/38017/conf /mongodb/38017/log
2,配置文件
vim /mongodb/38017/conf/mongos.conf
systemLog:
  destination: file
  path: /mongodb/38017/log/mongodb.log
  logAppend: true
net:
  bindIp: 10.0.0.65,127.0.0.1
  port: 38017
sharding:
  configDB: configReplSet/10.0.0.65:38018,10.0.0.65:38019,10.0.0.65:38020
processManagement:
  fork: true

3,启动mongos
mongos -f /mongodb/38017/conf/mongos.conf

d.分片集群操作:
连接到其中一个mongos,做以下配置
(1)连接到mongos的admin数据库
su - mongod
mongo 10.0.0.65:38017/admin

(2)添加分片
db.runCommand({addshard: 
"sh1/10.0.0.65:38021,10.0.0.65:38022,10.0.0.65:38023", name: "shard1"})
db.runCommand({addshard: "sh2/10.0.0.65:38024,10.0.0.65:38025,10.0.0.65:38026", name: "shard2"})

(3)列出分片
mongos> db.runCommand({listshards: 1})

(4)整体状态查看
mongos> sh.status();

==========================================
e.使用分片集群

## RANGE分片配置及测试
test库下的vast大表进行手动分片
1,激活数据库分片功能
mongo 10.0.0.65:38017/admin
admin> db.runCommand({enablesharding: "test"})

2,指定分片建对集合分片
use test
> db.vast.ensureIndex({id: 1})

--开启分片
use admin
> db.runCommand({shardcollection: "test.vast", key: {id: 1}})

3,集合分片验证
admin> use test
test> for(i=1;i<500000;i++){
db.vast.insert({"id":1, "name": "shengzheng", "age": 70, "date":new Date()});}
test> db.vast.stats()

4,分片结果测试
shard1:
mongo --port 38021
db.vast.count();

shard2:
mongo --port 38024
db.vast.count();

--------------------------------------------------------
f.Hash分片例子:
对oldpeng库下的vast大表进行hash
创建哈希索引
(1)对于oldpeng开启分片功能
mongo --port 38017 admin
use admin
admin> db.runCommand({enablesharding: "oldpeng"})
(2) 对于oldpeng库下的vast表建立hash索引
use oldpeng
oldpeng> db.vast.ensureIndex({id: "hashed"})
(3)开启分片
use admin
admin> sh.shardCollection("oldpeng.vast", {id: "hashed"})

(4)录入10w行数据测试
use oldpeng
for(i=1;i<100000;i++){db.vast.insert({"id": i, "name": "shengzheng", "age": 70, "date": new Date()});}

(5)hash分片结果测试
shard1:
mongo --port 38021
use oldpeng
db.vast.count();

shard2:
mongo --port 38024
use oldpeng
db.vast.count();
--------------------------------------------------
g.分片的管理
1,判断是否Shard集群
admin> db.runCommand({isdbgrid: 1})
2,列出所有分片信息
admin> db.runCommand({listshards: 1})
3,列出开启分片的数据库
admin>use config
config> db.database.find()
4,查看分片的片键
config>db.collections.find().pretty()
5,查看分片的详细信息
admin> db.printShardingStatus()
或
admin> sh.status()
6,删除分片节点(谨慎)
(1)确认blance是否在工作
sh.getBalancerState()
(2)删除shard2节点
mongos>db.runCommand({removeShard: "shard2"})
注意:删除操作一定会立即触发blancer.
7,balancer操作
介绍:
mongos的一个重要功能,自动巡查所有shard节点上的chunk的情况,自动做chunk迁移
什么时候工作?
自动运行,会检测系统不繁忙的时候做迁移
在做节点删除的时候,立即开始迁移工作
balancer只能在预设定的时间窗口内运行

有需要时可以关闭和开启balancer(备份的时候)
mongos> sh.stopBalancer()
mongos> sh.startBalancer()
8,自定义 自动平衡进行的时间段
use config
sh.setBalancerState(true)
db.settings.update({_id: "balancer"}, {$set: {activeWindow: {start: "3:00", stop: "5:00"}}}, true)
sh.getBalancerWindow()
sh.status()

企业中分片集群设计

分片的基本标准

关于数据:数据量不超过3TB,尽可能保持在2TB一个片;
关于索引:常用索引必须容纳进内存
按照以上标准初步确定分片后,还需要考虑业务压力,随着压力增大,cpu,ram,磁盘中任何一项出现瓶颈时,
都可以通过添加更多分片来解决.

如何粗略判断需要多少分片

条件

分片个数

B:工作集大小/单服务器内存容量

400GB/(256G*0.6) = 3

A:所需存储总量/单服务器可挂载容量

8TB/2TB=4

C:并发量总数/(单服务器并发量*0.7)

30000/(9000*0.7) = 6

D:额外开销

?

分片数量=max(A,B,C)+D

额外的考量

考虑分片的分布:
是否需要跨机房分布分片?
是否需要容灾?
高可用的要求如何?

选择片键的正确姿势

影响片键效率的主要因素
取值基数
取值分布
分散写,集中读
被尽可能多的业务场景用到
避免单调递增或递减的片键

选择基数大的片键

对于小基数的片键:
因为备选值有限,那么块的总数量就有限
随着数据增多,块的大小会越来越大
水平扩展时移动块会非常困难

例如:存储一个高中的师生数据,以年龄作为片键
15<=年龄<=65,且只为整数,所以最多只会有51个chunk
结论:取值基数要大!

选择分布均匀的片键

对于分布不均匀的片键:
造成某些块的数据量急剧增大
这些块压力随着增大
数据均衡以chunk为单位,所以系统无能为力

例如:存储一个高中的师生数据,以年龄作为片键
15<=年龄<=65,且只为整数
大部人的年龄范围为15~18岁
15,16,17,18四个chunk的数据量访问压力远大于其他chunk
结论:取值分布应尽可能均匀

1)首选关闭Balancer
mongos> sh.stopBalancer()

2)查询特大块
mongos>use config
mongos> db.chunks.find({jumbo:true})

3)拆分特大块
mongos>sh.splitAt({"db.collection", {shardkey: "拆分的临界值"}})

4)手动挪动块
mongos>sh.moveChunk("db.collection", {shardkey: "shardkey所在的块"}, "需要移动的目标分片ID")

5)重启Balancer
mongos>sh.startBalancer()

MongoDB备份与迁移及恢复

备份恢复工具介绍

(1) mongoexport/mongoimport
(2) mongodump/mongorestore

备份工具区别在那里?

mongoexport/mongoimport 导入/导出的是JSON格式或者CSV格式
mongodump/mongorestore 导入/导出的是BSON格式.

应用场景:
mongoexport/mongoimport: json csv
1,异构平台迁移 mysql <------> mongodb
2,同平台,跨大版本:mongodb 2 <--------> mongodb 3

mongodump/mongorestore
日常备份恢复时使用.

导出工具mongoexport

mongodb中的mongoexport工具可以把一个collection导出成JSON格式或CSV格式的文件
可以通过参数指定导出的数据项,也可以根据指定的条件导出数据
(1)版本差异较大
(2)异构平台数据迁移

mongoexport具体用法如下所示:
参数说明:
-h:指明数据库宿主机的ip
-u:指明数据库的用户名
-p:指明数据库的密码
-d:指明数据库的名字
-c:指明collection的名字
-f:指明要导出那些列
-o:指明要导出的文件名
-q:指明要导出数据的过滤条件
--authenticationDatabase admin

1,单表备份至json格式
mongoexport -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -c log -o /mongodb/log.json

注:备份文件名字可以自定义,默认导出JSON格式的数据
如果我们需要导出CSV格式的数据,则需要使用--type=csv参数

导入工具mongoimport

Mongodb中的mongoimport工具可以把一个特定格式文件中的内容导入到指定的collection中.该工具可以导入JSON格式数据
也可以导入CSV格式数据,具体使用如下所示:
参数说明:
-h:指明数据库宿主机的ip
-u:指明数据库的用户名
-p:指明数据库的密码
-d:指明数据库的名字
-c:指明collection的名字
-f:指明要导入那些列
-j, --numInsertionWorkers=<number>  number of insert operations to run concurrently (defaults to 1)

数据恢复:
1,恢复json格式表数据到log1
mongoimport -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -c log1 /mongodb/log.json

2,恢复csv格式文件到log2
注意
(1)csv格式的文件头行,有列名字
mongoimport -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -c log2 --type=csv --headerline --file /mongodb/log.csv

(2)csv格式的文件头行,没有列名字
mongoimport -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -c log3 -j 4  --type=csv -f id,name,age,data --file /mongodb/log.csv

--headerline:指明第一行是列名,不需要导入.

3,异构平台迁移案例(离线)
mysql ------->mongodb
world数据库下city表进行导出,导入到mongodb
(1)mysql开启安全路径
vim /etc/my.cnf
[mysqld]
secure-file-priv=/data/backup/

注意:一定要放在[mysqld]下面哈
--重启数据库生效
/etc/init.d/mysqld restart

(2)导出mysql的city表数据
select * from world.city into outfile 'tmp/t100w.csv' fields terminated by ',' 
ENCLOSED BY '"'

(3)获取列信息
select table_name, group_concat(column_name) from information_schema.columns where
table_schema='test' group by table_name order by null;

(4)在mongodb中导入备份
mongoimport -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -c city
--type=csv -f ID,Name,CountryCode,District,Population --file tmp/t100w.csv
use world
db.t100w.find({})

mongodump和mongorestore

a.介绍

mongodump能够在mongodb运行时进行备份,它的工作原理是对运行的mongodb做查询
然后将所有查到的文档写入磁盘,但是存在的问题是使用mongodump产生的备份不一定是数据库的实时快照
如果我们在备份时对数据进行了写入操作,则备份出来的文件可能不完全和mongodb实时数据相等
另外在备份时可能会对其他客户端性能产生不利的影响.

b.mongodump参数

mongodump 
参数说明:
-h:指明数据库宿主机的ip
-u:指明数据库的用户名
—p:指明数据库的密码
-d:指明数据库的名字
-c:指明collection的名字
-o:指明要导出的文件名
-q:指明导出数据的过滤条件
-j --numParallelCollections = number of collections to dump in parallel (4 by default)
--oplog 备份的时候备份oplog

c.mongodump和mongorestore基本使用

全库备份
mkdir /mongodb/backup -p
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -o /mongodb/backup/

--备份test库
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -o /mongodb/backup/

--备份test库下log集合
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -c log -o /mongodb/backup/

--压缩备份
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -o /mongodb/backup --gzip

--全备中恢复单库
mongorestore -uroot -proot123 --port 27017 --authenticationDatabase admin -d test /mongodb/backup/test --gzip

--全备中恢复单表
mongorestore -uroot -proot123 --port 27017 --authenticationDatabase admin -d a -c t1 /mongodb/backup/city.bson.gz   --gzip

--drop表示恢复的时候把之前的集合drop掉(危险操作)

mongdump和mongorestore高级企业应用(--oplog)

注意:这是relica set模式专用
--oplog

# oplog介绍
在replica set中oplog是一个定容集合,它的默认大小是磁盘空间的5%(可以通过--oplogSizeMB参数修改)

位于local库的db.oplog.rs,有兴趣可以看看里面到底有什么内容。
其中记录的是整个mongod实例一段时间内数据库的所有变更(插入/更新/删除)操作。
当空间用完时新记录自动覆盖最老的记录
其覆盖范围被称为oplog时间窗口.需要注意的是,因为oplog是一个定容集合,
所有时间窗口能覆盖的范围会因为你单位时间内的更新次数不同而变化。
想要查看当前的oplog时间窗口预计值,可以使用以下命令:
my_repl:PRIMARY> rs.printReplicationInfo()
configured oplog size:   2048MB    <-----集合大小
log length start to end: 113830secs (31.62hrs)  <-------预计窗口覆盖时间
oplog first event time:  Wed Aug 09 2023 09:50:31 GMT+0800 (CST)
oplog last event time:   Thu Aug 10 2023 17:27:41 GMT+0800 (CST)
now:                     Thu Aug 10 2023 17:27:48 GMT+0800 (CST)

oplog企业级应用
(1)实现热备,在备份时使用--oplog选项
注:为了演示效果我们在备份过程,模拟数据插入
(2)准备测试数据
use test
for(var i=1;i<100;i++){
	db.foo.insert({a: i});
}
my_repl:PRIMARY> db.oplog.rs.find({"op": "i"})

oplog配合mongodump实现热备
mongodump --port 28017 --oplog -o /mongodb/backup
作用介绍: --oplog会记录备份过程中的数据变化,会以oplog.bson保存下来

恢复
mongorestore --port 28017 --oplogReplay /mongodb/backup

!!!!!!!!oplog高级应用 ===========>binlog
背景:每天23点全备,oplog恢复窗口为48小时
周二,上午10点world.city业务表被误删除

恢复思路:
	1,停应用
  2,找测试库
  3,恢复昨天晚上全备
  4,截取全备之后到world.city误删除时间点的oplog,并恢复到测试库
  5,将误删除表导出,恢复到生产库

6,分片集群的备份思路
6.1,需要备份的数据
shard configserver

6.2痛点
chunk迁移,关闭或者调整balancer时间窗口
备份出来的数据时间不一致

6.3
a.使用Ops Manager商用工具

b.
balancer 关闭----->
同一时刻config, shard其中一个节点脱离集群
开始备份节点数据
把节点恢复到集群


MongoDB监控实践

常见的监控工具及手段

MongoDB Ops Manager商用工具
Percona
通用监控平台
程序脚本

如何获取监控数据

监控信息的来源
db.serverStatus()
rs.status()
sh.status()

serverStatus()主要信息

connections 关于连接数的信息
locks    关于MongoDB使用的锁情况
network  网络使用情况统计
opcounters crud的执行次数统计
repl    复制集配置信息
wiredTiger   包含大量WirdTiger 执行情况的信息

mem:  内存使用情况
metrics:   一系列性能指标统计信息

监控报警的考量

具备一定的容错机制以减少误报的发生
总结应用各指标峰值
适时调整报警阈值
留出足够的处理时间

建议监控指标说明

指标

功能

采集方法

opcounters(操作计数器)

查询,更新,插入,删除,getmore和其他命令的数量

db.serverStatus().opcounters

tickets(令牌)

对于WiredTiger存储引擎的读/写令牌数量,令牌数量表示了可以进入存储引擎的并发操作数量

db.serverStatus().wiredTiger.concurrentTransacations

replication lag(复制延迟)

这个指标代表了写操作到达节点所需要的最小时间,过高的replication lag会减少从节点的价值并且不利于配置了写关注w>1的那些操作

db.adminCommand({'replSetGetStatus': 1})

oplog window(复制时间窗)

这个指标代表oplog可以容纳多长时间的写操作,它表示了一个从节点可以离线多少时间仍能够追上主节点,通常建议该值应大于24小时为佳

db.oplog.rs.find()

connections(连接数)

连接数应作为监控指标的一部分,因为每个连接都将消耗资源,应该计算低峰/正常/高峰时间的连接数,并指定合理的报警阈值范围

db.serverStatus().connections

Query

targeting(查询专注度)

索引键/文档扫描数量比返回的文档数量,按秒平均,如果该值较高表示查询系需要进行很多低效的扫描来满足查询,这个情况通常代表了索引不当或缺少索引来支持查询

var status=db.serverStatus()


status.metrics.queryExecutor.scanned/status.metrics.document.returned


status.metrics.queryExecutor.scannedObjects/status.metrics.document.returned

Scan  and Order(扫描和排序)

每秒内内存排序操作所占的平均比例,内存排序可能会十分昂贵,因为她们通常要求缓冲大量数据,如果有适当索引的情况下,内存排序是可以避免的

var status=db.serverSstatus()


status.metrics.operation.scanAndOrder/status.opcounters.query

复制集节点状态

每个节点的运行状态,如果节点状态不是PRIMARY,SECONDARY,ARBITER中的一个,或无法执行上述命令则报警

db.runCommand("isMaster")

dataSIze(数据大小)

整个实例数据总量

每个DB执行db.stats()

StorageSize(磁盘空间大小)

已使用的磁盘空间占总空间的百分比

每个DB执行db.stats()


MongoDB索引管理

Index/Key/DataPage--索引/键/数据也页?

MongoDB从入门到进阶_mongodb_22


Covered Query

MongoDB从入门到进阶_mongodb_23

INDEXSCNA/COLLECTIONSCAN

MongoDB从入门到进阶_数据库_24

复合索引

db.hunman.createIndex({firstName:1, lastName:1, gender: 1, age: 1})

以上索引的全部前缀包括:

{firstName:1}

{firstName:1, lastName:1}

{firstName:1, lastName:1, gender:1}

所有索引前缀都可以被该索引覆盖,没必要针对这些查询建立额外的索引

selectivity--过滤性

在一个有10000条记录的集合中:
满足gender=F的记录有4000条
满足city=LA的记录有100条
满足ln=parker的记录有10条
查询条件:
ln=10 city=SZ gender=F
条件 ln 能过滤掉最多的数据,city
其次,gender最弱
所以ln的过滤性> city> gender

获取执行计划

MongoDB从入门到进阶_数据库_25

优化后的执行计划

MongoDB从入门到进阶_数据库_26

组合索引的最佳方式:ESR原则

组合索引的最佳方式:ESR原则
精确 匹配的字段放在最前面
排序  条件放中间
范围  匹配的字段放最后面

部分索引

MongoDB从入门到进阶_mongodb_27


MongoDB性能诊断

问题诊断工具-mongostat

MongoDB从入门到进阶_数据库_28

主要关注点:
dirty:
内存中的脏页数量百分比,默认是5%以下,每分钟在后台刷盘,超过5%后台进程频繁刷盘
超过20%会阻止新请求

used:
分配给mongodb的内存使用的百分比,低于80%,不会触发内存回收,超过80%,触发LRU回收内存
默认超过95%会阻止新请求

qrw:排队的请求数量,超过10以上,需要关注下积压的操作是什么(慢查询,锁等).

问题诊断工具-mongotop

MongoDB从入门到进阶_mongodb_29

total为总时间消耗 read读时间消耗  write写时间消耗

问题诊断-mongod慢日志

配置方法:
方法-:启动命令行
启动mongodb时记上--profile=级别

方法二:配置文件
operationProfiling:
mode: slowOp
slowOpThresholdMs: 10

方法三:在线配置
db.setProfilingLevel(1);
db.setProfilingLevel(1, 10);

上面profile的级别可以取0,1,2三个值,他们表示的意义如下:
0 - 不开启
1 - 记录慢命令(默认为>100ms)
2 - 记录所有命令

查询Profiling记录:
列出执行时间长于某一限度(100ms)的Profile记录:
db.system.profile.find({millis: {$gt: 100}})


MongoDB的生产上线

主要是看业务需要,对于关系型不强的大批量业务数据mongodb是一个很好选择,以我司为例

我司游戏上存放的数据都是类似于k,v数据对这种格式的数据,数据量算小的,只能达到GB级,这里我以阿里云云mongo选购举例

MongoDB从入门到进阶_数据库_30

容量GB级别的话,同时游戏数据也是比较重要的内容,那么选择副本集就完全够了,副本集中有单可用区的和多可用区的,像我司游戏对延迟性要求较高的选择单可用区副本集即可,如果是其他业务对性能延迟性要求不高但对数据安全性要求高的那么选择多可用区副本集即可,如果你的业务数据量达到PB级别的,那么就需要考虑选购分片集群了

第二就是成本问题了,同配置rds和mongodb预计购买价格比对

第三就是技术选型的问题了,如果老项目使用新的存储数据库,需要考虑数据迁移,业务代码修改问题,如果你只是小运维的话基本没有话语权的,成本太大别人不会考虑,不过在新项目上尝试还是可以

总之根据业务需要去选购,选择阿里云服务的话主要是稳定,技术成熟,免费支撑服务,节省人员管理成本