Evo sređeno je i radi. Bogdane HVALA na dragocenim hintovima.
Da sublimiramo.
Zadatak je bio da se improvizuje replikacija baze na SLAVE koji se nalazi na shared hostu, jer zbog ograničenja shared hostinga nije
bilo mogućnosti da se napravi prava replikacija.
Bogdan je predložio da se iskoriste binary logovi koje MySql inače pravi za potrebe replikacije i point in time restore.
Binarne logove exportovati u text format pomocu mysqlbinlog alatke.
Prilikom exporta obavezno koristiti --base64-output=decode-rows opciju da ne bi đubrio po logu BINLOG komande koje ne mogu da prođu na SLAVE hostu.
Text logove očistiti od komandi koje se ne mogu izvršiti na shared hostu.
(za sada se samo jednu pojavila: SET @@session.pseudo_thread_id i nije bitna te je mogžemo slobodno skloniti)
Izvršiti logove na SLAVE
Počistiti ili skloniti poslate logove i spremiti se za sledeći ciklus.
Prvo je potrebno konfigurisati MASTER server i restartovati ga:
Code:
my.ini:
log-bin=mojabaza-bin
binlog-format=STATEMENT
binlog-do-db=mojabaza
Na SLAVE poslati MASTER bazu bitno je samo da se odradi FLUSH LOG prilikom dumpa MASTERA kako bi smo imali tačan presek stanja.
Periodično pokretati skriptu koja će žonglirati sa logovima i upucavati ih na SLAVE
Implementacija nije bog zna koliko teška. Ja sam se odlučio za win powershell, jer mi je "dot-baš-hoću" blizak :-)
Odrađena je za Win sistem, mada bi na linuxu išlo i još jendostavnije ....
Preduslovi su:
.NET 2+
MySQL .NET Connector v 6.4.3.0
Powershell 2.0
Win XP SP3 i noviji
Za inicijalno upucavanje baze na SLAVE odnosno hvatanje koraka sa MASTER bazom ide ova skripta:
(može se koristiti za bilo kakvo prebacivanje mysql baze sa servera na server):
Code:
####################################################################################################
##
## Mysql: Windows PowerShell skripta za kopiranje baze sa servera na server
##
####################################################################################################
[string]$masterhost = "host"
[string]$masteruser = "user"
[string]$masterpassword = "password"
[string]$masterdatabase = "database"
[string]$slavehost = "host"
[string]$slaveuser = "user"
[string]$slavepassword = "password"
[string]$slavedatabase = "database"
[string]$dumpfile = "dumpbaze"
[string]$mysqldatapath = "C:\Documents and Settings\All Users\Application Data\MySQL\MySQL Server 5.1\data"
set-alias mysqlexe 'C:\Program Files\MySQL\MySQL Server 5.1\bin\mysql.exe'
set-alias mysqldumpexe 'C:\Program Files\MySQL\MySQL Server 5.1\bin\mysqldump.exe'
[string]$dumptmp = $dumpfile + ".tmp"
[string]$dumpsql = $dumpfile + ".sql"
###################################################################################################################
# Dump baze, obrati pažnju na --flush-logs koji pravi presek stanja u logovima!
# Izvršiti: mysqldump --host=masterhost --user=masteruser --password=masterpassword --result-file=dumpfile.tmp --no-create-db --flush-logs masterdatabase
Set-Location $mysqldatapath
$arguments = '"--host=' + $masterhost + '" ' + '"--user=' + $masteruser + '" ' + '"--password=' + $masterpassword + '" ' + '"--result-file=' + $dumptmp + '" ' + '"--no-create-db" ' + '"--flush-logs" "' + $masterdatabase + '"'
mysqldumpexe $arguments
# Vidi da li je sve proslo OK?
if(!$?)
{
Write-Host "Master MySQL server error! "
Remove-Item $dumptmp
exit 1
}
else
{
Write-Host "MASTER dump OK!"
}
# Izbaci iz dumpa sve sto ima DEFINER=.....
# U pitanju je zvrčka sa mysqldumpom, ako imamo view, trigger ili stored proc u bazi, a ne poklapaju se useri na masteru i slave
Get-Content $dumptmp | Where-Object {$_ -notmatch 'DEFINER'} | Out-File -Encoding default $dumpsql
# Pošalji bazu na SLAVE server
# Izvršiti: mysql --host=slavehost --user=slaveuser --password=slavepassword --database=slavedatabase --compress < dumpfile.sql
$arguments = '"--host=' + $slavehost + '" "--user=' + $slaveuser + '" "--password=' + $slavepassword + '" "--database=' + $slavedatabase + '" "--compress"'
$stderr = Get-Content $dumpsql -Encoding Ascii | mysqlexe $arguments 2>&1
# Vidi da li je sve proslo OK?
if ($LASTEXITCODE -eq 1)
{
$Greska = 'Greska: SLAVE server nije dostupan, nije u funkciji ili nije mogao da izvrši poslatu komandu' + [Environment]::NewLine + [Environment]::NewLine + $stderr
Write-Host $Greska
exit 1
}
#Počisti za sobom
Remove-Item $dumptmp
Remove-Item $dumpsql
Write-Host "SLAVE dump import OK!"
Za periodično upucavanje logova u SLAVE bazu ide ova:
Code:
# Ucitaj .NET MySQL Konektor Assembly
Add-Type -AssemblyName ('MySql.Data, Version=6.4.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d')
#Parametri za MASTER server
[string]$masterhost = "host"
[string]$masteruser = "user"
[string]$masterpassword = "password"
[string]$masterdatabase = "database"
#Parametri za SLAVE server
[string]$slavehost = "host"
[string]$slaveuser = "user"
[string]$slavepassword = "password"
[string]$slavedatabase = "slave"
#Pomoćne stvarčice
[string]$mysqldatapath = 'C:\Documents and Settings\All Users\Application Data\MySQL\MySQL Server 5.1\data'
set-alias mysqlbinlogexe 'C:\Program Files\MySQL\MySQL Server 5.1\bin\mysqlbinlog.exe'
set-alias mysqlexe 'C:\Program Files\MySQL\MySQL Server 5.1\bin\mysql.exe'
[string]$txtlogfile = "txt-log.log"
[string]$txttmpfile = "txt-log.tmp"
[string]$stderr = ""
[string]$greska = ""
#Email infrastruktura
[bool]$SendEmail = $True # (Email Isključ/Uključ - $True / $False)
[string]$EmailFrom = "from"
[string]$EmailTo = "to"
[string]$SMTPServer = "host"
[string]$SMTPUser = "user"
[string]$SMTPPassword = "password"
[string]$ReplicationFailed = "Replikacija baze nije uspela!"
[string]$ReplicationOk = "Replikacija baze uspešna!"
[int]$SMTPort = 25
function SendEmail
{param ($Subject, $Body)
if ($SendEmail)
{
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, $SMTPort)
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($SMTPUser, $SMTPPassword);
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
}
}
############################################################################################################
## Ovde počinjemo
############################################################################################################
# Okači se na MASTER server, koristi se .NET connector
[string]$connectionstring = "SERVER=" + $masterhost + ";DATABASE=" + $masterdatabase + ";UID=" + $masteruser + ";PWD=" + $masterpassword
$cn = New-Object -TypeName MySql.Data.MySqlClient.MySqlConnection
$cn.ConnectionString = $connectionstring
try
{
$cn.Open()
}
catch
{
Write-Host Write-Host 'Greska: MASTER server nije dostupan ili ne radi'
SendEmail $ReplicationFailed 'MASTER server nije dostupan ili ne radi'
exit 1
}
# Flushuj bin-log - pravimo presek stanja pred slanje
$cm = New-Object -TypeName MySql.Data.MySqlClient.MySqlCommand
$cm.Connection = $cn
$cm.CommandText = "FLUSH LOGS;"
$cm.ExecuteNonQuery() > $null
# Pokupi naziv tekućeg bin-log fajla, on se NE šalje sada
$cm.CommandText = "SHOW MASTER STATUS;"
$dr = $cm.ExecuteReader()
if ($dr.Read())
{
$currentbinlog = $dr.GetString(0)
$dr.Close()
}
else
{
Write-Host 'Greska: Na MASTER serveru nije podešen bin-log!'
SendEmail $ReplicationFailed 'Na MASTER serveru nije podešen bin-log!'
exit 1
}
# Pokupi nazive svih bin-log fajlova, može da ih bude i nekoliko komada
# Svaki restart servera pravi novi bin-log ... a ima i još načina da se nakote ...
$cm.CommandText = "SHOW BINARY LOGS;"
$dr = $cm.ExecuteReader()
# Od naziva bin-log fajlova napravi kobasicu koja će reći mysqlbinlog utilu koje sve logove da prebaci u tekst format
# Naravno, izuzima se tekući bin-log
[string]$binlogs = ''
while($dr.read())
{
$binlog = $dr.GetString(0)
if (!($binlog -eq $currentbinlog))
{
$binlogs += '"' + $dr.GetString(0) + '" '
}
}
$dr.Close()
# Export bin-logova u tekst format
# Izvršava se nešto slično: mysqlbinlog.exe mojabaza-bin.000020 mojabaza-bin.000021 --base64-output=decode-rows --result-file=mojabaza-txt.tmp
Set-Location $mysqldatapath
$arguments = $binlogs + '"--base64-output=decode-rows" ' + '"--result-file=' + $txttmpfile + '"'
mysqlbinlogexe $arguments
# Čišćenje loga od komandi koje ne mogu da prođu, za sada se pojavljuje samo ova SET @@sesssion.pseudo_thread_id .... i nije problematična da se otfikari.
Get-Content $txttmpfile | Where-Object {$_ -notmatch 'SET @@session.pseudo_thread_id'} | Out-File -Encoding default $txtlogfile
# Pošalji logove na SLAVE
# Izvršava se: mysql --host=slavehost --user=slaveuser --password=slavepassword --database=slavedatabase --compress < mojabaza-txt.sql
$arguments = '"--host=' + $slavehost + '" "--user=' + $slaveuser + '" "--password=' + $slavepassword + '" "--database=' + $slavedatabase + '" "--compress"'
$stderr = Get-Content $txtlogfile -Encoding Ascii | mysqlexe $arguments 2>&1
if ($LASTEXITCODE -eq 1)
{
$Greska = 'Greska: SLAVE server nije dostupan, nije u funkciji ili nije mogao da izvrši poslatu komandu' + [Environment]::NewLine + [Environment]::NewLine + $stderr
Write-Host $Greska
SendEmail $ReplicationFailed $Greska
exit 1
}
# Ako je sve proslo kako treba, obriši poslate logove i obavesti da smo OK
$cm.CommandText = "PURGE BINARY LOGS TO '" + $currentbinlog + "'"
$cm.ExecuteNonQuery() > $null
$cn.Close()
# Remove-Item $txtlogfile
# Remove-Item $txttmpfile
Write-Host $ReplicationOk
SendEmail $ReplicationOk $ok 'Replikacija uspešno završena'
[Ovu poruku je menjao vbbojan dana 15.08.2011. u 09:05 GMT+1]
[Ovu poruku je menjao vbbojan dana 15.08.2011. u 09:06 GMT+1]