== **Складирање и репликација** === Логичка репликација \\ Логичка репликација претставува процес на репродуцирање на податоците помеѓу повеќе јазли (инстанци) на база на податоци, при што промените направени на изворниот сервер се запишуваат во бинарни логови и потоа се репродуцираат на реплика-серверите.\\Во пракса, тоа претставува копирање на INSERT, UPDATE, DELETE и DDL операции од еден сервер кон друг, а не директно копирање на физичките датотеки.\\ Овој метод овозможува систем отпорен на грешки (fault tolerant), бидејќи секоја промена што се случува на примарната база се пресликува и на репликите.\\ Дополнително, може да се користи за load-balancing - операции за читање се насочуваат кон реплика-инстанците, а write-операциите кон примарниот сервер,\\ === Подесување на MySQL GTID-базирана репликација со Docker Во овој чекор ќе конфигурираме **GTID(Global Transaction Identifier)**-базирана логичка репликација во MySQL, користејќи две Docker инстанци - **source и replica.**\\ Секоја трансакција што се извршува во Source серверот добива GTID, кој обезбедува прецизно и автоматско следење и примена на промените во Replica.\\ Во MySQL се користат бинарни log датотеки (binlog files) за да се запише секоја промена врз базата - **INSERT, UPDATE, DELETE и DDL**.\\ \\ GTID структурата се гледа преку binlog, пример:\\ {{{ SET @@SESSION.GTID_NEXT='4e89b248–2c8e-11ed-af3b-020017000790:1'; }}} \\ Со **GTID** можеме да го користиме **AUTO_POSITION = 1**, што значи: * replica сама знае која трансакција ѝ недостига * не следиме BINLOG позиции рачно * можеме да рестартираме replica без губење на sync == **Конфигурација на Source и Replica Docker инстанци** Се одлучивме за конфигурација преку Docker затоа што добиваме изолирана и лесно пренослива средина, каде секоја MySQL инстанца е одвоена, конфигурирана со сопствени параметри и може да се стартува, стопира или реконфигурира без да влијае на остатокот од системот.\\ === Конфигурирање replica user {{{ CREATE USER IF NOT EXISTS 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'replpass'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES; }}} Овој корисник е потребен за да се воспостави безбедна комуникација меѓу двата сервери. Привилегијата **REPLICATION SLAVE** дозволува читање на bin-log записите и нивно пренесување кон репликата, без да се доделуваат пошироки непотребни администраторски права.\\ \\ Во replica ја поставуваме врската кон изворниот MySQL инстанцa со следната команда:\\ {{{ CHANGE REPLICATION SOURCE TO SOURCE_HOST = 'mysql_source', SOURCE_PORT = 3306, SOURCE_USER = 'repl', SOURCE_PASSWORD = 'replpass', SOURCE_AUTO_POSITION = 1; START REPLICA; }}} SQL-конфигурацијата ја одвоивме во посебни .sql фајлови кои автоматски се извршуваат преку docker-entrypoint, затоа што: * конфигурацијата станува репродуцибилна и повторно извршлива * setup-от на репликација е целосно автоматизиран (без рачно внесување команди) * ако се создадат контејнерите повторно, MySQL сам ќе ја примени истата конфигурација без дополнителна интервенција * документацијата, миграцијата и version-control стануваат чисти и управливи === Некои конфигурациски параметри за MySQL GTID-базирана репликација [[Image(mysql-parameters.png)]] За подигнување на GTID-базирана репликација, користиме docker-compose.yml во кој дефинираме две MySQL инстанци:\\ >**source** → примарен сервер (прифаќа WRITE операции)\\ >**replica** → сервер кој ги реплицира состојбите од source (READ-only)\\ \\ {{{ version: "3.9" services: source: image: mysql:8.0 container_name: mysql_source environment: MYSQL_ROOT_PASSWORD: root command: - --server-id=1 - --log-bin=mysql-bin - --binlog-format=ROW - --gtid-mode=ON - --enforce-gtid-consistency=ON - --log-slave-updates=ON - --skip-host-cache - --skip-name-resolve ports: - "3307:3306" volumes: - ./data-source:/var/lib/mysql - ./init-source:/docker-entrypoint-initdb.d replica: image: mysql:8.0 container_name: mysql_replica environment: MYSQL_ROOT_PASSWORD: root command: - --server-id=2 - --log-bin=mysql-bin - --relay-log=relay-bin - --binlog-format=ROW - --gtid-mode=ON - --enforce-gtid-consistency=ON - --log-slave-updates=ON - --read-only=1 - --super-read-only=1 - --skip-host-cache - --skip-name-resolve ports: - "3308:3306" depends_on: - source volumes: - ./data-replica:/var/lib/mysql - ./init-replica:/docker-entrypoint-initdb.d }}} Во нашиот случај базата веќе постоеше локално (airportdb) и содржеше голем број редови и табли. Наместо да создадеме празна структура и рачно да ја пополнуваме повторно, потребно беше да ја мигрираме целата база во Source контејнерот, а потоа преку репликацијата таа автоматски се пренесува во Replica.// За таа цел користевме **MySQL Shell util.dumpInstance()**, со што креиравме оптимизиран dump со паралелна обработка.// // Прво се конектиравме на локалната MySQL инстанца со MySQL Shell: {{{ mysqlsh root@localhost:3306 --js }}} Потоа извршивме dump на базата: {{{ util.dumpInstance('./dump-airport', { threads: 8, compression: 'gzip' }) }}} Airportdb е голема продукциска база со ~60M редови и ~2.22GB податоци, па затоа со повеќенитно извршување добиваме паралелно читање на блокови и редови.// По успешно креирање на dump-датотеките, ги импортиравме во Source: {{{ mysqlsh root@127.0.0.1:3307 --js -e " util.loadDump('./dump-airport', {threads: 8}) " }}} '''Резултат:''' [[Image(dump.PNG)]] == **Стартување, валидација и тестирање на репликацијата** По подигнувањето на двете Docker MySQL инстанци и вчитувањето на post-dump податоците во source, направивме процес на проверка дали репликацијата функционира коректно и дали податоците се усогласени на двата сервери.// // Прво проверивме дали сите табели и податоци од локалната база (airportdb) се успешно внесени во source, а потоа автоматски реплицирани и на replica. Со стартувањето на контејнерите и примената на dump-датотеката, податоците беа присутни на двата сервери.// // Потоа, за дополнителна проверка, креиравме test табела во source, по што истата по неколку секунди се појави и во replica-инстанцата. Ова беше показател дека GTID binlog репликацијата е активна и дека записите се процесираат и применуваат на репликата во реално време.// // Направивме и обид да се додаде нов ред во replica. Операцијата беше одбиена со грешка, бидејќи репликата беше во read-only режим и дополнително заштитена со super-read-only, што го блокираше пишувањето дури и за root корисник. Ова го потврди целосниот механизам за заштита, при што единствено source може да прима write-операции, а replica функционира како read-only копија.// // * [[html(Скриншоти од процесот на валидација)]]