簡介
(簡書木有目錄, 建議去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:123456@127.0.0.1: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 -*-
"""
* 作者: 小肥爬爬
* 簡書: http://www.lxweimin.com/u/db796a501972
* gitee: https://gitee.com/xiaofeipapa
* 郵箱: imyunshi@163.com
* 您可以自由轉載此博客文章, 懇請保留原鏈接, 謝謝!
"""
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:123456@127.0.0.1:30001,127.0.0.1:30002,127.0.0.1:30003/ExampleDb?authSource=test&replicaSet=MongoSet
作者簡介
藝名小肥爬爬(小肥耙耙/小肥巴巴), 一個喜歡閱讀/寫字/健身/踢球/吉他的程序員, 他和小田園犬肥花在深圳愉快地生活.
歡迎探討技術領域的方方面面, 你可以在簡書, gitee和知乎找到他:
簡書: http://www.lxweimin.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