Репликацията в MongoDB включва набори от реплики от членове с архитектура на първичен и вторичен членове, но понякога с член, който не носи данни, наречен арбитър. Процесът на репликация е, че когато данните са записани в първичния възел, промените се записват в oplog файл, от който вторичните членове прилагат същите промени. Операциите за четене могат да бъдат направени от всеки член, носещ данни, като по този начин се създава сценарий, известен като висока наличност.
В някои случаи обаче вторичните членове може да не успеят да настигнат първичния при извършване на промени и в случай, че основният възел не успее, преди тези промени да бъдат приложени, човек ще бъде принуден да синхронизира повторно целия клъстер така че да могат да бъдат в едно и също състояние на данните.
Какво е връщане назад?
Това е функция за автоматично превключване при отказ в MongoDB, при която основният възел в набор от реплика може да се провали, докато прави промени, които за съжаление не се отразяват на вторичните членове навреме от oplog, следователно трябва да върнете състояние на основното към едно преди да бъдат направени промените.
Следователно отмяната е необходима само когато основният е приел да запише операциите, които не са били репликирани на вторичните членове, преди първичните да отстъпят поради някаква причина, като например мрежов дял. Ако в случай, че операциите на запис успеят да бъдат репликирани в един от членовете, който е наличен и достъпен за по-голямата част от набора от реплики, връщане няма да се случи.
Основната причина за връщането назад в MongoDB е да се запази последователността на данните за всички членове и следователно, когато основният се присъедини отново към набора от реплика, ако промените му не са били приложени към вторичните членове, той ще бъде върнат до състоянието преди повредата.
Въпреки това, връщането трябва да бъде рядко или по-скоро да се избягва в MongoDB, тъй като може да доведе до голяма загуба на данни и следователно да повлияе на работата на свързани приложения към базата данни.
Процес за връщане на MongoDB
Нека разгледаме набор от три члена реплика с A като основен, B и C като вторични членове. Ще попълваме данни в A и в същото време ще задействаме мрежово разделяне на B и C. Ще използваме MongoDB версия 4.2 и Atlas в този тест.
Първо ще получим състоянието на зададената реплика, като изпълним командата rs.status() в mongo shell
MongoDB Enterprise Cluster0-shard-0:PRIMARY> rs.status()
Разглеждайки атрибута members, можете да видите нещо като
"members" : [
{
"_id" : 0,
"name" : "cluster0-shard-00-00-sc27x.mongodb.net:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 1891079,
"optime" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDurable" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDate" : ISODate("2020-07-15T15:25:11Z"),
"optimeDurableDate" : ISODate("2020-07-15T15:25:11Z"),
"lastHeartbeat" : ISODate("2020-07-15T15:25:19.509Z"),
"lastHeartbeatRecv" : ISODate("2020-07-15T15:25:18.532Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"syncSourceHost" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"syncSourceId" : 2,
"infoMessage" : "",
"configVersion" : 4
},
{
"_id" : 1,
"name" : "cluster0-shard-00-01-sc27x.mongodb.net:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 1891055,
"optime" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDurable" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDate" : ISODate("2020-07-15T15:25:11Z"),
"optimeDurableDate" : ISODate("2020-07-15T15:25:11Z"),
"lastHeartbeat" : ISODate("2020-07-15T15:25:17.914Z"),
"lastHeartbeatRecv" : ISODate("2020-07-15T15:25:19.403Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"syncSourceHost" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"syncSourceId" : 2,
"infoMessage" : "",
"configVersion" : 4
},
{
"_id" : 2,
"name" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1891089,
"optime" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDate" : ISODate("2020-07-15T15:25:11Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1592935644, 1),
"electionDate" : ISODate("2020-06-23T18:07:24Z"),
"configVersion" : 4,
"self" : true,
"lastHeartbeatMessage" : ""
}
],
Това ще ви покаже състоянието на всеки член от вашия набор от реплика. Сега отворихме нов терминал за възел А и го попълнихме с 20000 записа:
MongoDB Enterprise Cluster0-shard-0:PRIMARY> for (var y = 20000; y >= 0; y--) {
db.mytest.insert( { record : y } )
}
WriteResult({ "nInserted" : 1 })
MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.mytest 2020-07-15T21:28:40.436+2128 I NETWORK [thread1] trying reconnect to 127.0.0.1:3001 (127.0.0.1) failed
2020-07-15T21:28:41.436+2128 I
NETWORK [thread1] reconnect 127.0.0.1:3001 (127.0.0.1) ok
MongoDB Enterprise Cluster0-shard-0:SECONDARY> rs.slaveOk()
MongoDB Enterprise Cluster0-shard-0:SECONDARY> db.mytest.count()
20000
По време на мрежовото разделяне, A ще бъде изключен, което го прави недостъпен за B и C и следователно B ще бъде избран като основен в нашия случай. Когато A се присъедини отново, той ще бъде добавен като вторичен и можете да проверите това с помощта на командата rs.status(). Въпреки това, някои записи успяха да бъдат репликирани на член B преди разделянето на мрежата, както се вижда по-долу:(Не забравяйте, че в този случай B е основният сега)
MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.mytest.find({}).count()
12480
Числото е броят на документите, които са били в състояние да бъдат репликирани в B, преди A да падне.
Ако запишем някои данни в B и позволим на A да се присъедини към мрежата, тогава можем да забележим някои промени в A
connecting to: 127.0.0.1:3001/admin
MongoDB Enterprise Cluster0-shard-0:ROLLBACK>
MongoDB Enterprise Cluster0-shard-0:RECOVERING>
MongoDB Enterprise Cluster0-shard-0:SECONDARY>
MongoDB Enterprise Cluster0-shard-0:SECONDARY>
MongoDB Enterprise Cluster0-shard-0:PRIMARY>
С помощта на oplogFetcher вторични членове синхронизират oplog записи от техния syncSource. OplogFetcher задейства метод за намиране към изходния oplog, последван от серия от курсори getMores. Когато A се присъедини отново като вторичен, се прилага същият подход и се връща документ, по-голям от предикатното времеви печат. Ако първият документ в B не съвпада с последния запис на oplog на A, A ще бъде принуден да се върне назад.
Възстановяване на данни за връщане в MongoDB
Отмяната не е лошо нещо в MongDB, но трябва да се опитате колкото е възможно повече, за да гарантирате, че не се случват често. Това е автоматична мярка за безопасност за осигуряване на съгласуваност на данните между членовете на набор от реплика. В случай, че се случи връщане назад, ето няколко стъпки за справяне със ситуацията:
Събиране на данни за връщане назад
Трябва да събирате данни за членове относно връщането назад. Това се прави, като се гарантира, че са създадени файлове за връщане назад (достъпно само с MongoDB версия 4.0), като се активира createRollbackDataFiles. По подразбиране тази опция е зададена на true, следователно файловете за връщане винаги ще се създават.
Файловете за връщане се поставят в пътя
Зареждане на данните за файловете за връщане назад в отделна база данни или сървър
Mongorestore е жизненоважен аспект на MongoDB, който може да помогне за възстановяване на файлове с данни за връщане назад. Първото нещо е да копирате файловете за връщане назад в нов сървър, след което с помощта на mongorestore да заредите файловете във вашия сървър. Командата mongorestore е показана по-долу.
mongorestore -u <> -p <> -h 127.0.0.1 -d <rollbackrestoretestdb> -c <rollbackrestoretestc> <path to the .bson file> --authenticationDatabase=<database of user>
Почистване на данни, които не са необходими, и пресяване на данни
Тази стъпка е необходимо да използвате дискретност, за да изберете между данните, които да бъдат запазени от файловете за връщане назад, и данните, които да бъдат изхвърлени. Препоръчително е да импортирате всички данни за файловете за връщане назад, тази точка на решение прави тази стъпка най-трудната стъпка при възстановяване на данни.
Използване на основния като клъстер за импортиране на данни
Започнете последната стъпка, като изтеглите изчистени данни чрез използването на mongorestore и mongodump, следвайте това чрез повторно импортиране на данните в оригиналния производствен клъстер.
Предотвратяване на връщане на MongoDB
За да предотвратите връщане на данни при използване на MongoDB, можете да направите следното.
Извършване на всички гласуващи членове „MJORITY“
Това може да бъде направено чрез използване на w:загриженост за повечето записи, която има правомощията да изисква опция за потвърждение, което ще позволи операция на запис към дадени конкретни тагове на екземпляри на Mongod. Това може да се постигне с помощта на опцията w, последвана от таг
Потребителски операции
Актуализираната версия на MongoDB, т.е. версия 4.2, има способността да изключва всички текущи операции в случай на връщане назад.
Изграждане на индекси
Версия 4.2 на версията за съвместимост на функциите на MongoDB (fcv) "4.2" може да изчака всички индекси в процес на изграждане и да завърши, преди да започне връщане назад място. Въпреки това, версия 4.0 изчаква продължаващото в ход и изгражда фонов индекс, така че вероятността от връщане е голяма.
Размер и ограничения
Версия 4.0 на MongoDB няма изброени ограничения за дадени данни, които могат да бъдат върнати, когато се изгражда фонов индекс в ход.
Заключение
Отмяната на MongoDB е често срещано явление за тези, които използват MongoDB, без да знаят как да го предотвратят. Отмяната са предотвратими, ако човек внимателно следва и се придържа към някои от безопасните практики и начини за избягване на връщане назад в MongoDB. Като цяло винаги е препоръчително да надстроите до най-новата версия на MongoDB, за да избегнете някои предотвратими хълцания.