Για τους περισσότερους ένα application έχει μια database. Αυτό απολοποιεί σε μεγάλο βαθμό κάποια πράγματα και ένα από αυτά είναι και το backup της database. Ακόμα και σε αυτή την περίπτωση όμως πρέπει να ληφθούν σοβαρά όλες οι παράμετροι όπως το RPO και το RTO.
Τα πράγματα όμως παίρνουν μια άγρια ομορφιά όταν πίσω από την εφαρμογή σου υπάρχουν περισσότερες από μια databases. Σενάρια για μια τέτοια κατάσταση υπάρχουν αρκετά όπως για παράδειγμα να έχεις μια OLTP database που ενημερώνει μια MIS database ή να έχεις δύο OLTP databases που ενημερώνονται παράλληλα κατά την διάρκεια των transactions που υλοποιούνται από το application.
To πιθανότερο είναι αυτές οι δύο ή και περισσότερες databases να μην είναι του ίδιου μεγέθους και φυσικά δεν χρειάζονται το ίδιο χρόνο για ολοκληρωθεί σε κάθε μία το backup της. Ακόμα υπάρχει και η περίπτωση να εκτελείται πρώτα το backup στην μία μετά στην άλλη κ.ο.κ.
Σε αυτές τις περιπτώσεις το κάθε backup περιέχει μεν όλα τα δεδομένα αλλά αυτά βρίσκονται σε διαφορετική χρονική στιγμή. Επειδή με την τελευταία μου πρόταση κάποιοι ίσως να βγάλουν λάθος συμπεράσματα θα δώσω ένα απλό παράδειγμα.
Έστω ότι έχω μια database στην οποία έχω τα τιμολόγια και σε μια άλλη έχω τους πελάτες και την αποθήκη. Όταν κόβω ένα τιμολόγιο αυτό θα πρέπει να ενημερώσει τα tables και στις δύο databases. Αυτό είναι κάτι που εύκολα μπορεί να γίνει με την χρήση ενός transaction, μια τυπική και ασφαλής διαδικασία.
Αν τώρα για την πρώτη database χρειάζονται 2 ώρες για την εκτέλεση του backup και 1 ώρα για την δεύτερη τότε σε περίπτωση που υπάρξει η ανάγκη να κάνω restore η πρώτη θα περιέχει περισσότερα δεδομένα από την δεύτερη καθώς χρειάζεται περισσότερο χρόνο για την ολοκλήρωση του backup. Αυτή είναι η καλύτερη περίπτωση καθώς έχω φροντίσει να ξεκινάει το backup ταυτόχρονα. Στην περίπτωση που η διαδικασία γίνεται σειριακά τότε η δεύτερη θα περιέχει περισσότερα δεδομένα από την πρώτη.
Αν αυτές κάποια στιγμή χρειαστεί να τις κάνω restore τότε θα βρεθώ σε εκπλήξεις όπως να έχω περισσότερα τιμολόγια και να μην είναι ενημερωμένοι οι πελάτες και η αποθήκη ή και το αντίθετο. Αυτό δεν είναι κάποιο bug ούτε μπορεί κάποιος να ισχυριστεί σε καμία περίπτωση κάτι τέτοιο. Είναι ένα φαινόμενο που πρέπει ένας DBA να το διαχειριστεί, αρκεί βέβαια να γνωρίζει πως λειτουργεί το application.
Η λύση σε μια τέτοια περίπτωση είναι σχετική απλή και βασίζεται σε μια απλή τεχνική η οποία με κάποιο τρόπο να τραβάει μια γραμμή (νοητή) λίγο πριν ξεκινήσει η διαδικασία του backup έτσι ώστε αν χρειαστεί να γίνει κάποιο restore αυτό να γίνει με την συγκεκριμένη γραμμή στην οποία τα δεδομένα σε όλες τις εμπλεκόμενες databases θα είναι in-sync.
Φυσικά σε μια τέτοια περίπτωση δεν θα έχουμε ότι έχει γίνει κατά την διάρκεια των backups αλλά είναι επιλογή μας αν θα σταματήσουμε στο σημείο αυτό ή θα πάμε και στα παρακάτω. Το σίγουρο είναι ότι επιλέγοντας την πρώτη λύση θα έχουμε δεδομένα που θα είναι λογικά σωστά, ενώ στην άλλη λύση θα πρέπει να βρούμε τους μηχανισμούς αυτούς που συγχρονίζουν και φέρνουν τα δεδομένα στην λογική τους ορθότητα.
Για να γίνει κάτι τέτοιο απλά σε κάθε database θα πρέπει να δημιουργηθεί ένα table (όμοιο δε δομή κατά προτίμηση) και πριν ξεκινήσει η διαδικασία του backup να εκτελείται ένα απλό transaction που θα κάνει ένα απλό insert στα table αυτά. Αυτό όμως θα είναι ένα marked transaction. Δυνατότητες που χρόνια τώρα έχουμε διαθέσιμες στον SQL Server.
Ας υποθέσουμε ότι έχουμε δύο databases τις DB1 και DB2. Αυτές θα πρέπει να μην είναι σε simple recovery model καθώς για την υλοποίηση της λύσης είναι απαραίτητο το transaction log backup.
Σε αυτές μπορούμε να φτιάξουμε ένα πίνακα ίδιο ο οποίος μπορεί να περιέχει ότι θέλουμε προσωπικά έχω επιλέξει αυτός ο πίνακας να έχει την εξής μορφή
create table dbo.backupmmarks(markname varchar(100),markdesc varchar(100),markdate datetime2(7) default (sysdatetime()));
Από την στιγμή που έχουμε φτιάξει το πίνακα αυτό σε όλες τις εμπλεκόμενες databases μπορούμε να δημιουργήσουμε την διαδικασία του backup.
Αν απλά έχουμε σαν πλάνο να παίρνουμε μόνο full backup το script πρέπει να είναι το παρακάτω:
-- take full backup on databases
backup database db1 to dev1;
backup database db2 to dev2;
-- generate mark string
declare @trn_mark_name varchar(100),@trn_mark_desc varchar(100);
set @trn_mark_name = 'backup_' + convert(varchar(16),getdate(),126);
set @trn_mark_desc = 'backup of ' + convert(varchar(100),getdate(),100);
-- implementation of marked transaction
begin tran @trn_mark_name with mark @trn_mark_desc
insert into db1.dbo.backupmmarks (markname,markdesc) values (@trn_mark_name,@trn_mark_desc);
insert into db2.dbo.backupmmarks (markname,markdesc) values (@trn_mark_name,@trn_mark_desc);
commit
-- take transaction log backup to keep mark
backup log db1 to dev1;
backup log db2 to dev2;
Αν τώρα στο πλάνο μας είναι να έχουμε και άλλα backups όπως differential και transaction log backups τότε:
Για τα differential backups το script είναι το ίδιο με το παραπάνω αρκεί φυσικά να προσθέσουμε το with differential.
Για τα transaction logs μας χρειάζεται το τμήμα από το παραπάνω script που ξεκινάει από το comment "generate mark string"
Αν χρειαστεί τώρα να κάνουμε restore μπορούμε να κάνουμε αυτό και στις δύο databases επιλέγοντας το mark που θέλουμε από backup που θέλουμε το οποίο μπορούμε να το πάρουμε είτε διαβάζοντας το table dbo.backupmarks είτε το πίνακα που υπάρχει στην msdb και είναι ο dbo.logmarkhistory.
Μάλιστα αν χρησιμοποιήσουμε τον dbo.logmarkhistory μπορούμε να βάλουμε στην εντολή του restore log το LSN αντί του mark
restore database db1 from dev1 with file=1, norecovery, replace;
restore log db1 from dev1 with file=2, stopatmark='backup_2016-01-23T23:30';
restore database db2 from dev2 with file=1, norecovery, replace;
restore log db2 from dev2 with file=2, stopatmark='backup_2016-01-23T23:30';
Υ.Γ
Το άρθρο αυτό είναι αφιερωμένο σε κάποιο ventor που ενώ η εφαρμογή του χρησιμοποιεί περισσότερες από μία databases δεν είχε υλοποιήσει κάτι τέτοιο και με ταλαιπώρησε την προηγούμενη εβδομάδα καθώς έλεγε ότι αυτές μετά από ένα restore που χρειάστηκε να γίνει δεν είναι inconsistent και έψαχνα να βρω ποιες είναι οι προβληματικές σελίδες αλλά τελικά αυτό που εννοούσε ήταν ότι δεν ήταν λογικά ορθά τα δεδομένα τους.
/*antonch*/