简介
(木有目录, 建议去gitee看: https://gitee.com/xiaofeipapa/docker_mongodb)
我司数据说多不多, 说少不少, 也到了搭个大数据系统的时候了. 更何况我们系统最重要的环节-风控系统, 本来就需要OLAP类的数据库对数据进行处理. 于是一把年纪的我也重新捡起这个苦力活, 开始了技术选型和框架搭建.
大数据时代的技术栈让人眼花缭乱. 我先是复习了一下hadoop(若干年前曾经浅尝辙止使用过), 觉得技术栈实在太过繁琐(纯粹个人意见), 出于对mongodb 更熟悉的原因, 我最终选择了mongodb 作为大数据的存储架构, 后续打算用 mongodb + spark 的技术栈.
作为公司产品的基石, 肯定要考虑高可用, 扩展性, 维护性这几个维度的平衡. 在先后搭建了分片副本集和仅副本集的集群模式之后, 我最终决定用副本集的模式. 因为:
- 我们的数据量没大到需要sharding的程度.
- 现阶段只要保证高可用, 数据不丢失即可.
- 未来数据增长了, 还可以再进行数据迁移的嘛.
以下内容总结了搭建副本集和测试的过程, 祝君阅读愉快.
(关于更多mongodb的副本集和分片副本集概念, 请自行查询网上资料)
搭建mongodb副本集
副本集模式的基础知识
副本集集群模式的概念图如下:
每个副本集是由多台机器组成的, 它的特点是:
- 主节点(Primary): 所有写入操作都在主节点上进行.
- 从节点(Secondary): 作为数据的备份, 和主节点数据完全一致. 默认状态下从节点不可读(可以设置成可读)
- 仲裁节点(Arbiter): 当主节点发生故障时, 判断选择哪个从节点成为新的主节点. 如果有多个从节点, 可以设置各个节点的优先级(priority), 仲裁节点会优先选择高优先级的节点.
- 整个过程自动故障转移.
- 整个过程数据自动恢复.
看上去很美, 要实际试试.
自定义网络
在docker里 创建自定义网络:
sudo docker network create --subnet=172.20.1.0/24 mongo_net --gateway 172.20.1.250
网关为 172.20.1.251 , 可用网段为 172.20.1.1 - 172.20.1.250 , 共250台. (取个整数)
docker规划和启动
名称 | 数据存储位置 | 主从/优先级 | ip | docker映射端口 |
---|---|---|---|---|
mongo_1 | ~/mongo-data/data01 | 主 | 172.20.1.1 | 30001 : 27017 |
mongo_2 | ~/mongo-data/data02 | 从 | 172.20.1.2 | 30002 : 27017 |
mongo_3 | ~/mongo-data/data03 | 仲裁节点 | 172.20.1.3 | 30003 : 27017 |
准备所需的文件夹和文件:
# 创建文件夹
mkdir -p ~/mongo-data/{data01,data02,data03,key,backup}
# 设置key文件. 此文件用于在集群机器间互相访问.
cd ~/mongo-data
openssl rand -base64 756 > key/mongo-rs.key
sudo chown 999 key/mongo-rs.key
# 不能是755, 权限太大不行.
sudo chmod 600 key/mongo-rs.key
创建并启动第一个mongo容器. 这个容器的几个关键信息是:
节点名称: mongo-1
用户: admin
密码: 123456
副本集名称: MongoSet
数据目录: ~/mongo-data/data01 (在之前创建)
映射端口: 30001
# 第一个
sudo docker run --name mongo-1 --network=mongo_net --ip=172.20.1.1 -p 30001:27017 -v ~/mongo-data/data01:/data/db -v ~/mongo-data/backup:/data/backup -v ~/mongo-data/key:/data/key -v /etc/localtime:/etc/localtime -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=123456 -d mongo:5.0.0 --replSet MongoSet --auth --keyFile /data/key/mongo-rs.key --bind_ip_all
MONGO_INITDB_ROOT_USERNAME 和 MONGO_INITDB_ROOT_PASSWORD 是 mongo 镜像的方便功能, 关于这个镜像的更多功能, 可以参考官方文档: https://hub.docker.com/_/mongo/
现在, 用 sudo docker ps 来查看容器状态, 应该能够看到容器已经启动了. 如果不能看到容器启动, 那么可以将上述命令的 -d 参数去掉, 运行的时候在窗口查找原因.
启动其他容器
# 第二个
sudo docker run --name mongo-2 --network=mongo_net --ip=172.20.1.2 -p 30002:27017 -v ~/mongo-data/data02:/data/db -v ~/mongo-data/backup:/data/backup -v ~/mongo-data/key:/data/key -v /etc/localtime:/etc/localtime -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=123456 -d mongo:5.0.0 --replSet MongoSet --auth --keyFile /data/key/mongo-rs.key --bind_ip_all
# 第三个
sudo docker run --name mongo-3 --network=mongo_net --ip=172.20.1.3 -p 30003:27017 -v ~/mongo-data/data03:/data/db -v ~/mongo-data/backup:/data/backup -v ~/mongo-data/key:/data/key -v /etc/localtime:/etc/localtime -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=123456 -d mongo:5.0.0 --replSet MongoSet --auth --keyFile /data/key/mongo-rs.key --bind_ip_all
现在你用 sudo docker ps , 应该能够看到这3个容器:
初始化副本集信息
登录第一个容器, 设置副本集的信息
sudo docker exec -it mongo-1 bash
mongo
use admin
# 密码 123456
db.auth("admin","123456")
# 设置
# MongoSet 是启动容器时候的副本集名字
var config={
_id:"MongoSet",
members:[
{_id:0,host:"172.20.1.1:27017"},
{_id:1,host:"172.20.1.2:27017"},
{_id:2,host:"172.20.1.3:27017",arbiterOnly:true}
]};
rs.initiate(config)
# 查看集群状态
rs.status()
# 设置完之后, 退出
exit
设置完之后退出, 重新登录容器:
sudo docker exec -it mongo-1 bash
mongo
这个时候可以看到, 这台容器显示的是副本集的主节点:
增加业务数据库和用户名
保持第一台容器的登录状态, 输入命令如下:
mongo
use admin
# 密码 123456
db.auth("admin","123456")
# 数据库不用创建, 使用use 即可
use ExampleDb
# 新增用户
db.createUser({
user: "test",
pwd: "123456",
roles: [ { role: "readWrite", db: "ExampleDb" } ]
})
设置从节点可读
默认的情况下, 主节点负责读写, 副节点仅起到备份作用, 不能读也不能写. 所以还要进行如下设置:
登录第2台容器, 依次进行:
sudo docker exec -it mongo-2 bash
mongo
use admin
# 密码 123456
db.auth("admin","123456")
# 注意:这条命令要在副节点上运行
# mongodb默认是从主节点读写数据,副本节点上不允许读,设置副本节点可读。
# 网上的文章基本都是 setSlaveOk, 这个方法已经是 deprecated 状态.
db.getMongo().setSecondaryOk()
设置了之后, 从节点会分担主节点读的压力, 提高了系统的性能.
图形化访问工具
mongodb 的图形化工具很多, 官方的mongo compass 就不错, 下载地址: https://www.mongodb.com/try/download/compass
下载安装这个工具之后, 在连接字符串输入:
mongodb://test:[email protected]:30001,127.0.0.1:30002,127.0.0.1:30003/ExampleDb?authSource=test&replicaSet=MongoSet
点击连接, 就可以看到漂漂亮亮的界面了.
至此, 副本集的搭建就算完成了.
测试副本集
接下来打算测试一下集群各类性能和可用能力, 先测试主从复制.
测试主从复制功能
这时候, 在第一台容器的mongo 控制台输入如下代码:
mongo
use admin
db.auth("admin","123456")
# test 使用test 数据库
use test
# 插入一条数据
db.users.insert({name:"jack",age:0,addr:"guangzhou",country:"China"})
登录第二台容器, 看看是否有这条记录:
use admin
db.auth("admin","123456")
use test
db.users.find()
此时你应该看到这条记录, 证明我们的主从复制功能已经成功了.
测试插入性能
用python写个简单的程序, 往集群插入 10 万条数据
#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
* 作者: 小肥爬爬
* : https://www.jianshu.com/u/db796a501972
* gitee: https://gitee.com/xiaofeipapa
* 邮箱: [email protected]
* 您可以自由转载此博客文章, 恳请保留原链接, 谢谢!
"""
from pymongo import MongoClient
import time
ip_list = [
'127.0.0.1:30001',
'127.0.0.1:30002',
'127.0.0.1:30003',
]
conn = MongoClient(ip_list)
ExampleDb = conn.ExampleDb
# 用业务数据库的用户名密码登录
user = 'test'
pwd = '123456'
ExampleDb.authenticate(user, pwd)
# 使用 TestUser 这个Collection
TestUser = ExampleDb.TestUser
# 测试插入10万数据的时间
def test_1():
time_start = time.clock() # 记录开始时间
count = 100000
# 使用批量插入功能
batch_list = []
for i in range(0, count):
data = {
'index': i,
'name': 'test-%d' % i
}
batch_list.append(data)
if len(batch_list) > 1000:
TestUser.insert_many(batch_list)
batch_list.clear()
# 防止还有值遗留
if len(batch_list) > 0:
TestUser.insert_many(batch_list)
batch_list.clear()
# -------------------------
time_end = time.clock() # 记录结束时间
time_sum = time_end - time_start # 计算的时间差为程序的执行时间,单位为秒/s
print('程序运行时间: %d 秒' % time_sum)
if __name__ == '__main__':
test_1()
程序运行几乎是一闪而过, 也可以看到"程序运行时间 0 秒", 证明速度非常快. 用compass 看看, 确实有数据了:
测试可用性
(为了观察方便, 在compass 里将之前的 TestUser 删掉.)
步骤:
- 停止主节点(第一个容器)
- 再次运行python程序
- compass 检查数据
停止第一个容器:
sudo docker stop mongo-1
然后再次运行之前的python代码. 可以观察到如下现象:
- 即使主节点关了, 代码还是顺利运行
- compass 能看到10万条数据.
这说明测试可用性也成功了.
如果此时你登录到第二台容器, 运行mongo 命令. 你会发现这台机器已经成了主节点. 这表示在第一台容器down掉之后, 第二台容器成功地挑起了重担.
测试数据丢失
(为了观察方便, 在compass 里将之前的 TestUser 删掉.)
先将第一台容器重新启动:
sudo docker restart mongo-1
(此时, 此容器已经成了从节点)
步骤:
- 用python插入 500 万数据
- 插入途中停止主节点容器
- 看看程序的反应如何?
- 5秒后, 重新启动"主节点"容器(重启之后, 它其实变成了从节点)
- 程序运行完之后, 检查 compass 到底插入了多少数据
- 分别进入第一个, 第二个容器, 看看他们的数据是否有差值?
我们期望的结果:
- 插入过程不能停. (这个已经验证过了)
- 看看数据是否有丢失?
(这些过程你自己可以试试)
我的测试结果
查询插入时间: 38 秒
compass 查询结果: 500万数据 (没有丢失!!)
主节点容器数据: 500万. (用 db.TestUser.count() 查总数)
从节点容器运行查询语句的时候报这个错:
这个错误的意思是从节点不可读. 在之前搭建集群的时候, 我们要在从节点这样设置:
db.getMongo().setSecondaryOk()
这表明, 当某个节点down掉再重连之后, 它会恢复成mongodb 的默认从节点配置, 没事, 再次运行这句就是了. 比起数据没有丢失, 这不算多大的事吧?
至此, mongodb集群的搭建, 测试就算彻底完成了.
附录
修改mongodb 密码
为了安装方便, 密码都使用 123456 这样的弱密码. 在安装好集群之后, 我们应该将它改掉.
我向你推荐 pwgen , 用这个小工具来生成密码:
sudo apt install pwgen
pwgen -s 20
登录主节点容器, 使用 db.changeUserPassword 来修改密码:
sudo docker exec -it mongo-1 bash
use admin
db.auth('admin', '123456')
# 修改密码, 假设你的是 xxxyyy
db.changeUserPassword('admin','xxxyyy')
防止日志文件过大
mongodb的日志膨胀非常快, 单个文件非常大, 可以通过这个命令来设置:
db.adminCommand({logRotate:1})
之后日志就会按日期分割成独立的文件. 一些过期的日志, 删了即可.
docker 便捷命令
查看正在运行的容器
sudo docker ps
查看创建没有启动的容器
sudo docker ps -a
一键停止所有容器
sudo docker ps -q |xargs sudo docker stop
删除全部容器
sudo docker ps -aq |xargs sudo docker rm
springboot 配置
spring:
data:
mongodb:
uri: mongodb://test:[email protected]:30001,127.0.0.1:30002,127.0.0.1:30003/ExampleDb?authSource=test&replicaSet=MongoSet
作者简介
艺名小肥爬爬(小肥耙耙/小肥巴巴), 一个喜欢阅读/写字/健身/踢球/吉他的程序员, 他和小田园犬肥花在深圳愉快地生活.
欢迎探讨技术领域的方方面面, 你可以在, gitee和知乎找到他:
: https://www.jianshu.com/u/db796a501972
gitee: https://gitee.com/xiaofeipapa
知乎: https://www.zhihu.com/people/chen-yun-shi-75
csdn(不常用): https://blog.csdn.net/m0_46322443
参考文章&感谢
https://blog.csdn.net/lzkIT/article/details/8146567
https://blog.csdn.net/zhanngle/article/details/105132527
https://www.cnblogs.com/zqyx/p/10169820.html