Replica Set – это набор/группа серверов, которые обслуживают один и тот же набор данных
В такой группе содержится один мастер(первичный) и несколько слейв(вторичный) серверов.
Также в Replica Set может использоваться арбитр. Это сервер, на котором нет базы данных, а его назначение – участвовать в голосовании при выборе нового мастера, создавать кворум необходимый для голосования.
Первичный/мастер сервер обслуживает все запросы на запись, а также записывает эти запросы в свой лог-файл(oplog).
Вторичные/слейв сервера реплицируют oplog первичного сервера и применяют операции к своим наборам данных. Репликация является асинхронной.
Если первичный сервер не общается с другими членами реплики в течение времени ,определенного параметром electionTimeoutMillis (по умолчанию 10 секунд), то в результате голосования выбирается новый первичный сервер из состава текущих вторичных серверов.
Replica set не может обслуживать запросы на запись, до тех пор, пока выборы и назначение нового мастера не закончатся успешно.
С настройками по умолчанию среднее время на выбор нового мастера составляет 12 секунд
Это включает обнаружение недоступности мастера(10 секунд по умолчанию) и выбор нового мастера.
При необходимости можно изменить значение параметра electionTimeoutMillis.
По умолчанию запросы на чтения от клиентов обрабатываются только мастером, но можно настроить, чтобы запросы на чтение посылались и обрабатывались вторичными серверами.
Т.к. используется асинхронная репликация между первичным и вторичными серверами, то вторичные сервера могут отдавать клиентам устаревшие данные по сравнению с теми данными, которые содержатся на мастере в данный момент
В схеме один мастер(первичный), один слейв (вторичный) и один арбитр
Master db01.mydomain.com 192.168.100.1
Slave db02.mydomain.com 192.168.100.2
Арбитр advisor.mydomain.com 192.168.100.3
Настройка мастера db01
1 |
# cat /etc/hosts |
1 2 3 |
192.168.100.1 db01.mydomain.com db01 192.168.100.2 db02.mydomain.com db02 192.168.100.3 advisor.mydomain.com advisor |
Имя реплики — rs0
1 |
replSetName: rs0 |
Настройка Mongodb на прослушивание запросов на всех интерфейсах
1 |
bindIp: 0.0.0.0 |
Конфигурационный файл mongodb на сервере db01
1 |
# grep -Ev '^#|^$' /etc/mongod.conf |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
storage: dbPath: /var/lib/mongodb journal: enabled: true systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log net: port: 27017 bindIp: 0.0.0.0 processManagement: timeZoneInfo: /usr/share/zoneinfo replication: replSetName: rs0 |
Настройка слейв db02
1 |
# cat /etc/hosts |
1 2 3 |
192.168.100.2 db02.mydomain.com db02 192.168.100.1 db01.mydomain.com db01 192.168.100.3 advisor.mydomain.com advisor |
1 |
# grep -Ev '^#|^$' /etc/mongod.conf |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
storage: dbPath: /var/lib/mongodb journal: enabled: true systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log net: port: 27017 bindIp: 0.0.0.0 processManagement: timeZoneInfo: /usr/share/zoneinfo replication: replSetName: rs0 |
Настройка арбитра
1 |
# cat /etc/hosts |
1 2 3 |
192.168.100.3 advisor.mydomain.com advisor 192.168.100.2 db02.mydomain.com db02 192.168.100.1 db01.mydomain.com db01 |
1 |
# grep -Ev '^#|^$' /etc/mongod.conf |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
storage: dbPath: /var/lib/mongodb journal: enabled: true systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log net: port: 27017 bindIp: 0.0.0.0 processManagement: timeZoneInfo: /usr/share/zoneinfo replication: replSetName: rs0 |
Запускаем MongoDB и добавляем в автозагрузку на всех серверах
1 |
# systemctl start mongod |
1 |
# systemctl status mongod |
На мастере db01 выполняем
1.Инициируем создание реплики
1 |
rs.initiate() |
2.Добавляем Slave-сервер
1 |
rs.add("db02") |
3.Проверяем конфигурацию Replica Set
1 |
rs0:PRIMARY> rs.conf() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
{ "_id" : "rs0", "version" : 2, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "db01:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "db02:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("5b01da6631d1454bc1a46596") } } rs0:PRIMARY> |
4.Проверяем статус Replica Set
1 |
rs0:PRIMARY> rs.status() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
{ "set" : "rs0", "date" : ISODate("2018-05-21T20:45:22.166Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1526935517, 1), "t" : NumberLong(1) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1526935517, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1526935517, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1526935517, 1), "t" : NumberLong(1) } }, "members" : [ { "_id" : 0, "name" : "db01:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 87457, "optime" : { "ts" : Timestamp(1526935517, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2018-05-21T20:45:17Z"), "electionTime" : Timestamp(1526848104, 1), "electionDate" : ISODate("2018-05-20T20:28:24Z"), "configVersion" : 2, "self" : true }, { "_id" : 1, "name" : "db02:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 87160, "optime" : { "ts" : Timestamp(1526935517, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1526935517, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2018-05-21T20:45:17Z"), "optimeDurableDate" : ISODate("2018-05-21T20:45:17Z"), "lastHeartbeat" : ISODate("2018-05-21T20:45:21.253Z"), "lastHeartbeatRecv" : ISODate("2018-05-21T20:45:20.263Z"), "pingMs" : NumberLong(0), "syncingTo" : "db01:27017", "configVersion" : 2 } ], "ok" : 1, "operationTime" : Timestamp(1526935517, 1), "$clusterTime" : { "clusterTime" : Timestamp(1526935517, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs0:PRIMARY> |
5.Добавляем арбитра
1 |
rs0:PRIMARY> rs.addArb("advisor:27017") |
1 2 3 4 5 6 7 8 9 10 11 |
{ "ok" : 1, "operationTime" : Timestamp(1526935810, 1), "$clusterTime" : { "clusterTime" : Timestamp(1526935810, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } |
6. Проверяем конфигурацию Replica Set
1 |
rs0:PRIMARY> rs.conf() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
{ "_id" : "rs0", "version" : 3, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "db01:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "db02:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "advisor:27017", "arbiterOnly" : true, "buildIndexes" : true, "hidden" : false, "priority" : 0, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("5b01da6631d1454bc1a46596") } } rs0:PRIMARY> |
Начиная с версии MongoDB 3.6 арбитр всегда имеет приоритет 0
7. Проверяем статус Replica Set
Выполняем команду на мастере db01
1 |
rs0:PRIMARY> rs.status() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
{ "set" : "rs0", "date" : ISODate("2018-05-21T20:54:11.786Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1526936047, 1), "t" : NumberLong(1) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1526936047, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1526936047, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1526936047, 1), "t" : NumberLong(1) } }, "members" : [ { "_id" : 0, "name" : "db01:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 87986, "optime" : { "ts" : Timestamp(1526936047, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2018-05-21T20:54:07Z"), "electionTime" : Timestamp(1526848104, 1), "electionDate" : ISODate("2018-05-20T20:28:24Z"), "configVersion" : 3, "self" : true }, { "_id" : 1, "name" : "db02:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 87690, "optime" : { "ts" : Timestamp(1526936047, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1526936047, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2018-05-21T20:54:07Z"), "optimeDurableDate" : ISODate("2018-05-21T20:54:07Z"), "lastHeartbeat" : ISODate("2018-05-21T20:54:11.096Z"), "lastHeartbeatRecv" : ISODate("2018-05-21T20:54:11.097Z"), "pingMs" : NumberLong(0), "syncingTo" : "db01:27017", "configVersion" : 3 }, { "_id" : 2, "name" : "advisor:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 240, "lastHeartbeat" : ISODate("2018-05-21T20:54:11.092Z"), "lastHeartbeatRecv" : ISODate("2018-05-21T20:54:11.185Z"), "pingMs" : NumberLong(0), "configVersion" : 3 } ], "ok" : 1, "operationTime" : Timestamp(1526936047, 1), "$clusterTime" : { "clusterTime" : Timestamp(1526936047, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs0:PRIMARY> |
Тестируем репликацию
На mongo Master вставляем тестовые данные в тестовую базу
1 |
rs0:PRIMARY> db.test_db.insert({id:1, value:'test'}) |
1 |
WriteResult({ "nInserted" : 1 }) |
1 |
rs0:PRIMARY> db.test_db.findOne() |
1 |
{ "_id" : ObjectId("5b222a8f23ac3277a8639d82"), "id" : 1, "value" : "test" } |
На Mongo Slave(Secondary) проверяем
1 |
rs0:SECONDARY> db.test_db.findOne() |
1 |
2018-06-14T09:45:35.889+0100 E QUERY [thread1] Error: error: { "operationTime" : Timestamp(1528965929, 1), "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotMasterNoSlaveOk", ….} |
По умолчанию запросы на чтение не разрешены на Secondary(для избежания проблем с получением клиентами устаревших данных)
Разрешим Secondary-реплике обрабатывать запросы на чтение.
Это разрешение будет действовать до окончания текущей mongo-shell-сессии(т.е. после отключения/выхода с mongo-сеанса значение снова станет дефолтным — запрещающим)
1 |
rs0:SECONDARY> rs.slaveOk(true) |
1 |
rs0:SECONDARY> db.test_db.findOne() |
1 |
{ "_id" : ObjectId("5b222a8f23ac3277a8639d82"), "id" : 1, "value" : "test" } |
Останавливаем mongodb на мастере db01 и проверяем статус Replica Set на db02-сервере, который автоматически стал мастером(Primary)
1 |
rs0:SECONDARY> rs.status() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
{ "set" : "rs0", "date" : ISODate("2018-05-21T21:12:02.946Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1526937087, 1), "t" : NumberLong(1) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1526937087, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1526937115, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1526937115, 1), "t" : NumberLong(2) } }, "members" : [ { "_id" : 0, "name" : "db01:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDurable" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2018-05-21T21:12:02.747Z"), "lastHeartbeatRecv" : ISODate("2018-05-21T21:11:33.494Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "Connection refused", "configVersion" : -1 }, { "_id" : 1, "name" : "db02:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 88948, "optime" : { "ts" : Timestamp(1526937115, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2018-05-21T21:11:55Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1526937104, 1), "electionDate" : ISODate("2018-05-21T21:11:44Z"), "configVersion" : 3, "self" : true }, { "_id" : 2, "name" : "advisor:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 1311, "lastHeartbeat" : ISODate("2018-05-21T21:12:02.735Z"), "lastHeartbeatRecv" : ISODate("2018-05-21T21:12:01.512Z"), "pingMs" : NumberLong(0), "configVersion" : 3 } ], "ok" : 1, "operationTime" : Timestamp(1526937115, 1), "$clusterTime" : { "clusterTime" : Timestamp(1526937115, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs0:PRIMARY> |
В логах db02-сервера
1 2 3 |
2018-05-21T22:14:04.871+0100 I ASIO [NetworkInterfaceASIO-Replication-0] Failed to connect to db01:27017 - HostUnreachable: Connection refused 2018-05-21T22:14:04.871+0100 I ASIO [NetworkInterfaceASIO-Replication-0] Dropping all pooled connections to db01:27017 due to failed operation on a connection 2018-05-21T22:14:04.871+0100 I REPL_HB [replexec-16] Error in heartbeat (requestId: 209206) to db01:27017, response status: HostUnreachable: Connection refused |
После запуска mongod на db01
Нода db01 становится слейвом(Secondary) проходя через такие состояния
1 |
Startup->Startup2>Recovering->Secondary |
Синхронизируясь/реплицируя данные с текущего мастера db02-сервера
Содержание лога ноды db01 в этот момент
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
2018-05-21T22:16:34.169+0100 I REPL [initandlisten] No oplog entries to apply for recovery. appliedThrough and checkpointTimestamp are both null. 2018-05-21T22:16:34.184+0100 I NETWORK [initandlisten] waiting for connections on port 27017 2018-05-21T22:16:34.186+0100 I REPL [replexec-0] New replica set config in use: { _id: "rs0", version: 3, protocolVersion: 1, members: [ { _id: 0, host: "db01:27017 ", arbiterOnly: false, buildIndexes: true, hidden: false, priority: 1.0, tags: {}, slaveDelay: 0, votes: 1 }, { _id: 1, host: "db02:27017", arbiterOnly: false, buildInd exes: true, hidden: false, priority: 1.0, tags: {}, slaveDelay: 0, votes: 1 }, { _id: 2, host: "advisor:27017", arbiterOnly: true, buildIndexes: true, hidden: false, pr iority: 0.0, tags: {}, slaveDelay: 0, votes: 1 } ], settings: { chainingAllowed: true, heartbeatIntervalMillis: 2000, heartbeatTimeoutSecs: 10, electionTimeoutMillis: 1 0000, catchUpTimeoutMillis: -1, catchUpTakeoverDelayMillis: 30000, getLastErrorModes: {}, getLastErrorDefaults: { w: 1, wtimeout: 0 }, replicaSetId: ObjectId('5b01da663 1d1454bc1a46596') } } 2018-05-21T22:16:34.186+0100 I REPL [replexec-0] This node is db01:27017 in the config 2018-05-21T22:16:34.186+0100 I REPL [replexec-0] transition to STARTUP2 from STARTUP 2018-05-21T22:16:34.186+0100 I REPL [replexec-0] Starting replication storage threads 2018-05-21T22:16:34.186+0100 I ASIO [NetworkInterfaceASIO-Replication-0] Connecting to db02:27017 2018-05-21T22:16:34.186+0100 I ASIO [NetworkInterfaceASIO-Replication-0] Connecting to advisor:27017 2018-05-21T22:16:34.187+0100 I REPL [replexec-0] transition to RECOVERING from STARTUP2 2018-05-21T22:16:34.187+0100 I REPL [replexec-0] Starting replication fetcher thread 2018-05-21T22:16:34.187+0100 I REPL [replexec-0] Starting replication applier thread 2018-05-21T22:16:34.187+0100 I REPL [replexec-0] Starting replication reporter thread 2018-05-21T22:16:34.187+0100 I ASIO [NetworkInterfaceASIO-Replication-0] Successfully connected to db02:27017, took 1ms (1 connections now open to db02:27017) 2018-05-21T22:16:34.187+0100 I REPL [rsSync] transition to SECONDARY from RECOVERING 2018-05-21T22:16:34.187+0100 I ASIO [NetworkInterfaceASIO-Replication-0] Successfully connected to advisor:27017, took 1ms (1 connections now open to advisor:27017) 2018-05-21T22:16:34.188+0100 I REPL [replexec-2] Member db02:27017 is now in state PRIMARY 2018-05-21T22:16:34.188+0100 I REPL [replexec-1] Member advisor:27017 is now in state ARBITER 2018-05-21T22:16:35.027+0100 I NETWORK [listener] connection accepted from 192.168.100.2:36948 #1 (1 connection now open) 2018-05-21T22:16:35.028+0100 I NETWORK [conn1] received client metadata from 192.168.100.2:36948 conn1: { driver: { name: "NetworkInterfaceASIO-Replication", version: "3.6.4" }, os: { type: "Linux", name: "Ubuntu", architecture: "x86_64", version: "16.04" } } 2018-05-21T22:16:35.187+0100 I REPL [rsBackgroundSync] sync source candidate: db02:27017 2018-05-21T22:16:35.187+0100 I ASIO [NetworkInterfaceASIO-RS-0] Connecting to db02:27017 2018-05-21T22:16:35.189+0100 I ASIO [NetworkInterfaceASIO-RS-0] Successfully connected to db02:27017, took 2ms (1 connections now open to db02:27017) |
Проверка состояния Replica Set с выполнением команды на слейве – сервере db01
1 |
rs0:SECONDARY> rs.status() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
{ "set" : "rs0", "date" : ISODate("2018-05-21T21:18:57.149Z"), "myState" : 2, "term" : NumberLong(2), "syncingTo" : "db02:27017", "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1526937535, 1), "t" : NumberLong(2) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1526937535, 1), "t" : NumberLong(2) }, "appliedOpTime" : { "ts" : Timestamp(1526937535, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1526937535, 1), "t" : NumberLong(2) } }, "members" : [ { "_id" : 0, "name" : "db01:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 145, "optime" : { "ts" : Timestamp(1526937535, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2018-05-21T21:18:55Z"), "syncingTo" : "db02:27017", "configVersion" : 3, "self" : true }, { "_id" : 1, "name" : "db02:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 142, "optime" : { "ts" : Timestamp(1526937525, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1526937525, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2018-05-21T21:18:45Z"), "optimeDurableDate" : ISODate("2018-05-21T21:18:45Z"), "lastHeartbeat" : ISODate("2018-05-21T21:18:55.244Z"), "lastHeartbeatRecv" : ISODate("2018-05-21T21:18:57.082Z"), "pingMs" : NumberLong(0), "electionTime" : Timestamp(1526937104, 1), "electionDate" : ISODate("2018-05-21T21:11:44Z"), "configVersion" : 3 }, { "_id" : 2, "name" : "advisor:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 142, "lastHeartbeat" : ISODate("2018-05-21T21:18:55.243Z"), "lastHeartbeatRecv" : ISODate("2018-05-21T21:18:55.851Z"), "pingMs" : NumberLong(0), "configVersion" : 3 } ], "ok" : 1, "operationTime" : Timestamp(1526937535, 1), "$clusterTime" : { "clusterTime" : Timestamp(1526937535, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs0:SECONDARY> |
Проверка отставания репликации на Slave(db01)
1 |
rs0:SECONDARY> rs.printSlaveReplicationInfo() |
1 2 3 |
source: db02:27017 syncedTo: Mon May 21 2018 21:23:45 GMT+0100 (BST) 0 secs (0 hrs) behind the primary |
Обновление MongoDB на нодах,которые находятся в Replica Set
Обновление Secondary-нод
1.На одной из Secondary-нод останавливаем mongodb-службу
1 |
use admin |
1 |
db.shutdownServer() |
2. Обновляем пакеты Mongodb
1 |
# apt-get update && apt-get upgrade |
3.Запускаем mongodb-службу
1 |
# systemctl start mongod |
Провеярем корректность запуска
1 |
# systemctl status mongod |
4. На Primary-ноде проверяем,что обновленная Secondary-нода добавлена в Replica Set
1 |
rs.status() |
Аналогично обновляем все остальные Secondary-ноды
Обновление Primary Node
На Primary-ноде выполняем
1.Принудительный перевод Primary-ноды в Secondary
1 |
rs.stepDown() |
2.Проверяем,что была выбрана новая Primary-нода и все Secondary-ноды сейчас реплицируются с новой Primary-нодой.
Дождаться окончания процесса синхронизации всех Secondary-нод с новой Primary-нодой
1 |
rs.status() |
3.Выполняем на бывшей Primary-ноде(сейчас она уже Secondary-нода) те же команды,что выполнялись для обновления Secondary-нод
4. На Primary-ноде проверяем,что обновленная Secondary-нода добавлена в Replica Set
1 |
rs.status() |
Таким образом все ноды в Replica Set были обновлены
Полезные команды Репликация/Replica Set в MongoDB
1 |
> rs.[TAB] — для просмотра существующих команд по реликации |
Просмотр конфигурации репликации
1 |
rs.conf() |
Просмотр состояния репликации
1 |
> rs.status() |
Просмотр членов репликации
1 |
> rs.status()['members'] |
Keep an eye on the configVersion key of each member. Every
change in the replica set’s configuration increments the value of
configVersion by one. This can be handy for a members’s current
configuration state.
Проверка,выступает ли нода мастером репликации
1 |
> rs.isMaster()['ismaster'] |
Принудительный перевод роли мастера между нодами, например, перевести роль мастера с 1-й ноды на 3-ю ноду
Например, есть 3 ноды
1 2 3 |
1-нода-primary 2-нода-secondary 3-нода-secondary |
Алгоритм работы
1. На второй ноде(secondary) выполняем команду, которая вынуждает эту ноду
принудительно оставаться в статусе secondary и не принимать участие в выборе нового мастера в течение 120 секунд
1 |
rs.freeze(120) |
2. На текущем мастере(нода 1) выполняем команду, переводя ее принудительно в статус secondary
1 |
rs.stepDown() |
Проверем,что нода-1 стала сейчас secondary
1 |
rs.isMaster()['ismaster'] |
3.Проверить на ноде-3,что она стала primary
1 |
rs.isMaster()['ismaster'] |
Изменение параметров репликации(все команды должны выполнятся на текущем мастере)
Сохраняем состояние репликации в переменную cfg
1 |
rs0:PRIMARY>cfg = rs.conf() |
Изменяем интересующий нас параметр
1 |
rs0:PRIMARY>cfg.settings.heartbeatTimeoutSecs=4 |
Загружаем параметры репликации из переменной cfg
1 |
rs0:PRIMARY>rs.reconfig(cfg) |
Проверяем состояние репликации
1 |
rs0:PRIMARY>rs.conf() |
Как работает репликация MongoDB?
Primary записывает все свои операции(только по добавлению/удалению/изменению) в Oplog,который представляет собой коллекцию(таблицу) в базе данных с именем local и эта коллекция реплицируется на все Secondary-ноды, и применяется на этих Secondary-нодах
Отчет о состоянии репликации с точки зрения Primary-сервера
1 |
rs0:PRIMARY> rs.printReplicationInfo() |
1 2 3 4 5 |
configured oplog size: 990MB log length start to end: 4047058secs (1124.18hrs) oplog first event time: Fri Jul 06 2018 22:37:08 GMT+0100 (BST) oplog last event time: Wed Aug 22 2018 18:48:06 GMT+0100 (BST) now: Wed Aug 22 2018 18:48:15 GMT+0100 (BST) |
Отчет о состоянии репликации с точки зрения Secondary-сервера
1 |
rs0:SECONDARY> rs.printSlaveReplicationInfo() |
1 2 3 |
source: db01:27017 syncedTo: Wed Aug 22 2018 18:48:06 GMT+0100 (BST) 0 secs (0 hrs) behind the primary |
Как определяется отставание/ между первичной и вторичной нодами?
MongoDВ знает временную метку последней записи в oplog на мастере
1 |
oplog last event time: Wed Aug 22 2018 18:48:06 GMT+0100 (BST) |
и сверяет ее с временной меткой oplog на вторичном сервере
1 |
syncedTo: Wed Aug 22 2018 18:48:06 GMT+0100 (BST) |
Источник:
https://docs.mongodb.com/manual/replication
https://www.linode.com/docs/databases/mongodb/create-a-mongodb-replica-set
http://www.linux-admins.net/2016/05/deploying-mongodb.html