Jump to content

antonch

Administrators
  • Posts

    1030
  • Joined

  • Last visited

  • Days Won

    7

Blog Entries posted by antonch

  1. antonch
    Σήμερα έλαβα ένα mail από το MVP Program το οποίο με ενημέρωνε ότι έγινα MVP στον SQL Server.
    Ήταν το πρώτο και μοναδικό mail που πήρα για το 2010 και με γέμισε χαρά, ικανοποίηση αλλά και υποχρεώσεις για το μέλλον.
    Θα ήθελα να ευχαριστήσω όλους σας για αυτό και ιδιαίτερα τον Νάσο Κλαδάκη και την Μάρθα Πετροπούλου για την βοήθεια τους και την προτροπή τους στο να προσπαθήσω για τον τίτλο αυτό.
    ΚΑΛΗ ΧΡΟΝΙΑ ΣΕ ΟΛΟΥΣ
  2. antonch
    Αναζητώντας τρόπους για να εκτονώσω την όρεξη μου για εκπαίδευση αποφάσισα να κάνω κάτι μιας και δεν βλέπω να γίνεται κάτι άλλο αυτό τον καιρό.
    Η ιδέα μου ήρθε έτσι ξαφνικά σήμερα το βράδυ ακούγοντας την βροχή να πέφτει.
    Σκέφτηκα ότι τώρα που χειμωνιάζει και οι περισσότεροι θα είμαστε χωμένοι μέσα στην ζεστασιά του σπιτιού μας τα βράδια του Σαββάτου να διοργανώσω εκπαιδευτικά live meetings με θέματα που αφορούν τον SQL Server (και Visual Studio άμα θέλετε).
    Το concept όπως το έχω σκεφτεί έχει ως εξής:
    Κάθε δεύτερο Σάββατο γύρω στις 23:00 το βράδυ που όλοι θα είστε χαλαροί μιας και θα έχετε βάλει τις πυτζάμες σας, τις παντόφλες σας, και τα παιδία για ύπνο να βρισκόμαστε ηλεκτρονικά και να σας κάνω εκπαίδευση πάνω στο θέμα το οποίο είτε θα έχω επιλέξει είτε θα μου έχετε προτείνει εσείς.
    Φυσικά όλα αυτά θα είναι δωρεάν και θα έχει διάρκεια περίπου 2 ώρες.
    Επειδή δεν ξέρω αν αυτό θα έχει ανταπόκριση θα ήθελα τη γνώμη σας ώστε αν δω ότι είναι θετική να προχωρήσω στην διοργάνωση του.
    Για το λόγο αυτό θα ήθελα να ποστάρετε την απάντηση σας στο παρόν ποστ και φυσικά αν έχετε κάποιο θέμα που θα θέλατε να παρουσιαστεί.
    Επίσης καλό θα ήταν αυτό να το διαδώσετε και στους φίλους σας ώστε να μπουν και αυτοί να ψηφίσουν και φυσικά να θέσουν το θέμα τους
    Φιλικά
    Αντώνης
    Υ.Γ Θα παρακαλούσα να έχω τις απαντήσεις εδώ και όχι κάπου άλλου ώστε να τις έχω συγκεντρωμένες σε ένα σημείο
  3. antonch
    Βλέποντας μέσα από το forum μας διάφορες συζητήσεις σχετικά με το θέμα του μεγέθους του transaction log (T-Log) διαπίστωσα ότι υπάρχει ένα θολό τοπίο γύρω από το θέμα disaster recovery (backup - restore) πάνω στον SQL Server.
    Πήρα την απόφαση να γράψω για αυτό το θέμα ώστε να το ξεκαθαρίσω μια και καλή διότι είναι τόσο απλό και τόσο δυνατό που είναι αμαρτία από το Θεό να παιδεύεται ο κόσμος.
    Πριν ξεκινήσω να παρουσιάζω το θέμα θα πρέπει να ξεκαθαρίσω, και θα ήθελα αυτό να το έχετε σαν αρχή, στον SQL Server παίρνουμε SQL Server Backup και όχι οτιδήποτε άλλο.
    Μέχρι σήμερα δεν σας έχω πει τίποτα για Ευαγγέλια, όσοι έχετε κάνει μάθημα μαζί μου όμως ξέρετε, σήμερα θα σας πω ένα από αυτά.
    Επίσης θεωρώ φρόνιμο να ρίξετε πρώτα μια ματιά σε αυτό το post μου διότι θα σας φανεί χρήσιμο για να κατανοήσετε το παρόν άρθρο.
    Database Recovery Models
    Πρώτα από όλα θα πρέπει να καταλάβουμε τι είναι τα database recovery models, τι κάνουν, ποια είναι και πως επηρεάζουν το disaster recovery strategy που θα ακολουθήσουμε.
    Simple Recovery Model
    Είναι το απλούστερο μοντέλο το οποίο μπορεί κάποιος να επιλέξει σε μια βάση δεδομένων.
    Ενδείκνυται για :
    1. μικρές βάσεις δεδομένων,
    2. για βάσεις που έχουν μικρό αριθμό αλλαγών (transactions),
    3. για τις περιπτώσεις που θέλουμε να έχουμε μικρό μέγεθος στο T-Log file,
    4. για βάσεις που είναι read-only,
    5. για τις βάσεις που είναι σε φάση development ή δουλεύουν developers με αυτές για την κατασκευή εφαρμογών.
    Το συγκεκριμένο μοντέλο δουλεύει με τον εξής τρόπο, κάθε φορά που γίνεται διαδικασία checkpoint ή φτάνει να γεμίσει το φυσικό αρχείο του T-Log file γίνεται αυτόματα truncate με αποτέλεσμα να επαναχρησιμοποιείτε ο εσωτερικά ελεύθερος χώρος.
    Το μειονέκτημα στο μοντέλο αυτό είναι ότι μπορείς να γυρίσεις στον τελευταίο full backup που έχεις πάρει στην περίπτωση που η βάση σκάσει.
    Full Recovery Model
    Είναι το default recovery model. Όλα τα transactions που γίνονται στην βάση καταγράφονται στην λεπτομέρεια τους σε επίπεδο data page και row και αυτό έχει σαν αποτέλεσμα να χρειάζεται περισσότερος χώρος για το T-Log file. Το πλεονεκτήματα του είναι ότι στις περιπτώσεις που έχω πτώση της βάσης μπορώ να γυρίσω ακριβώς στο σημείο της πτώσης της χάνοντας μόνο τα transactions που την στιγμή της πτώσης ήταν σε εξέλιξη δηλαδή δεν είχαν γίνει commit!.
    Bulk-Logged Recovery Model
    Είναι ίσως αυτό που πολλοί DBA δεν το έχουν καταλάβει πως δουλεύει!. Αυτό κάνει ακριβώς ότι κάνει και το Full Recovery Model με μια σημαντική διαφορά. Καταγράφει τα πάντα σε επίπεδο extent (8 συνεχόμενες σελίδες). Αυτό έχει σαν αποτέλεσμα να μπορώ να γυρίσω πάλι στο σημείο της πτώσης της βάσης αλλά χρειάζομαι περισσότερο χρόνο για να έρθει αυτή σε κατάσταση online.
    Για να γίνει κατανοητό αυτό ας πάρουμε σαν παράδειγμα το εξής:
    Έστω ότι έχω μια βάση που είναι σε Full Recovery Model και φτιάχνω ή συντηρώ κάποιον index που έχω σε αυτή. Αφού έχει τελειώσει η εργασία αυτή έστω ότι έχω πτώση της βάσης. Όταν θα κάνω restore η εργασία αυτή θα είναι έτοιμη. Εάν όμως είμαι σε Bulk-Logged Model τα πράγματα είναι λίγο διαφορετικά. Για να το κάνω πιο κατανοητό ας πούμε ότι καταγράφεται η ενέργεια και όχι το αποτέλεσμα της. Έτσι όταν θα κάνω restore θα πρέπει να ξανακάνει πάλι την ενέργεια αυτή, που όπως γίνεται εύκολα αντιληπτό από την φύση της ενέργειας θα πάρει χρόνο για να την ολοκληρώσει.
    Σε αυτό το μοντέλο όλες οι bulk εργασίες γίνονται logged με το ελάχιστο δυνατό τρόπο. Τέτοιες εργασίες είναι:
    · Bulk imports of data (BCP, BULK INSERT, OPENROWSET with BULK clause)
    · BLOB operations ( WRITETEXT, UPDATETEXT)
    · SELECT INTO statements
    · CREATE/ALTER INDEX, ALTER INDEX REBUILD, DBCC REINDEX
    Το μοντέλο αυτό είναι ιδανικό για περιπτώσεις που έχω data warehouses και βάσεις με μεγάλο όγκο από bulk εργασίες. Το μειονέκτημα του είναι δεν μπορώ να γυρίσω σε συγκεκριμένο σημείο (point-in-time) το οποίο όμως θα δούμε παρακάτω τι είναι αυτό.
    SQL Server Backup Types
    Για να δούμε όμως τις δυνατές επιλογές που έχουμε για backup στον SQL Server. Επίσης θα πρέπει να επισημάνω, κάτι το οποίο ισχύει για όλα τους τύπους, ότι μπορώ να πάρω backup ενώ υπάρχει κόσμος και δουλεύει πάνω στην βάση μου (το λέω γιατί μπορείς κάποιος να μην το έχει καταλάβει ότι αυτό γίνεται). Μπορώ να έχω όσα backups θέλω μέσα στην ημέρα αν αυτό δεν μου δημιουργεί προβλήματα απόδοσης στην βάση κατά τη στιγμή που γίνεται αυτή backup. Ναι υπάρχει κάποιο πέναλτι το οποίο επηρεάζεται από πολλούς παράγοντες όπως ταχύτητα μαγνητικών μέσων, μέγεθος βάσης και τύπος του backup.
    Full Backup
    Είναι η βάση για όλες τις άλλες επιλογές που έχω για backup στον SQL Server. Με άλλα λόγια δεν μπορώ να κάνω πχ Differential, Transaction, Tail-Log κλπ εάν δεν έχω πάρει έστω ένα Full Backup. Στην ουσία παίρνει τα πάντα backup με τον έξης τρόπο. Πρώτα κάνει την διαδικασία checkpoint όπου κατεβάζει με αυτή όλα τα data pages της βάσης, που παίρνω backup, από την buffer cache στον δίσκο και μετά ρουφάει σαν σκούπα hoover τα αρχεία από δίσκο και τα βάζει στο backup μου. Ο χρόνος εκτέλεσης μιας τέτοιας εργασίας είναι πάντα σχετικός με το μέγεθος το δεδομένων μου. Το μέγεθος του backup είναι πάντα το πραγματικό μέγεθος των δεδομένων που έχω στην βάση και όχι το φυσικό μέγεθος των αρχείων που υπάρχουν στο δίσκο. Εάν θέλει κάποιος να κάνει εκτίμηση του μεγέθους του Full Backup σε μια βάση του δεν έχει από το να εκτελέσει την sp_spaceused stored procedure και θα πάρει την απάντηση στο ερώτημα του. Επίσης θα πρέπει να γνωρίζουμε ότι Full Backup μπορώ να πάρω με οποιοδήποτε επιλεγμένο Recovery Model έχω στην βάση μου. Εν κατακλείδι ΠΑΝΤΑ FULL BACKUP είναι η ΒΑΣΗ ΟΛΩΝ.
    Στην ουσία το επόμενο full backup που θα πάρω περιέχει μέσα του όλα τα προηγούμενα.
    Differential Backup
    Για να πάρω Differential Backup πρέπει να έχω πάρει Full Backup. Μπορώ να πάρω diffrential backup με όλα τα recovery models. Κάθε τέτοιου τύπου backup περιέχει ΟΛΕΣ τις αλλαγές που έχουν γίνει από το τελευταίο Full Backup που έχω πάρει. Στην ουσία κάθε νέο differential backup "καταργεί" όλα τα προηγούμενα diffential backups.
    Ας πάρουμε ένα παράδειγμα. Έστω ότι έχω πάρει full backup στις 05:00 και στις 10:00 παίρνω differential backup. Αυτό περιέχει ότι έχει γίνει από τις 05:00 μέχρι τις 10:00. Εάν στις 13:00 ξαναπάρω differential αυτό περιέχει ότι έχει γίνει από τις 05:00 μέχρι τις 13:00.
    Ελπίζω αυτό να έγινε κατανοητό διότι αρκετοί συνάδελφοι πιστεύουν ότι το κάθε differential περιέχει τις αλλαγές από το τελευταίο backup.
    Ιδανικό σενάριο για μεγάλες βάσεις που το full backup παίρνει χρόνο. Επίσης το χρησιμοποιούμε και για να μειώσουμε τον αριθμό των αρχείων αλλά και το χρόνο που θα χρειαστούμε σε περίπτωση που θελήσουμε να κάνουμε restore.
    Transaction Log Backup
    Αυτός ο τύπος backup παίζει μόνο εφόσον στην βάση μου έχω FULL ή BULK-LOGGED Recovery Model.
    Φυσικά για να πάρω T-Log backup θα πρέπει να έχω πάρει ένα full backup. Κάθε T-Log backup περιέχει τα transactions που έχουν γίνει στην βάση μου για το χρονικό διάστημα από το τελευταίο full ή differential ή t-log backup. Ο χρόνος που απαιτείται για αυτό συνήθως είναι μικρός και το πέναλτι στην απόδοση σχεδόν ασήμαντο. Επίσης θα πρέπει να τονιστεί ότι EINAI ΤΟ ΜΟΝΑΔΙΚΟ BACKUP ΠΟΥ ΚΑΝΕΙ TRUNCATE TO LOG FILE. (δείτε το άρθρο μου "Ο θαυμαστός κόσμος του Transaction Log")
    Ας πάρουμε ένα παράδειγμα. Έστω ότι έχω πάρει full backup στις 05:00 και στις 07:00 παίρνω t-log backup αυτό περιέχει τα transactions που έχουν γίνει από τις 05:00 έως τις 07:00. Εάν στις 09:00 πάρω ξανά t-log backup αυτό θα περιέχει τα transactions που έχουν γίνει από τις 07:00 έως τις 09:00.
    Προσοχή: Το κάθε t-log backup χρειάζεται την προηγούμενη βάση του για να γίνει restore. Εάν για παράδειγμα χάσω το t-log backup που πήρα στις 07:00 δεν θα μπορέσω να κάνω restore αυτό που πήρα στις 09:00 αυτό στην ορολογία του SQL Server το ονομάζουμε transaction log chain.
    Tail-Log Backup
    Ένα tail-log backup είναι στην ουσία ένα t-log backup το οποίο περιέχει το κομμάτι του log το οποίο δεν έχει παρθεί backup. Μα θα μου πει κάποιος αυτό δεν είναι t-log backup; Όχι ακριβώς. Για να γίνει κατανοητό θα πάρουμε ένα παράδειγμα. Έστω ότι κάθε μέρα στις 05:00 κάνω full backup και κάθε 2 ώρες από εκεί και μετά t-log backup. Έστω ότι στις 11:30 η βάση μου αποδημεί εις τον Κύριο. Σύμφωνα με αυτά που έχουμε πει μέχρι τώρα στα χέρια μου έχω backups που όταν θα τα κάνω restore θα με γυρίσουν στις 11:00 άρα χάνω ότι έχω κάνει από 11:00-11:30. Ξαναλέω ότι η βάση είναι off. Πριν αρχίσω να κάνω restore θέλω να βάλω στην τσέπη μου αυτό το μισάωρο. Εάν τα καταφέρω τότε θα είμαι σε θέση να γυρίσω στις 11:30. Και τώρα κάποιος θα πει, "και γιατί δεν προσπαθώ να πάρω t-log backup;". Αυτό δεν μπορεί να γίνει διότι πρέπει για μια ακόμα φορά να τονίσουμε ότι κάθε φορά που πάω να πάρω t-log backup αυτός κάνει διαδικασία checkpoint αλλά επειδή η βάση είναι off δεν μπορεί να πάρει και backup. Έτσι παίρνω tail-log backup όπου ρουφάει σαν σκουπα hoover το log file χωρίς να κάνει διαδικασία checkpoint. Μάλιστα από την έκδοση του SQL Server 2005 και σας ζητάει σε κάποιες περιπτώσεις να πάρετε πρώτα tail-log backup και μετά να κάνετε restore. Θα σας εξηγήσω παρακάτω τον τρόπο με τον οποίο μπορείτε να κάνετε κάτι τέτοιο.
    File/Filegroup Backup
    Είναι το δυσκολότερο σε υλοποίηση backup στον SQL Server μιας και πρέπει να ξέρεις αρκετά πράγματα για το πώς είναι δομημένη η βάση στην οποία θέλεις να το εφαρμόσεις. Ας πάρουμε ένα παράδειγμα για να το κατανοήσουμε καλύτερα. Έστω ότι έχω μια βάση που έχει 2 data files και 1 log file, αν και το log δεν με ενδιαφέρει στο συγκεκριμένο τύπο backup το λέω για να μην υπάρχει κάποια παρεξήγηση. Έστω αυτός που σχεδίασε την βάση αποφάσισε να βάλει τους πίνακες που δέχονται τα transactions πχ τιμολόγια, κινήσεις λογιστηρίου στο 2ο data file και όλους τους άλλους στο 1ο data file. Επίσης αυτή η βάση είναι τεράστια και ο χρόνος που χρειάζεται να την πάρεις full backup είναι 10 ώρες (υπερβολικό μεν αλλά το έχω δει σε βάση που είναι κάτι ΤΒ). Σε αυτή την περίπτωση μπορεί να παίρνεις full backup κάθε Κυριακή και κάθε μέρα στις 05:00 κάνεις bakup μόνο το 2ο data file το οποίο περιέχει τις κινήσεις.
    Τώρα κάποιος θα μου πει και το filegroup; Δεν ξέρω αν γνωρίζεται τι είναι το filegroup. Μπορείτε να κοιτάξετε στα BOL του SQL Server αν δεν το ξέρετε, απλά εγώ εδώ θα σας πω ότι είναι λογικές οντότητες μέσα στης οποίες ανήκουν τα data files μιας βάσης. Κάθε filegroup ανήκει σε μια συγκέκριμένη βάση και μπορεί να έχει μέσα του περισσότερα από ένα data files. By default κάθε βάση έχει το primary filegroup στο οποίο ανήκει by default το πρώτο data file της βάσης (.mdf). Εάν στο παραπάνω παράδειγμα αντικαταστήσουμε τα files με filegroup θα έχουμε το ίδιο ακριβώς αποτέλεσμα του backup.
    ΠΡΟΣΟΧΗ ΠΡΟΣΟΧΗ ΠΡΟΣΟΧΗ: ΜΕΤΑ ΑΠΟ ΚΑΘΕ FILE/FILEGROUP BACKUP ΠΑΙΡΝΟΥΜΕ Τ-LOG BACKUP. AYTO EINAI ΑΠΑΡΑΒΑΤΟΣ ΚΑΝΟΝΑΣ. Παίζει με Full & Bulk-Logged Recovery Model.
    Partial Backup
    Στην ουσία είναι το backup κάποιου ή κάποιον filegroup(s). Στο παράδειγμα που σας έδωσα στο file/filegroup backup ας έρθουμε να προσθέσουμε ακόμα ένα data file το οποίο θα ανήκει σε ένα ακόμα filegroup. Έτσι η βάση θα έχει τρία (3) data files έστω τα (a1.mdf,a2.ndf,a3.ndf) και έχω και τρία (3) filegroups το primary που έτσι και αλλιώς το έχω και το fg1 και fg2. Στο primary by default ανήκει το πρώτο data file και έχουμε a2.ndf->fg1 & a3.ndf->fg2. Στο 3ο data file βάζουμε τα δεδομένα που είναι από κλεισίματα προηγούμενων χρήσεων και τα οποία δεν θέλουμε αν τα αλλάζουν οι χρήστες. Αυτό είναι εύκολο να γίνει διότι μπορώ να κάνω το fg2 read-only. Ένα τώρα θελήσω να πάρω partial backup στην ουσία παίρνω full backup απλά προσδιορίζω αν θέλω να πάρω τα read only ή τα read-write filegroups. Παίζει με Full & Bulk-Logged Recovery Model.
    Copy Only
    Από τον SQL Server 2005 και μετά έχουμε διαθέσιμο τον συγκεκριμένο τύπο και ομολογώ ότι ήταν κάτι το οποίο έλειπε από το εργαλείο. Θα καταφύγω πάλι σε παράδειγμα για την κατανόηση του τύπου αυτού. Έστω ότι σε ημερήσια βάση έχω μια πολιτική για το backup σε μια βάση μου που είναι η εξής:
    · Full Backup στις 05:00 πμ
    · Transaction Log Backup κάθε μια ώρα
    Στις 15:00 έρχεται το αφεντικό και μου λέει "Θέλω να πάρεις την βάση έτσι όπως είναι τώρα και να την βάλει στο laptop μου γιατί θέλω να πάω ένα ταξίδι και θέλω να έχω τα δεδομένα μαζί μου". Στις προηγούμενες εκδόσεις είχα θέμα με τέτοιου είδους ερωτήματα. Εάν για παράδειγμα έκανα το λάθος και έκανα full backup στην ουσία κατέστρεφα την αλληλουχία των t-logs backups από τις 15:00 και μετά, διότι αυτά θα είχαν σαν βάση το full backup που μόλις έκανα. Έτσι στην περίπτωση που θα ήθελα να κάνω restore στην συγκεκριμένη ημέρα δεν θα ξεκίναγα από το full backup που πήρα στις 05:00 αλλά από αυτό που πήρα στις 15:00. Εάν ήθελα να γυρίσω πριν τις 15:00 θα ξεκίναγα από των 05:00, αλλά θα έπρεπε να τα κάνω όλα με την σειρά που τα πήρα και φυσικά θα χρειαζόμουν περισσότερο χρόνο. Βέβαια όλα θα ήταν καλά αν το backup στις 15:00 το κρατούσα στο μέσο που παίρνω τα backups μου. Για σκεφτείτε όμως τι θα γίνει αν μετά από την μεταφορά στο laptop του αφεντικού το έσβηνα; Πολύ απλά αν ήθελα να γυρίσω στην συγκεκριμένη μέρα αυτό θα ήταν εφικτό να γίνει αλλά μέχρι τις 14:00 όπου ήταν τα τελευταίο valid t-log backup.
    To Copy Only αυτό ακριβώς το πρόβλημα λύνει. Μπορείς να πάρεις full backup οποιαδήποτε στιγμή μέσα στην ημέρα θέλεις χωρίς να επηρεάζεις την συνέχεια στην πολιτική του backup που έχεις.
    How To Backup Database
    Αφού είδαμε, και ελπίζω να έγιναν κατανοητοί οι διάφοροι τύποι του backup, είναι η στιγμή να δούμε και πως παίρνουμε backup αυτούς. Για να το κάνουμε αυτό μπορούμε να χρησιμοποιήσουμε το SQL Server Management Studio ή T-SQL. Δεν θα δώσω ιδιαίτερη βάση στο περιβάλλον του SSMS διότι κυρίως το χρησιμοποιούμε για να πάρουμε ad-hoc backups. Το T-SQL είναι αυτό που μας χρειάζεται διότι αφού το δοκιμάσουμε μετά το κάνουμε schedule. Ναι ξέρω τι θα μου πείτε ότι μπορώ να το κάνω schedule και μέσα από τον SSMS (Maintenance Plans, κλπ), δεν θα διαφωνήσω μαζί σας απλά πιστεύω ότι ο καλύτερος τρόπος εκμάθησης είναι με T-SQL.
    Backup Devices
    Πριν αρχίσω να σας δείχνω τους τρόπους και τις εντολές backup, θέλω να σας πω μερικά πράγματα σχετικά με τα backup devices στα οποία μπαίνουν τα backups που παίρνουμε. Αυτά μπορεί να είναι είτε στον σκληρό δίσκο σας είτε σε ταινία, θα πρέπει όμως να γνωρίζεται ότι η συσκευή της ταινίας πρέπει να είναι φυσικά συνδεδεμένη στη μηχανή στην οποία έχετε βάλει τον SQL Server. Δεν μπορείς π.χ να πάρεις tape backup σε βάση όταν το tape device είναι συνδεμένο στον domain controler server και έχεις σε άλλο server τον SQL Server. Επίσης για τα disk devices αυτά θα πρέπει να είναι δίσκους που βλέπει φυσικά ο server στον οποίο έχουμε εγκαταστήσει τον SQL Server. Εάν θέλουμε να είναι κάποιος share folder μπορεί να γίνει μόνο με UNC Name και όχι με map drive, και φυσικά το account το οποίο ξεκινάει το sql service να έχει τα απαραίτητα read-write permissions σε αυτό.
    Για να σας κάνω την ζωή ευκολότερη θα σας έλεγα να θεωρήσετε ότι το backup device είναι μια συρταριέρα με πολλά συρτάρια, όπου το κάθε ένα συρτάρι είναι ένα backup που παίρνουμε, οποιουδήποτε τύπου (full, diff, t-log) και από οποιαδήποτε βάση. Το μόνο που θα πρέπει να ξέρω είναι τι έχω σε κάθε συρτάρι, το οποίο φυσικά δεν χρειάζεται να το θυμάμαι μιας υπάρχει τρόπος που θα δούμε παρακάτω πως γίνεται να βρω τι έχω μέσα σε αυτό. Θα σας έδινα όμως μια συμβουλή και αυτή είναι backup device να είναι με backups από μια συγκεκριμένη βάση ώστε να μην χάσετε την μπάλα.
    Υπάρχουν δύο είδη backup devices, τα permanent και τα temporary. Η ουσιαστική διαφορά τους είναι ότι τα permanent τα βλέπεις μέσα από SSMS και μπορείς να τα διαχειριστείς με γραφικό περιβάλλον, ενώ τα temporary μόνο με T-SQL μπορείς να τα διαχειριστείς και φυσικά δεν τα βλέπεις από το SSMS. Υπάρχει ακόμα μία διαφορά σε αυτά και είναι ο τρόπος με τον οποίο δημιουργούνται. Τα permanent δημιουργούνται είτε μέσα από το γραφικό περιβάλλον είτε με T-SQL πριν πάρω κάποιο backup, ενώ τα temporary με την εντολή που θα δώσω για να πάρω backup.
    Για να φτιάξω ένα permanent backup device με το SSMS πάω στο Object Explorer Window > Server Objects > Backup Devices > Right Click > New Backup Device. Στο διάλογο που μας δίνεται δίνουμε το όνομα του device και το destination (disk or tape). Ενώ με T-SQL χρησιμοποιώ την sp_addumpdevice πχ
    EXEC master.dbo.sp_addumpdevice 'disk', 'demodevice', 'C:\....\demodevice.bak'
    GO
    Σημείωση : Μέχρι να βάλω το πρώτο backup στο device αυτό δεν υπάρχει σαν αρχείο στο δίσκο αν είναι τύπου disk.
    Όπως είπα και παραπάνω για να φτιάξω temporary device αυτό γίνεται μόνο με T-SQL και μόνο κατά την στιγμή που πάω να πάρω backup π.χ.
    BACKUP DATABASE demoDB TO DISK='C:\...\tempDevName.bak'
    BACKUP LOG demoDB TO DISK='C:\...\tempDevName.bak'
    BACKUP Statement
    Αντιγραφή από τα SQL Server Books Online.

    Backing Up a Whole Database
    BACKUP DATABASE { database_name | @database_name_var }
    TO [ ,...n ]
    [ ] [ next-mirror-to ]
    [ WITH { DIFFERENTIAL | [ ,...n ] } ]
    [;]
    Backing Up Specific Files or Filegroups
    BACKUP DATABASE { database_name | @database_name_var }
    [ ,...n ]
    TO [ ,...n ]
    [ ] [ next-mirror-to ]
    [ WITH { DIFFERENTIAL | [ ,...n ] } ]
    [;]
    Creating a Partial Backup
    BACKUP DATABASE { database_name | @database_name_var }
    READ_WRITE_FILEGROUPS [ , [ ,...n ] ]
    TO [ ,...n ]
    [ ] [ next-mirror-to ]
    [ WITH { DIFFERENTIAL | [ ,...n ] } ]
    [;]

    Backing Up the Transaction Log (full and bulk-logged recovery models)
    BACKUP LOG { database_name | @database_name_var }
    TO [ ,...n ]
    [ ] [ next-mirror-to ]
    [ WITH { | } [ ,...n ] ]
    [;]
    ::=
    {
    { logical_device_name | @logical_device_name_var }
    | { DISK | TAPE } =
    { 'physical_device_name' | @physical_device_name_var }
    }
    ::=
    MIRROR TO [ ,...n ]
    ::=
    {
    FILE = { logical_file_name | @logical_file_name_var }
    | FILEGROUP = { logical_filegroup_name | @logical_filegroup_name_var }
    }
    ::=
    FILEGROUP = { logical_filegroup_name | @logical_filegroup_name_var }
    [ ,...n ]::=
    --Backup Set Options
    COPY_ONLY
    | { COMPRESSION | NO_COMPRESSION }
    | DESCRIPTION = { 'text' | @text_variable }
    | NAME = { backup_set_name | @backup_set_name_var }
    | PASSWORD = { password | @password_variable }
    | { EXPIREDATE = { 'date' | @date_var }
    | RETAINDAYS = { days | @days_var } }
    --Media Set Options
    { NOINIT | INIT }
    | { NOSKIP | SKIP }
    | { NOFORMAT | FORMAT }
    | MEDIADESCRIPTION = { 'text' | @text_variable }
    | MEDIANAME = { media_name | @media_name_variable }
    | MEDIAPASSWORD = { mediapassword | @mediapassword_variable }
    | BLOCKSIZE = { blocksize | @blocksize_variable }
    --Data Transfer Options
    BUFFERCOUNT = { buffercount | @buffercount_variable }
    | MAXTRANSFERSIZE = { maxtransfersize | @maxtransfersize_variable }
    --Error Management Options
    { NO_CHECKSUM | CHECKSUM }
    | { STOP_ON_ERROR | CONTINUE_AFTER_ERROR }
    --Compatibility Options
    RESTART
    --Monitoring Options
    STATS [ = percentage ]
    --Tape Options
    { REWIND | NOREWIND }
    | { UNLOAD | NOUNLOAD }
    --Log-specific Options
    { NORECOVERY | STANDBY = undo_file_name }
    | NO_TRUNCATE
    Θα σταθώ όμως σε μερικά options του BACKUP statement
    Mirror Backup - Enterprise Edition Only
    Με το mirror κάνω αυτό που λέει και η λέξη με μια κίνηση έχω δύο devices που περιέχουν το ίδιο backup. Αυτά όμως πρέπει να είναι του ίδιου τύπου πχ disk και όχι το ένα disk και το άλλο tape. Αρκετά χρήσιμο διότι έτσι εξασφαλίζω ότι θα έχω περισσότερη ασφάλεια.
    Compression Backup - Enterprise Edition Only
    Πολλές φορές μέχρι τώρα για να εξοικονομήσουμε χώρο στο δίσκο αφού τελειώναμε με το backup μας κάναμε compress το device. Πλέον αυτό μπορεί να γίνει αυτόματα από τον SQL Server. Υπάρχει ένα πέναλτι σε χρησιμοποιούμενους πόρους από το σύστημα αλλά είναι πολύ λιγότεροι από αυτούς που χρησιμοποιούσα όταν έκανα μόνος τη διαδικασία με εξωτερικά εργαλεία. Γενικά θα πρέπει να δεις αν το πέναλτι που έχεις αξίζει να το έχεις. Με αυτό θέλω να πω ότι πρέπει να δεις το πόσο compression ratio και αυτό μπορείς να το δεις με αυτό το query
    SELECT backup_size/compressed_backup_size FROM msdb.dbo.backupset
    Striped Backups
    Είναι η δυνατότητα να έχω το backup σε περισσότερα από ένα backup device (μέχρι 64). Χρήσιμο σε περιπτώσεις που βλέπω ότι έχω μια διαδικασία backup να παίρνει μεγάλο χρόνο πχ 4 ώρες, με αυτό μειωνεται ο χρονος στο μισό αν χρησιμοποιήσω 2 backup devices. Ακόμα μια χρήση του είναι όταν έχω πρόβλημα χώρου είτε με το δίσκο που είναι το device είτε με το χώρο τoυ tape (αρκεί βέβαια να έχω δύο tape device).
    How To Restore Database
    Η διαδικασία του restore χωρίζεται σε τρεις φάσεις:
    1. Data Copy Phase
    2. Redo Phase
    3. Undo Phase and Recovery
    Copy Data Phase
    Είναι η πρώτη φάση σε κάθε restore που κάνουμε. Σε αυτή την φάση γίνονται copy όλα (data, log, indexes) από τα backup της βάσης στα database files. Με το τέλος της φάσης αυτής όλα τα περιέχομενα της βάσης έχουν γίνει reset στα περιεχόμενα του backup που κάνουμε restore. Αυτό γίνεται με τα full και differential backups.
    Redo Phase
    Σε αυτή την φάση γίνονται apply όλα τα logged transactions στα δεδομένα που έγιναν copy στην προηγούμενη φάση. Αυτό γίνεται κατά τη στιγμή που γίνονται restore τα transactions log backup. Στην ουσία κοιτάζει ποια transactions που τα logs έχουν δεν υπάρχουν μέσα στα data files και τα κάνει apply.
    Undo Phase and Recovery
    Με το τέλος της προηγούμενης φάσης το επόμενο βήμα είναι βρει τα transactions τα οποία δεν έχουν γίνει commit και κάνει rollback ότι έχουν αυτά κάνει με σκοπό να κρατηθεί η ακεραιότητα και συνοχή των δεδομένων. Αυτό γίνεται διαβάζοντας τα transaction logs. Αφού τελειώσει με την διαδικασία Undo το επόμενο βήμα είναι να προχωρήσει στην επόμενη εσωτερική εργασία της φάσης αυτή που είναι το Recovery Process με την οποία φέρνει στη βάση σε κατάσταση λειτουργίας (online).
    Τι σημαίνουν οι φάσεις αυτές για την διαδικασία Restore;
    Ας απαντήσουμε σε αυτό το κρίσιμο ερώτημα με ένα παράδειγμα. Έστω ότι σε ημερήσια βάση έχουμε το εξής σενάριο για το backup στην βάση μας.
    · FULL BACKUP στις 00:00.
    · DIFFERENTIAL BACKUP στις 06:00, 12:00, 18:00
    · TRANSACTION LOG BACUP κάθε μία ώρα στις εναπομείνασες ώρες
    Στη παρακάτω εικόνα βλέπουμε το σενάριο μας.

    Η ώρα είναι 19:30 και η βάση μας σκάει….
    Τι πρέπει να κάνουμε restore και πως;
    1. Προσπαθούμε να πάρουμε tail-log backup. Εάν το καταφέρουμε γυρνάμε ακριβώς στο σημείο της πτώσης 19:30. Αλλιώς αναγκαστικά γυρνάμε στις 19:00.
    2. Κάνουμε restore to full backup που έχουμε πάρει τα μεσάνυκτα αλλά χωρίς να ενεργοποιήσουμε τις φάσεις 2 & 3. Αυτό γίνεται βάζοντας στην εντολή RESTORE DATABASE το option NORECOVERY. Αυτό το κάνουμε διότι έχουμε να κάνουμε restore και άλλα backups που μας χρειάζονται μέχρι να φτάσουμε στο επιθυμητό σημείου που είναι το σημείο της πτώσης μιας το full backup περιέχει την εικόνα της βάσης όπως ήταν τα μεσάνυκτα.
    3. Επειδή στο σενάριο μας έχουμε differential backups το επόμενο βήμα είναι να κάνουμε restore το ποιο πρόσφατο που έχουμε και δεν είναι άλλο από αυτό των 18:00 όπου και αυτό θα γίνει χωρίς να ενεργοποιήσουμε τις φάσεις 2 & 3. (WITH NORECOVERY).
    4. Μετά κάνουμε restore το t-log backup που έχουμε μετά και αυτό είναι των 19:00. Εδώ τώρα υπάρχουν δύο δρόμοι.
    a. Ο πρώτος δρόμος είναι αυτός που έχει προκύψει από την περίπτωση να μην έχουμε στα χέρια μας tail-log backup. Σε αυτή την περίπτωση με το τέλος του restore θέλουμε να ενεργοποιηθούν οι φάσεις 2 & 3 μιας και δεν έχω κάτι άλλο να κάνω restore έτσι επιλέγω αυτό να το κατεβάζω με την επιλογή WITH RECOVERY, οπότε στο τέλος της θα έχω Online την βάση μου, αλλά θα έχω χάσει αυτή την μίση ώρα.
    b. Ο δεύτερος είναι αυτό που έχει προκύψει από τη περίπτωση να έχω tail-log backup στα χέρια μου. Οπότε επειδή έχω να κάνω restore και αυτό, το t-log backup των 19:00 το κάνω restore χωρίς να ενεργοποιήσω τις φάσεις 2 & 3.
    5. Εφόσον έχω tail-log backup, το κάνω restore και επιλέγω να γίνουν οι φάσεις 2 & 3 (WITH RECOVERY).
    Από το παραπάνω παράδειγμα γίνεται κατανοητό ότι υπάρχει ένας βασικός κανόνας ο οποίος λέει: "ΚΑΝΩ RESTORE ΟΛΑ ΤΑ BACKUPS ΠΟΥ ΕΧΩ ΜΕ ΝΟRECOVERY ΕΚΤΟΣ ΤΟΥ ΤΕΛΕΥΤΑΙΟΥ ΠΟΥ ΤΟ ΚΑΝΩ ΜΕ RECOVERY, ΧΩΡΙΣ ΟΜΩΣ ΝΑ ΞΕΧΝΑΩ ΠΡΙΝ ΚΑΝΩ RESTORE NΑ ΠΡΟΣΠΑΘΗΣΩ ΝΑ ΠΑΡΩ TAIL-LOG BACKUP".
    Restore Statement
    Αντιγραφή από τα SQL Server Books Online.
    --To Restore an Entire Database from a Full database backup (a Complete Restore):
    RESTORE DATABASE { database_name | @database_name_var }
    [ FROM [ ,...n ] ]
    [ WITH
    {
    [ RECOVERY | NORECOVERY | STANDBY =
    {standby_file_name | @standby_file_name_var }
    ]
    | , [ ,...n ]
    | ,
    | ,
    | ,
    | ,
    } [ ,...n ]
    ]
    [;]

    --To perform the first step of the initial restore sequence
    -- of a piecemeal restore:
    RESTORE DATABASE { database_name | @database_name_var }
    [ ,...n ]
    [ FROM [ ,...n ] ]
    WITH
    PARTIAL, NORECOVERY
    [ , [ ,...n ]
    | ,
    ] [ ,...n ]
    [;]

    --To Restore Specific Files or Filegroups:
    RESTORE DATABASE { database_name | @database_name_var }
    [ ,...n ]
    [ FROM [ ,...n ] ]
    WITH
    {
    [ RECOVERY | NORECOVERY ]
    [ , [ ,...n ] ]
    } [ ,...n ]
    [;]

    --To Restore Specific Pages:
    RESTORE DATABASE { database_name | @database_name_var }
    PAGE = 'file:page [ ,...n ]'
    [ , ] [ ,...n ]
    [ FROM [ ,...n ] ]
    WITH
    NORECOVERY
    [ , [ ,...n ] ]
    [;]

    --To Restore a Transaction Log:
    RESTORE LOG { database_name | @database_name_var }
    [ [ ,...n ] ]
    [ FROM [ ,...n ] ]
    [ WITH
    {
    [ RECOVERY | NORECOVERY | STANDBY =
    {standby_file_name | @standby_file_name_var }
    ]
    | , [ ,...n ]
    | ,
    | ,
    } [ ,...n ]
    ]
    [;]

    --To Revert a Database to a Database Snapshot:
    RESTORE DATABASE { database_name | @database_name_var }
    FROM DATABASE_SNAPSHOT = database_snapshot_name

    ::=
    {
    { logical_backup_device_name |
    @logical_backup_device_name_var }
    | { DISK | TAPE } = { 'physical_backup_device_name' |
    @physical_backup_device_name_var }
    }
    ::=
    {
    FILE = { logical_file_name_in_backup | @logical_file_name_in_backup_var }
    | FILEGROUP = { logical_filegroup_name | @logical_filegroup_name_var }
    | READ_WRITE_FILEGROUPS
    }

    [ ,...n ]::=
    --Restore Operation Options
    MOVE 'logical_file_name_in_backup' TO 'operating_system_file_name'
    [ ,...n ]
    | REPLACE
    | RESTART
    | RESTRICTED_USER
    --Backup Set Options
    | FILE = { backup_set_file_number | @backup_set_file_number }
    | PASSWORD = { password | @password_variable }
    --Media Set Options
    | MEDIANAME = { media_name | @media_name_variable }
    | MEDIAPASSWORD = { mediapassword | @mediapassword_variable }
    | BLOCKSIZE = { blocksize | @blocksize_variable }
    --Data Transfer Options
    | BUFFERCOUNT = { buffercount | @buffercount_variable }
    | MAXTRANSFERSIZE = { maxtransfersize | @maxtransfersize_variable }
    --Error Management Options
    | { CHECKSUM | NO_CHECKSUM }
    | { STOP_ON_ERROR | CONTINUE_AFTER_ERROR }
    --Monitoring Options
    | STATS [ = percentage ]
    --Tape Options
    | { REWIND | NOREWIND }
    | { UNLOAD | NOUNLOAD }
    ::=
    | KEEP_REPLICATION
    ::=
    | KEEP_CDC
    ::=
    | ENABLE_BROKER
    | ERROR_BROKER_CONVERSATIONS
    | NEW_BROKER
    ::=
    | {
    STOPAT = { 'datetime' | @datetime_var }
    | STOPATMARK = { 'lsn:lsn_number' }
    [ AFTER 'datetime' ]
    | STOPBEFOREMARK = { 'lsn:lsn_number' }
    [ AFTER 'datetime' ]
    }
    ::=
    | {
    STOPAT = { 'datetime' | @datetime_var }
    | STOPATMARK = { 'mark_name' | 'lsn:lsn_number' }
    [ AFTER 'datetime' ]
    | STOPBEFOREMARK = { 'mark_name' | 'lsn:lsn_number' }
    [ AFTER 'datetime' ]
    }
    Παράδειγμα Backup
    Ας πάρουμε για παράδειγμα το τελευταίο μας
    · FULL BACKUP στις 00:00.
    · DIFFERENTIAL BACKUP στις 06:00, 12:00, 18:00
    · TRANSACTION LOG BACUP κάθε μία ώρα στις εναπομείνασες ώρες

    1. Φτιάχνω ένα permanent device
    EXEC sp_addumpdevice 'disk','pdev1','c:\temp\pdev1.bak'
    GO
    2. Παίρνω το Full backup στις 00:00
    BACKUP DATABASE BackupDemoDB
    TO PDEV1
    WITH INIT, STATS
    GO
    3. Για το Differential backup το statement είναι
    BACKUP DATABASE BackupDemoDB
    TO PDEV1
    WITH DIFFERENTIAL, STATS
    GO
    3. Για το transaction log backup το statement είναι
    BACKUP LOG BackupDemoDB
    TO PDEV1
    WITH DIFFERENTIAL, STATS
    GO
    Και όπως είπαμε παραπάνω η βάση μου σκάει στις 19:30
    Παράδειγμα Restore
    1. Πρώτη κίνηση είναι να πάρω tail-log backup
    BACKUP LOG BackupDemoDB
    TO PDEV1
    WITH NO_TRUNCATE, STATS
    GO
    2. H επόμενη κίνηση είναι κάνω restore το full backup που έχω από τις 00:00. όμως όλα τα backups τα έχω στο ίδιο backup device. Έτσι πρέπει να μάθω τι θα επιλέξω μέσα από αυτό. Για αυτό το λόγο εκτελώ το statement
    RESTORE HEADERONLY FROM PDEV1
    Από το αποτέλεσμα που θα δω με ενδιαφέρουν τα πεδία BackupType , Position, και DatabaseName για την περίπτωση που έχω backup από διαφορετικές βάσεις στο ίδιο backup device. Εδώ δεν έχουμε.
    Το πεδίο BackupType δείχνει τον τύπο του backup (1=FULL, 5=DIFFERENTIAL, 2=TRANSACTION LOG) και το πεδίο Position την θέση του. Αν θυμάστε πιο πάνω το παράδειγμα με την συρταριέρα είναι ας πούμε το συρτάρι. Έτσι αφού έχουμε τις απαραίτητες πληροφορίες προχωράμε
    3. Κάνουμε restore to full backup με ΝΟRECOVERY μιας και πρόκειτε να κάνουμε restore και άλλα backups. Αλλά για να το επιλέξουμε από το backup device χρησιμοποιούμε το FILE=? όπου αντικαθηστούμε το ? με το αριθμό του Position που έχουμε βρει από το βήμα 2 και στο οποίο αντιστοιχεί το Full backup (BackupType=1)
    RESTORE DATABASE BackupDemoDB FROM PDEV1 WITH FILE=1, NORECOVERY
    4. Κάνουμε restore το differential backup των 18:00
    RESTORE DATABASE BackupDemoDB FROM PDEV1 WITH FILE=19, NORECOVERY
    5. Κάνουμε restore το t-log backup των 19:00
    α. για την περίπτωση που έχουμε tail-log backup από το βήμα 1
    RESTORE LOG BackupDemoDB FROM PDEV1 WITH FILE=20, NORECOVERY
    β. εάν δεν έχουμε tail-log
    RESTORE LOG BackupDemoDB FROM PDEV1 WITH FILE=20, RECOVERY
    6. Εφόσον έχουμε tail-log
    RESTORE LOG BackupDemoDB FROM PDEV1 WITH FILE=21, RECOVERY
    Επίλογος
    Σε αυτό το άρθρο σκοπός μου ήταν να κάνω μια καλή εισαγωγή σε αρεκτό βάθος όμως για το τι είναι backup & restore στον SQL Server. Δεν ασχολήθηκα με κάποια σενάρια πχ file/filegroup backup, όμως είναι κάτι που θα το κάνω σύντομα.
  4. antonch
    Στο 18ο event μας που είχα την ομιλία μου η συζήτηση μας έφερε να μιλήσουμε για το ότι ευτυχώς πλέον δεν υποστηρίζεται η χρήση της BACKUP LOG dbname> WITH TRUNCATE_ONLY.
    Ο φίλος μου, συναγωνιστής μου, Αθανάσιος Κλαδάκης είχε την εύλογη απορία γιατί έγινε αυτό. Είπαμε κάποια πράγματα αλλά επειδή ο χρόνος ήταν περιορισμένος διότι περίμεναν οι μπύρες και η πίτα-πίτσα δεν έμεινα ικανοποιημένος με την απάντηση που έδωσα. Έτσι επανέρχομαι στο θέμα.
    Ο Νάσος είπε ότι σε όσους πελάτες του βλέπει σε κάποια βάση ότι το Transaction Log (TL) έχει γίνει μεγάλο εκτελεί την εντολή αυτή και φέρνει τα πράγματα στα ίσια τους. Μέχρι εδώ σωστά. Όμως αυτό εγκυμονεί κινδύνους. Ο σημαντικότερος από αυτούς είναι ο παρακάτω.
    Έστω ότι έχω εγκαθιδρύσει μια διαδικασία backup στην βάση με την οποία παίρνω και TL Backup. πχ
    Στις 05:00 κάνω Full Backup (FB) και από τις 06:00 παίρνω TL backup.
    Κάποια στιγμή το μεσημέρι βλέπω το TL να είναι μεγάλο και σαν καλό παιδί αποφασίζω να το μικρύνω με την χρήση της παραπάνω εντολής. Όλα πάνε καλά και είμαι ευτυχισμένος. Αυτό ας υποθέσουμε ότι έγινε γύρω στις 12:30.
    Στις 17:30 για κάποιο λόγο σκάει η βάση και πρέπει να κάνω RESTORE. Σύμφωνα με αυτά που ξέρω θα πρέπει να κάνω RESTORE το FB των 05:00 WITH NORECOVERY και στη συνέχεια να κάνω RESTORE όλα τα TL backups με την σειρά που τα πήρα όλα WITH NORECOVERY εκτός του τελευταίου το οποίο είναι στις 17:00 (εκτός και αν έχω πάρει και tail log backup αμέσως μετά από την εμφάνιση του προβλήματος) το οποίο θα πρέπει να γίνει με RECOVERY.
    Όμως σιγά που θα γίνει αυτό που θέλω. Επειδή στις 12:30 έκανα το truncate στο log δεν μπορώ να συνεχίσω στο restore των logs από εκεί και πέρα διότι πολύ απλά δεν έχω τέτοια μιας και με την χρήση της εντολής που συζητάμε έχω καταφέρει να κάνω break το Log Sequence Number (LSN) πάνω στο οποίο βασίζεται η διαδικασία του TL backup.
    Αποτέλεσμα, πάω για κρύες μπύρες, διότι έχω χάσει τα περιεχόμενα του TL από το τελευταίο FB. (Ένα ρίγος περνάει αυτή τη στιγμή την πλάτη μου μόνο και που το σκέφτομαι)
    Αυτό που μέχρι την διακοπή της χρήσης της εντολής η Microsoft συνιστούσε ήταν αμέσως μετά την ολοκλήρωση της εντολής να παίρνουμε FULL BACKUP ώστε τα TL backups να μην σκάνε.
    Σήμερα (SQL Server 2005, 2008, 2008 R2) με την κατάργηση της εντολής αν θέλουμε να κάνουμε αυτό που θέλει να κάνει ο Νάσος σύμφωνα πάντα με την Microsoft θα πρέπει να γυρίσουμε την βάση σε SIMPLE RECOVERY MODEL ώστε να γίνει truncate το log και όταν τελειώσει αυτό να την επαναφέρουμε σε FULL RECOVERY MODEL.
    Ελπίζω να έδωσα τώρα μια πιο κατατοπιστική απάντηση γιατί δεν έχει θέση στον SQL Server η TRUNCATE_LOG.
    Να σημειώσω ότι μπορεί να την δείτε και σαν NO_LOG είναι το ίδιο αλλά με άλλο όνομα, και αυτή πήγε στον Καιάδα.
    Υ.Γ. Δεν ξέρω αν η κοινότητα είναι εξοικειωμένη με τις διάφορες μορφές backup που μπορώ να έχω στον SQL Server. Επειδή μέχρι σήμερα δεν έχω αναφερθεί σε αυτό, θεωρώντας το γνωστό, θα ήθελα να ξέρω αν υπάρχει ενδιαφέρον ώστε να γράψω για αυτό.
  5. antonch
    Τελικά ο Αη Βασίλης ήρθε και για μένα. Για δεύτερη χρονιά μου απονεμήθηκε ο τίτλος του MVP στον SQL Server. Αυτό σημαίνει περισσότερες ευθύνες για μένα, αλλά ευχάριστες.

    Η χρονιά αυτή θα συνεχιστεί με περισσότερη δράση και περισσότερα SQL Saturday Nights.

    Καλή Χρονιά σε όλους και σας ευχαριστώ για την υποστήριξη
  6. antonch
    Ένα όνειρο αρκετών χρόνων αποκτά σάρκα και οστά. Φιλοδοξία μου είναι όταν σας το παρουσιάσω με περιεχόμενο να αποτελέσει μια σοβαρή πηγή γνώσης για τον SQL Server.
    Αυτό που μένει πλέον είναι να βρω το χρόνο και να στρωθώ στην δουλειά ώστε να γίνει παραγματικότητα το όνειρο μου, γιατί είναι ωραίο να έχεις όνειρα και να τα πραγματοποιείς.
  7. antonch
    Ολοκληρώθηκε το meeting και το recording αυτού μπορείτε να το βρείτε στα παρακάτω σήμεια
    https://www311.livemeeting.com/cc/mvp/view?id=DSZT9W http://www.techdays.gr --------------------------------------------------------------------------------------------------------
    Συνεχίζουμε στο ρυθμό μας, ελπίζοντας αυτή η προσπάθεια να σας αρέσει, με το 3ο Saturday SQL Night και θεματολογία Backup & Restore (έπειτα με απαίτηση αρκετών από εσάς ).
    Παρακάτω σας δίνω τα στοιχεία του meeting και ελπίζω να δω τους περισσότερους από εσάς.
    Ημερομηνία & Ώρα : 06/11/2010 22:30 μμ
    Attendee URL: https://www.livemeeting.com/cc/mvp/join?id=DSZT9W&role=attend&pw=p7%28pCxwWq
    Meeting ID: DSZT9W
    Attendee Entry Code: p7(pCxwWq
    Duration: 2 hours
    Μέχρι τότε να είσαι όλοι καλά και να προσέχετε τον εαυτό σας.
  8. antonch
    Χειμώνιασε και νομίζω ότι είναι καιρός να αρχίσουμε .
    Βασανίστηκα αρκετά για να επιλέξω θέμα. Είχα να επιλέξω μεταξύ πολλών θεμάτων. Κάποια ήταν υψηλής δυσκολίας και ίσως φίλοι που δεν έχουν την απαιτούμενη εμπειρία στον SQL Server θα είχαν δυσκολίες στην παρακολούθηση. Κάποια άλλα εύκολα και οι έμπειροι φίλοι να τα έβρισκαν ανιαρά. Δύσκολη απόφαση.
    Έτσι πήρα μια απόφαση που πιστεύω ότι είναι η πιο δίκαιη. Θα ξεκινήσουμε από τα βασικά ώστε μέσα στα πρώτα 4-5 μαθήματα να έρθουν οι «μη έμπειροι» σε μια κατάσταση όμοια με τους «έμπειρους» .
    Στο 1ο μάθημα θα ξεκινήσουμε από την εγκατάσταση του SQL Server και θα φτάσουμε μέχρι την δημιουργία και την αρχιτεκτονική μιας database. Απλό θέμα αλλά νομίζω ότι είναι απαραίτητο για αρχή. Εξάλλου θα είναι και πιλότος για το διαδικαστικό μέρος της προσπάθειας αυτής.
    Παρακάτω θα σας δίνω τα στοιχεία του meeting και ελπίζω να δω τους περισσότερους από εσάς.
    Ημερομηνία & Ώρα : 16/10/2010 22:30 μμ
    Attendee URL: https://www.livemeeting.com/cc/mvp/join?id=ZR29ZM&role=attend&pw=5Kpf%5C%7B22w
    Meeting ID: ZR29ZM
    Attendee Entry Code: 5Kpf\{22w
    Duration: 2 hours
    Μέχρι τότε να είσαι όλοι καλά και να προσέχετε τον εαυτό σας.
  9. antonch
    Μόλις έλαβα ένα request από τον συνάδελφο Γιώργο Σίμο να φτιάξω κάτι για να παίρνει schedule backup στο sql server express, μιας και αυτή η έκδοση δεν έχει τον sql server agent.
    Λοιπόν όποιος θέλει να υλοποιήσει μια τέτοια λύση θα πρέπει να κάνει τα εξής βήματα:
    BHMA 1o
    Αποθηκεύω το παρακατώ script σε ένα αρχείο πχ c:\My SQL Scripts\DBBackupPerDay.sql
    declare @weekday char(3)
    declare @command varchar(2048)
    select @weekday=upper(left(datename(dw,getdate()),3))
    set @command = 'backup database $(dbname) to disk =''$(backupPath)\$(dbname)_'+@weekday+'.bak' + ''' with init'
    exec (@command)

    Εδώ έχω πάρει σαν υπόθεση εργασίας ότι κάθε μέρα θα παίρνουμε ημερήσιο full backup την βάση μας σε ξεχωριστό device, το οποίο στην επόμενη φορά που θα ξαναπάρω backup σε αυτό θα σβήνει το προηγούμενο. Δηλαδή έστω ότι είναι Δευτέρα και παίρνω backup την επόμενη Δευτέρα θα σβηστεί το backup αυτής. Εάν υπάρχει ανάγκη για κάτι διαφορετικό ενημερώστε με να σας δώσω την λύση.

    ΒΗΜΑ 2ο
    Ανοίγουμε τον Windows Task Scheduler και φτιάχνουμε ένα νέο Task (Create Task).

    Δίνουμε όνομα και βάζουμε να τρέχει το συγκεκριμένο με ένα windows account που έχει πρόσβαση στον sql server σαν administrator ( η εύκολη λύση ) ή σαν απλός χρήστης αλλά που το έχουμε δώσει να παίρνει backup databases. Επίσης θα πρέπει να έχει write permissions στο directory στο οποίο πρόκειται να τοποθετήσουμε τα backup μας.
    ΒΗΜΑ 3ο
    Πάμε στο Action Tab και φτίαχνουμε ένα νέο action στο οποίο βάζουμε τα εξής

    στο (1) βάζουμε το SQLCMD.exe μαζί με το full path του λογικά θα είναι το
    "C:\Program Files\Microsoft SQL Server\90\Tools\binn\SQLCMD.EXE"
    στο (2) βάζουμε τα εξής
    /S .\sqlexpress /E /i "c:\My SQL Scripts\DBBackupPerDay.sql" -v dbname=MyDB backuppath=”c:\backups"
    στις dbname, backuppath βάζουμε αντίστοιχα το όνομα της βάσης μας και το directory στο οποίο θέλουμε να αποθηκεύονται αυτά.
    ΒΗΜΑ 4ο
    Τέλος φτιάχνουμε έναν Trigger ( Trigger Tab ) για να πούμε κάθε πότε θέλουμε να γίνεται η εκτέλεση τoυ backup.
    Καλά backup!!!
  10. antonch
    Πρόσφατα αγόρασα ένα netbook για να έχω κάποια πράγματα τα οποία ήθελα μαζί μου και να μην κουβαλάω μεγάλο βάρος. Θέλησα να βάλω Windows 7 αλλά όπως είναι γνωστό dvd αυτά δεν έχουν. Έτσι ψαχνοντας από εδώ και απο εκεί βρήκα την λύση που σας την δίνω εδω http://www.intowindows.com/how-to-install-windows-7vista-from-usb-drive-detailed-100-working-guide/ . Είμαι σίγουρος ότι οι περισσότεροι την ξέρετε αλλα ίσως υπάρχουν κάποιοι που δεν την γνωρίζουν οπότε καλό είναι να την ξέρουν. Βέβαια μπορεί να χρησιμοποιηθεί και για άλλους σκοπούς, ένα bootable USB είναι πάντα χρήσιμο δεν νομίζετε;
  11. antonch
    Admin σε εταιρεία αγοράζει server με δίσκο 250 GB. (το αφήνω ασχολίαστο)

    Σπάει τον δίσκο σε C: & D:

    Στο C: δίνει χώρο 29,2 GB και όλα τα υπόλοιπα στο D:

    Στήνει Windows 2008 R2 και SQL Server 2008 όλα στο C: !!!

    Kαι τον D: τι τον κάνει?????????????????????

    Τον αφήνει έτσι όπως είναι για να παίρνει τα backups (ωχ παναγιά μου το είπα)

    Υ.Γ Δεν είχε άλλου είδους backup.

    Σε συνέχεια απο το #2

    Στην εταιρεία αυτή έρχεται ένας εξωτερικός συνεργάτης. Βλέπει τη κατάσταση και κατεβάζει πρόταση ότι για τα backups και μόνο πρέπει να αγορασθεί ξεχωριστό storage!!!!
  12. antonch
    from msdn
    Accessing SQL Server Databases with PHP Auditing in SQL Server 2008 Best Practices for Data Warehousing with SQL Server 2008 Connectivity Options for Microsoft SQL Server 2008 Integration Services Consolidation Guidance for SQL Server Consolidation Using SQL Server 2008 Cryptography in SQL Server Data Access Tracing in SQL Server 2008 Database Encryption in SQL Server 2008 Enterprise Edition Embedding SQL Server 2008 Express in an Application Engine Separation of Duties for the Application Developer FILESTREAM Storage in SQL Server 2008 Geo-Replication Performance Gains with SQL Server 2008 on Windows Server 2008 High Availability with SQL Server 2008 How to Implement Kerberos Constrained Delegation with SQL Server 2008 Hub-And-Spoke: Building an EDW with SQL Server and Strategies of Implementation Improving Performance with SQL Server 2008 Indexed Views Installing SQL Server 2008 Express Guidance Introduction to Fast Track Data Warehouse Architectures Introduction to New Data Warehouse Scalability Features in SQL Server 2008 Introduction to New T-SQL Programmability Features in SQL Server 2008 Introduction to Spatial Coordinate Systems: Flat Maps for a Round Planet Migrating DTS Packages to Integration Services Migrating to SQL Server from Other Database Products Partitioned Table and Index Strategies Using SQL Server 2008 Plan Caching in SQL Server 2008 Remote BLOB Store Provider Library Implementation Specification Reporting Services SharePoint Integration Troubleshooting Resources for Upgrading to SQL Server 2008 Scaling Up Your Data Warehouse with SQL Server 2008 SQL Server Best Practices - Implementation of Database Object Schemas SQL Server RDL Specification SQL Server 2008 Full-Text Search: Internals and Enhancements Statistics Used by the Query Optimizer in Microsoft SQL Server 2008 Troubleshooting Performance Problems in SQL Server 2008 Using the Microsoft Connector for Oracle by Attunity with SQL Server 2008 Integration Services Using Microsoft Connector for Teradata by Attunity Using the Resource Governor Using SQL Server to Build a Hub-and-Spoke Enterprise Data Warehouse Architecture Using SQL Server 2008 Integration Services with SAP BI 7.0 Using SQL Server 2008 Reporting Services with the.NET Framework Data Provider for Teradata Using SQL Server 2008 Reporting Services with SAP NetWeaver Business Intelligence Using Star Join and Few-Outer-Row Optimizations to Improve Data Warehousing Queries We Loaded 1 Terabyte in 30 Minutes with SSIS, and So Can You
  13. antonch
    Σήμερα είχα μια ωραία ερώτηση από μια αγαπητή συνάδελφο σχετικά με τον τελεστή LIKE. Η ερώτηση της ήταν:

    «Πως μπορώ να έχω την δυνατότητα με τον τελεστή LIKE να έχω όλα τα records που είναι περασμένα μέσα στην βάση μου σε ένα πίνακα ακόμα και στην περίπτωση που έχουν περαστεί κάποιοι χαρακτήρες με ελληνικά και κάποιοι με αγγλικά;»

    Στην ουσία αυτό που ήθελε ήταν αν έχω ένα πεδίο σε ένα πίνακα μου και έχω περάσει κάποια records που σε αυτό να έχω τις τιμές Antonis, Αντonis, Αntώνης εάν δώσω Antonis ή Αντώνης να μου τα φέρνει όλα.

    Αυτό γίνεται εύκολα αρκεί να χρησιμοποιήσεις για κάθε χαρακτήρα της λέξης που βάζει στο Like το pattern με τις αγκύλες στο οποίο ορίζεις τους χαρακτήρες που θα ψάξεις

    Πχ

    Έστω ότι έχω τον παρακάτω πίνακα
    create table tbLike
    ( id int identity(1,1) primary key,
    memo nvarchar(100))
    Στον οποίο περνάω 2 records
    insert into tbLike values ('λαλα'),('λaλa')
    και θέλω να μου φέρει και τα δύο records to query μου θα πρέπει να είναι το παρακάτω
    select * from tbLike where memo like 'λ[aα]λ[aα]'
    Επειδή αυτό θέλω να κατασκευάζεται αυτόματα τις έφτιαξα μια user define function η οποία παίρνει σαν παράμετρο το input του χρήστη και φτιάχνει το pattern me κάποιες παραδοχές όσον αφορά την αντιστοιχία των ελληνικών και αγγλικών γραμμάτων.

    Αυτή είναι η παρακάτω

    (σημείωση: Έχω προτιμήσει να την γράψω με τον απλούστερο τρόπο ώστε να είναι εύκολα μετατρέψιμη ακόμα και από κάποιον που ξέρει τα βασικά σε Τ-SQL)
    1: create function fnGreekEnglishLikeString(@instring nvarchar(100)) returns nvarchar(100)
    2: as
    3: begin
    4: --abcdefghijklmnopqrstuvwxyz
    5: --αβγδεζηθικλμνξοπρστυφχψω
    6:
    7: declare @rv nvarchar(100)
    8: declare @i int = 1
    9: declare @length int
    10: select @length = len(@instring)
    11: declare @s nvarchar(10)
    12: set @rv=''
    13: while @i
    14: begin
    15: set @s=''
    16: select @s=case
    17: when substring(@instring,@i,1) = 'a' or substring(@instring,@i,1) = 'α' then '[aα]'
    18: when substring(@instring,@i,1) = 'b' or substring(@instring,@i,1) = 'β' then '[bβv]'
    19: --when substring(@instring,@i,1) = 'c' or substring(@instring,@i,1) = '.' then '[bβ]'
    20: when substring(@instring,@i,1) = 'd' or substring(@instring,@i,1) = 'δ' then '[dδ]'
    21: when substring(@instring,@i,1) = 'e' or substring(@instring,@i,1) = 'ε' then '[eε]'
    22: when substring(@instring,@i,1) = 'f' or substring(@instring,@i,1) = 'φ' then '[fφ]'
    23: when substring(@instring,@i,1) = 'g' or substring(@instring,@i,1) = 'γ' then '[gγ]'
    24: when substring(@instring,@i,1) = 'h' or substring(@instring,@i,1) = 'β' then '[hηiι]'
    25: when substring(@instring,@i,1) = 'i' or substring(@instring,@i,1) = 'ι' then '[iιhη]'
    26: --when substring(@instring,@i,1) = 'j' or substring(@instring,@i,1) = 'β' then '[bβ]'
    27: when substring(@instring,@i,1) = 'k' or substring(@instring,@i,1) = 'κ' then '[kκ]'
    28: when substring(@instring,@i,1) = 'l' or substring(@instring,@i,1) = 'λ' then '[lλ]'
    29: when substring(@instring,@i,1) = 'm' or substring(@instring,@i,1) = 'μ' then '[mμ]'
    30: when substring(@instring,@i,1) = 'n' or substring(@instring,@i,1) = 'ν' then '[nν]'
    31: when substring(@instring,@i,1) = 'o' or substring(@instring,@i,1) = 'ο' then '[oοω]'
    32: when substring(@instring,@i,1) = 'p' or substring(@instring,@i,1) = 'π' then '[pπ]'
    33: --when substring(@instring,@i,1) = 'q' or substring(@instring,@i,1) = 'β' then '[bβ]'
    34: when substring(@instring,@i,1) = 'r' or substring(@instring,@i,1) = 'ρ' then '[rρ]'
    35: when substring(@instring,@i,1) = 's' or substring(@instring,@i,1) = 'σ' or substring(@instring,@i,1) = 'ς' then '[sσς]'
    36: when substring(@instring,@i,1) = 't' or substring(@instring,@i,1) = 'τ' then '[tτ]'
    37: when substring(@instring,@i,1) = 'u' or substring(@instring,@i,1) = 'υ' then '[uυy]'
    38: when substring(@instring,@i,1) = 'v' or substring(@instring,@i,1) = 'β' then '[vβb]'
    39: --when substring(@instring,@i,1) = 'w' or substring(@instring,@i,1) = 'β' then '[bβ]'
    40: when substring(@instring,@i,1) = 'x' or substring(@instring,@i,1) = 'χ' then '[xχ]'
    41: when substring(@instring,@i,1) = 'y' or substring(@instring,@i,1) = 'υ' then '[uυy]'
    42: when substring(@instring,@i,1) = 'z' or substring(@instring,@i,1) = 'ζ' then '[zζ]'
    43: else '['+substring(@instring,@i,1)+']'
    44: end
    45: set @rv=@rv+@s
    46: set @i+=1
    47: end
    48: return @rv
    49: end
    50: go
    Εάν κάποιος θέλει να δει το αποτέλεσμα που επιστρέφει η συγκεκριμένη function αρκεί να εκτελέσει το παρακάτω query
    select dbo.fnGreekEnglishLikeString('antonis')
    go
    Εάν τώρα θέλει να το ενσωματώσει στο query του
    select * from tbLike
    where memo like dbo.fnGreekEnglishLikeString('antonis')
    go
    Happy T-SQL Programming
  14. antonch
    Επειδή μάλλον το πρώτο άρεσε είπα να κάνω και το δεύτερο σύντομα.
    Έτσι λοιπόν θα συνεχίσουμε από εκεί που μείναμε δηλαδή την δημιουργία μιας database με πολλά tips για αυτούς που θέλουν να μάθουν να σχεδιάζουν σωστά μια database στον SQL Server.
    Φυσικά αυτό από πίσω κρύβει αρκετή δόση αρχιτεκτονικής.
    Παρακάτω θα σας δίνω τα στοιχεία του meeting και ελπίζω να δω τους περισσότερους από εσάς.
    Ημερομηνία & Ώρα : 23/10/2010 22:30 μμ
    Attendee URL: https://www.livemeeting.com/cc/mvp/join?id=WP7JZ9&role=attend&pw=s%26Dr5%29JWD
    Meeting ID: WP7JZ9
    Attendee Entry Code: s&Dr5)JWD
    Duration: 2 hours
    Μέχρι τότε να είσαι όλοι καλά και να προσέχετε τον εαυτό σας.
    Εδώ θα βρείτε την μαγνητοσκοπημένη παρουσίαση
  15. antonch
    Χαίρετε, καιρό είχατε να με ακούσετε ε;
    Δυστυχώς αυτά συμβαίνουν όταν αλλάζεις δουλειά.
    Όμως σιγά σιγά βρίσκω τα νέα βήματα μου οπότε επανέρχομαι δριμύτερος.
    Σήμερα θέλω να σας κουράσω με κάτι που δεν είναι στο administration του SQL Server αλλά στο programming του. Αυτό ακούει στο όνομα Stored Procedures.
    Είμαι σίγουρος ότι αν όχι όλοι οι περισσότεροι τις ξέρετε. Είμαι σίγουρος ότι υπάρχουν φανατικοί υποστηρικτές τους, όπως επίσης και άλλοι που όταν ακούνε το όνομα τους βγάζουν σπυράκια.
    Εάν έχετε ποτέ εμπλακεί σε μια τέτοια κουβέντα, σίγουρα θα έχετε πάρει το μέρος κάποιας πλευράς. Όπως συμβαίνει όμως πάντα σε αυτή τη ζωή η αλήθεια είναι πάντα στην μέση. Ίσως σε αυτή την περίπτωση να γέρνει λίγο περισσότερο στην μία μεριά αλλά καλύτερα αυτό να το αποφασίσετε εσείς μετά από όλα όσα σας πω σε αυτό το post μου.
    Μια σειρά από αλήθειες και μύθους έχουν φτιάξει την διαμάχη αυτή. Νομίζω όμως ότι είναι καιρός να βάλουμε τα πράγματα στην θέση τους όπως ακριβώς έχουν.
    Οφείλω να ομολογήσω ότι και εγώ προσωπικά έχω εμπλακεί σε αυτή την διαμάχη, όπως οφείλω να ομολογήσω ότι είμαι θερμός υποστηρικτής της μίας εκ των δύο.
    Ας πάρουμε τα πράγματα από την αρχή.
    Μία Stored Procedure είναι ένα σύνολο από T-SQL statements τα οποία είναι compiled μέσα σε ένα single execution plan.
    Επειδή ενδεχομένως κάποιος συνάδελφος να ρωτήσει τι είναι το execution plan παραθέτω το παρακάτω link για περισσότερη μελέτη.
    http://msdn.microsoft.com/en-us/library/ms181055.aspx
    Με την χρήση των SPs μπορώ να μεταφέρω την λογική που θα έγραφα μέσα σε κάποια εφαρμογή η οποία έχει να κάνει με τα δεδομένα πάνω στον SQL Server, στο σημείο δηλαδή που τα δεδομένα υπάρχουν. Απλά από την εφαρμογή μου καλώ την SP.
    Το παραπάνω αποτελεί ένα από τα βασικά σημεία τριβής των εμπλεκομένων στην διαμάχη αυτή.
    Οι καταγγέλλοντες τις SP λένε: "Με αυτό τον τρόπο έχω σπάσει το business logic σε πολλά διαφορετικά σημεία (data tier, middle tier) και έτσι μου γίνεται δύσκολη η ζωή στην περίπτωση που θέλω να το αλλάξω γιατί απλά δεν θα ξέρω που θα το βρω. Επίσης δεν μπορώ να έχω κάποιο έτοιμο documentation όπως αν θα το έκανα μέσα από το Visual Studio με τα διάφορα εργαλεία που αυτό έχει. Άσε που με T-SQL δεν μπορώ να κάνω όλα όσα μπορώ να κάνω με C# π.χ. Και εντάξει να κάνω χρήση των SPs αλλά τι θα γίνει στην περίπτωση που ο SQL Server είναι εξαντλημένος από resources; Η/Οι SP(s) δεν θα ανταποκρίνονται για αυτό κάνω το query/ τα queries μου και φέρνω τα δεδομένα στο middle ¨η στον client και κάνω μια χαρά την δουλειά μου."

    Οι υποστηρικτές λένε: "Μα τι λες τώρα. Ίσα ίσα αυτό είναι καλό διότι η λογική μπορεί εύκολα να μοιρασθεί σε περισσότερες εφαρμογές μια και δεν θα χρειασθεί να γραφτεί ξανά μέσα σε αυτές με τον κίνδυνο του λάθους να καραδοκεί. Επίσης επειδή η SP εκτελείται στον SQL Server στο σημείο που είναι τα δεδομένα έχω καλύτερο performance και φυσικά αποσυμφορίζω το δίκτυο από traffic να μεταφέρω τα δεδομένα από τον database server στον application server και σε κάποιες περιπτώσεις και στον client με σκοπό να γίνει η επεξεργασίας τους και μετά να τα επιστρέφω στον database server, αλλά και τον client από φόρτο εργασίας."
    Και τώρα μάλιστα, τα πιάσαμε τα λεφτά μας. Ποιος από τους δύο έχει δίκιο;
    Εδώ λοιπόν θα βρούμε μερικούς μύθους και μερικές αλήθειες.
    ΜΥΘΟΣ/ΑΛΗΘΕΙΑ 1.
    Όντως η T-SQL δεν είναι τόσο δυνατή γλώσσα όπως η C#, αλλά ο σκοπός της δεν είναι να κάνει ότι κάνει η C#. Ο σκοπός της είναι να ασχοληθεί με τα δεδομένα και σε αυτό είναι η τέλεια γλώσσα.
    ΜΥΘΟΣ/ΑΛΗΘΕΙΑ 2.
    Όντως η δυνατότητα για documentation που δίνει ο SQL Server είναι αρκετά φτωχή σε σχέση με τα XML Remarks που έχω στην C#. Όμως έχω το Microsoft Visio, το Microsoft Visual Studio Database Developer Team Suite, και φυσικά μια πληθώρα από άλλα εργαλεία πχ το SQL Toolbelt της Red-Gate ή τα εργαλεία της Apex.
    ΜΥΘΟΣ/ΑΛΗΘΕΙΑ 3.
    "Με αυτό τον τρόπο έχω σπάσει το business logic σε πολλά διαφορετικά σημεία (data tier, middle tier) και έτσι μου γίνεται δύσκολη η ζωή στην περίπτωση που θέλω να το αλλάξω γιατί απλά δεν θα ξέρω που θα το βρω."
    Όντως αυτό είναι ένα πιθανό σενάριο που όμως θα συμβεί από κακό documentation ή κακό σχεδιασμό. Δηλαδή όταν έχει στο middle tier X τον αριθμό από classes, web services κ.λ.π. δεν έχει σπάσει την λογική σε Ν σημεία; Εκτός και αν μιλάμε για Client-Server αρχιτεκτονική όπου όλα τα έχει στον client (Fat client), όπου αφού κάνει την αλλαγή θα πρέπει να την κάνει deploy σε n clients με ότι αυτό συνεπάγεται από χρόνο και κόστος.
    ΜΥΘΟΣ/ΑΛΗΘΕΙΑ 4.
    "Επίσης επειδή η SP εκτελείται στον SQL Server στο σημείο που είναι τα δεδομένα έχω καλύτερο performance και φυσικά αποσυμφορίζω το δίκτυο από traffic να μεταφέρω τα δεδομένα από τον database server στον application server και σε κάποιες περιπτώσεις και στον client με σκοπό να γίνει η επεξεργασίας τους και μετά να τα επιστρέφω στον database server, αλλά και τον client από φόρτο εργασίας."

    "Και εντάξει να κάνω χρήση των SPs αλλά τι θα γίνει στην περίπτωση που ο SQL Server είναι εξαντλημένος από resources; Η/Οι SP(s) δεν θα ανταποκρίνονται για αυτό κάνω το query/ τα queries μου και φέρνω τα δεδομένα στο middle ¨η στον client και κάνω μια χαρά την δουλειά μου."

    Οι υποστηρικτές σε αυτό το σημείο έχουν δίκιο όσο και οι καταγγέλλοντες που μιλάνε για την περίπτωση που ο SQL Server εξαντληθεί από resouces. Αυτό όντως θα γίνει αν ακολουθήσουμε την γενικότερη λογική των δεύτερων δλδ όχι SPs μόνο queries. Βέβαια υπάρχει και η περίπτωση που έχουμε κάνει λάθος εκτίμηση αναγκών, δηλαδή εκτιμήσαμε λιγότερο φόρτο εργασίας από αυτόν που έχουμε, ή αυτός που έγραψε την SP δεν είχε ιδέα από SQL άρα έγραφε όπως ήθελε με αποτέλεσμα να ζητάει το σύμπαν από resources. Αν τίποτα από όλα αυτά δεν συμβεί τότε NAI κερδίζω σε performance όπως επίσης και σε network traffic.
    Ας δούμε όμως τι κερδίζω από την χρήση των SPs σε σχέση με την χρήση των προγραμμάτων που εκτελούν μέσα τους T-SQL.
    Το αντιγράφω όπως είναι από τα BOL του SQL Server, δεν θέλω να το αλλάξω για να μην υπάρξει έστω και η παραμικρή αλλοίωση του νοήματος.
    1. They are registered at the server.
    2. They can have security attributes (such as permissions) and ownership chaining, and certificates can be attached to them.
    3. Users can be granted permission to execute a stored procedure without having to have direct permissions on the objects referenced in the procedure.
    4. They can enhance the security of your application.
    5. Parameterized stored procedures can help protect your application from SQL Injection attacks.
    6. They allow modular programming.
    7. You can create the procedure once, and call it any number of times in your program. This can improve the maintainability of your application and allow applications to access the database in a uniform manner.
    8. They are named code allowing for delayed binding.
    9. This provides a level of indirection for easy code evolution.
    10. They can reduce network traffic. An operation requiring hundreds of lines of Transact-SQL code can be performed through a single statement that executes the code in a procedure, rather than by sending hundreds of lines of code over the network.
    Προσωπικά δεν μπορώ να φανταστώ μια εφαρμογή που δεν χρησιμοποιεί SPs.
    Βέβαια ξέρω τι θα μου πει η άλλη πλευρά.
    "Ναι αλλά θέλω να έχω την δυνατότητα να μιλάω από την εφαρμογή μου με τα διάφορα ORM tools (Object Relational Models) όπως LINQ, Entity Framework και οι SPs δεν μου δίνουν όλα μου δίνουν οι πίνακες". Η απάντηση μου είναι ότι δεν είναι ακριβώς έτσι τα πράγματα, σίγουρα υπάρχουν κάποια πράγματα τα οποία χάνεις όμως η εφαρμογή αγαπητοί μου συνάδελφοι δεν είναι μόνο User Interface και δεν μπορώ να θυσιάζω τα πάντα για αυτό.
    Εξάλλου εάν έχω distributed εφαρμογές όλα αυτά τα εργαλεία που πραγματικά είναι ΦΟΒΕΡΑ δεν έχουν νόημα χρήσης στο UI μιας και μεσολαβεί το middle το οποίο είναι αυτό το οποίο κάνει την επικοινωνία με την βάση. Άρα η χρήση τους περιορίζεται σε αυτό και με άλλους τρόπους (δικά μας object, object lists) μεταφέρουμε τα δεδομένα στο UI.
    Ελπίζω να σας έπεισα και να κάνετε χρήση των SP.
  16. antonch
    Πρόσφατα έπεσα μούρη με μούρη σε μια παρουσίαση του Kevin Cox που είχε το παραπάνω θέμα. Επειδή είναι αρκετά ενδιαφέρουσα και ρίχνει αρκετούς μύθους αποφάσισα να την μοιραστώ μαζί σας.
    Ας ξεκινήσουμε με μερικά στατιστικά
    Category Metric Largest single database
    70 ΤΒ Largest table
    20 ΤΒ Biggest total data 1 application
    88 PB Highest database transactions per second 1 db (from Perfmon)
    130.000 Fastest I/O subsystem in production (SQLIO 64k buffer)
    18 GB/sec Fastest “real time” cube
    5 sec latency data load for 1TB
    20 minutes Largest cube
    12 TB Ας προχωρήσουμε στα πιο ειδικά
    OLTP Systems
    MySpace
    500+ SQL Servers, adding new ones every week Total data managed > 1 PB Data Dependent Routing, Distributed Partitioned Views, Replication, SODA Currently moving to Windows 2008 / SQL 2008 Data Dependent Routing, Distributed Partitioned Views, Replication, Caching Tier, Service Broker 500,000 Users: A Simple Architecture Stumbles (two Web servers talking to a single database server – 3 database servers (1 write, 2 read)) 1 Million Users: Vertical Partitioning Solves Scalability Woes (separate databases for parts of the Web site that served different functions) 3 Million Users: Scale-Out Wins Over Scale-Up (cost for scale-up too high), SODA 9 Million Users: Site Migrates to ASP.NET, Adds Caching Tier, Data Dependent Routing 26 Million Users: MySpace Embraces 64-Bit Technology, SQL Server 2008, Service Broker High Availability Windows Clusters 7+1, moving to 10+1 Unattended patching via custom Powershell scripts and management control screen Extensive testing before patching Continually improving operations process 3,000 – 7,000 connections average 2 data centers Doing SAN snapshots between sites In case of loss of one site, can bring up other site to take over within a few hours. Great article on architecture changes as they grew: http://www.baselinemag.com/article2/0,1540,2082921,00.asp Bwin.com
    Online gaming applications - Europe‘s largest betting line-up Sports Poker Casino Skill Games 90 different sports covered in 22 languages > 12,000 different bets offered per day > 3 million individual and combination bets placed every day Bwin.com sponsors top world soccer teams Real Madrid AC Milan FC Bayern Munich Key Technologies Running on SQL Server 2008 & Windows 2008 Enterprise Windows Communication Foundation Synchronous database mirroring between two centers 12 km apart Added 1 ms delay on transaction 99.99x% availability @ 24 x 7 since migrating to SQL from Oracle. 100.00% uptime in 2008 and 2009 (since moving to SQL 2008 and Windows 2008) Zero data loss (financial transactions are involved) Replication and Log shipping for most databases DB Mirroring for betting data base. Full suite of SQL products - IS, AS and RS ASP.NET for applicatio Some numbers Peak financial transactions 6000 per second Peak db transactions 30,000 per second Databases 800+ Instances 100+ Largest table 2 billion rows Total data in SQL Server 100+ TB Backup of 2 TB over network under 1 hr Largest machines 64 core 512 GB IA2 HP 6 x 32 core IA2 400% boost in performance on 128 cores 256GB RAM on IA64 using SQL 2008 R2 More info http://sqlcat.com/whitepapers/archive/2009/08/13/a-technical-case-study-fast-and-reliable-backup-and-restore-of-a-vldb-over-the-network.aspx http://www.microsoft.com/casestudies/Case_Study_Detail.aspx?casestudyid=4000001470 Retail Application
    1,200 stores SQL Standard 10 GB average 15,000 cash registers SQL Express 10 MB database average 1 Corporate Server SQL Enterprise Windows Cluster + DB Mirroring AS, IS, RS 12+ TB database, 1.5 TB cube. Merge replication for products & pricing Service Broker for all transactions One of the largest Service Broker and replication projects in the world 2 Million SKUs (products) 25 Million Accounts 10 million transactions / day SQL Server, AS, IS, RS Windows Cluster, DB Mirroring, Log Shipping

    DATA WAREHOUSEs

    Pan Starrs Project
    Largest Astronomy project in history 4 telescopes capturing 1.5 giga pixel images 100TB on single instance (5 db x 20TB) Total data managed > 1PB 5+TB added per day HA/DR Relying on backups of the input files for now Telecom
    CDR Analytics 70TB Relational 4TB largest cube 100+ concurrent queries Itanium 64 core with storage system rated over 20GB/sec throughput Loading 1TB in Processing 1m rec/sec in AS cubes Hilton Hotels
    Room forecasting system Full suite of SQL products (SQL, AS, IS, RS) Scale out AS and RS Load Balanced Analysis Services reader machines 40 to 50 concurrent users per RS server Complex queries Large data sets returned to many clients IBM xSeries and IBM Blade Center servers Case study: http://www.microsoft.com/casestudies/casestudy.aspx?casestudyid=49192 Stein Mart
    First Fast Track case study Saved $50,000 / month after AS/400 migration Faster results – 3 hours of processing instead of 14 hours Less people to maintain Users love the new tools! 4 TB data warehouse http://www.microsoft.com/casestudies/Case_Study_Detail.aspx?casestudyid=4000007013
  17. antonch
    Ίσως όσα θα αναφερθούν παρακάτω να είναι γνωστά, και το post αυτό να μην είναι ενδιαφέρον. Όμως έχω την υποχρέωση να τα αναφέρω ξανά γιατί θεωρώ ότι είναι πράγματα στα οποία δεν δίνουμε ιδιαίτερη σημασία και τα οποία όταν διογκώνονται είναι δύσκολα στην επίλυση τους.
    Θα μιλήσουμε εδώ γιa την αρχιτεκτονική μιας database στον SQL Server, από τι αποτελείτε μια database, πιο είναι το ιδανικό αρχικό μέγεθος δημιουργίας της, τι πολιτική να ορίσω για το growth της.
    Μια database στον SQL Server αποτελείτε από τουλάχιστον 2 αρχεία. Το ένα από αυτά είναι το data αρχείο και το άλλο είναι το log αρχείο. Εάν δεν ορίσω κάτι συγκεκριμένο κατά την δημιουργία της database, αυτή είναι ένα πιστό αντίγραφο της model database που έχει ο SQL Server. Δηλαδή έχει όλα τα περιεχόμενα και το μέγεθος της model database. Γενικότερα να γνωρίζεται ότι κάθε φορά που φτιάχνεται μια database αυτή είναι ένα αντίγραφο της model database τουλάχιστον ως προς τα περιεχόμενα της, μιας και κατά την δημιουργία έχουμε την δυνατότητα να αλλάξουμε το μέγεθος, το location που θα είναι τα αρχεία της database και ένα σωρό άλλα options και παραμέτρους.
    Αν αναρωτιέστε ποια είναι τα περιεχόμενα της model database, αυτά είναι τα database system catalogs και τυχόν δικά μας objects όπως tables, views, stored procedures κλπ που έχουμε φτιάξει στην model με σκοπό να τα έχουμε διαθέσιμα στις νέες δικιές μας databases. (Παρατήρηση: Εάν έχω ήδη δικίες μου databases και φτιάξω στην model ένα νέο δικό μου object (πχ table) τότε αυτό δεν θα πάει και στις ήδη υπάρχουσες αλλά μόνο στις νέες).
    Όπως είπα και πιο πάνω μια database έχει τουλάχιστον 2 αρχεία το data file και το log file. Μια database μπορεί να έχει μέχρι 32.767 αρχεία. Το μέγεθος της μπορεί να φτάσει στα 524.258 ΤΒ για τις εκδόσεις 2005 και 2008 και στα 1.048.512 ΤΒ για τις εκδόσεις 2000 και 7.0. Κάθε data file δεν μπορεί να είναι μεγαλύτερο από 16 ΤΒ για τις εκδόσεις 2005 και 2008 και 32 ΤΒ για 2000 και 7.0, ενώ το κάθε log file δεν μπορεί να είναι μεγαλύτερο των 2 ΤΒ για τις εκδόσεις 2005 και 2008, 32 ΤΒ στην έκδοση 2000 και 4 ΤΒ στην έκδοση 7.0.
    Όταν δημιουργούμε μια database συνήθως ορίζουμε το αρχικό μέγεθος δημιουργίας της, τόσο για το data όσο και για το log file. O χώρος αυτός καταλαμβάνεται άμεσα από το δίσκο μας. Δηλαδή αν φτιάξω μια βάση με 10 ΜΒ data file και 3 ΜΒ log file τότε έχω μείον 13 ΜΒ από τα διαθέσιμα του δίσκου μου.
    Κάθε data file χωρίζεται εσωτερικά σε σελίδες (pages) των 8ΚΒ (8.192 Β), στις οποίες αποθηκεύονται τα δεδομένα που έχω στην βάση (data, metadata, indexes). Κάθε 8 συνεχόμενες σελίδες μου κάνουν ένα extent (64 ΚΒ ήτοι 16 extents / 1 ΜΒ). Υπάρχουν δύο είδη extent τα uniform (και οι 8 σελίδες ανήκουν στον ίδιο object πχ. Table) και τα mixed (οι 8 σελίδες δεν ανήκουν στο ίδιο object αλλά σε περισσότερα από ένα) .
     
    Σε κάθε data file οι πρώτες 8 σελίδες είναι εξ ορισμού δεσμευμένες από κάποιες ειδικού χειρισμού σελίδες που στην ουσία κάνουν trace τον ελεύθερο και δεσμευμένο χώρο σε αυτό. Αυτές είναι οι File Header (FH), Page Free Space (PFS), Global Allocation Map (GAM), Shared Global Allocation Map (SGAM) (περισσότερα)
    Κάθε σελίδα όπως είπα και πιο πάνω είναι 8 ΚΒ. Από αυτά τα 96 ΚΒ είναι ο page header στον οποίο είναι αποθηκευμένες οι εξής πληροφορίες page number, page type, το ποσό του free space στην σελίδα, και το allocation unit ID του object στο οποίο ανήκει η σελίδα. Με το που βάζω το πρώτο record αυτό μπαίνει αμέσως μετά από τον header και στο τέλος της σελίδα υπάρχει το record offset το οποίο μπαίνει με την αντίστροφη σειρά της εισαγωγής. Άρα ο πραγματικός ωφέλιμος χώρος που έχω πάνω σε κάθε σελίδα είναι 8060 bytes. Αυτό είναι και το μέγιστο record length που μπορώ να έχω πάνω σε ένα πίνακα. Δηλαδή εάν έχω ένα πίνακα που το record length του είναι 100 bytes αυτό σημαίνει ότι σε κάθε σελίδα χωράνε 80 records, και αν ο πίνακας έχει 200 εγγραφές τότε θέλω 3 σελίδες για να αποθηκευτούν τα δεδομένα του. (περισσότερα). Επίσης θα πρέπει να το τονίσω σε αυτό το σημείο ότι ένα record ανήκει πάντα σε μια σελίδα, δεν υπάρχει ποτέ περίπτωση να είναι το μισό σε μια και το άλλο σε άλλη. Θα προλάβω μερικούς συναδέλφους που θα πουν για τα datatypes varchar(max), nvarchar(max), text, ntext, images, varbinary(max) ότι είναι σε άλλες σελίδες αλλά είναι εκεί λόγω μεγέθους.
    Αυτό που σας έχω αναφέρει μέχρι τώρα είναι αρκετά σημαντικό για τον σχεδιασμό των tables, indexes σε μια database στον SQL Server. Διότι κυρίες και κύριοι συνάδελφοι εάν κάποιος αρχίζει και βάζει πεδία μέσα στον πίνακα (μέχρι 1024 πεδία μπορεί να έχει ένας πίνακας) και φτιάξει ένα record length 5000 bytes αυτό σημαίνει ότι μέσα σε μια σελίδα μπαίνει ένα και μόνο ένα record και μένουν ανεκμετάλλευτα 3000 bytes. Εάν λοιπόν ο πίνακας μου έχει 10.000 records σημαίνει ότι θέλω και 10.000 σελίδες που στην κάθε μία χάνω 3.000 bytes, άρα 3.000 bytes x 10.000 σελίδες = 30.000.000 bytes / 1024 bytes = 29296 KB / 1024 KB=28 MB χαμένου χώρου στον δίσκο, μιας και αν θεωρήσουμε ότι υπάρχει μόνο αυτός ο πίνακας στην βάση μας (κάτι φυσικά που δε συμβαίνει στην πραγματικότητα) το data file μας έχει δεσμεύσει χώρο στο δίσκο ίσο με 78 ΜΒ. Όπως γίνεται άμεσα κατανοητό ο κακός σχεδιασμός επηρεάζει άμεσα και το performance πώς? η απάντηση σε λίγο.
    Επίσης όταν δημιουργούμε μια database σκόπιμο θα είναι να έχουμε κάνει μια εκτίμηση για το μέγεθος για τους πρώτους 3 μήνες της ζωής της ώστε να δεσμεύσουμε τον χώρο αυτό κατά την στιγμή της δημιουργίας της. Ο σκοπός του να γίνει κάτι τέτοιο είναι σημαντικός διότι αν δώσουμε κάτι το οποίο είναι μικρό και σε συνδυασμό με το τι έχουμε ορίσει σαν file growth policy, που συνήθως το αφήνουμε μικρό, και έχουμε μεγάλο αριθμό από transactions αυτό θα μας οδηγήσει μαθηματικά στο να έχουμε συνεχόμενο ΙΟ στον δίσκο μας επειδή ο SQL Server θα αιτείτε συνέχεια επιπλέον χώρο σε αυτόν μέσω του λειτουργικού συστήματος καθώς η database θα γεμίζει συχνότερα. Αυτό είναι καταστροφικό σε συστήματα που έχουν πολλά transactions.
    Πριν προχωρήσω στην διαδικασία θα επισημάνω την λέξη εκτίμηση. Αυτό σημαίνει ότι μετά από πάροδο μερικών ημερών κάνω έναν έλεγχο για να δω αν η εκτίμηση μου είναι σωστή και αναλόγως πράττω, μεγαλώνω ή μικραίνω την database. Τώρα πως κάνουμε την εκτίμηση αυτή.
    Πρώτα από όλα επιλέγουμε μια χρονική περίοδο για την εκτίμηση μας πχ 1 μήνα , 2 μήνες κλπ ανάλογα με το είδος της εφαρμογής που χρησιμοποιεί την database, πχ σε ένα ERP θα μπορούσαμε διαλέξουμε 3 μήνες.
    Έπειτα βρίσκω το record length του κάθε πίνακα αυτό το πολλαπλασιάζω με τον αριθμό των records που εκτιμώ ότι θα μπουν στην χρονική περίοδο που έχω διαλέξει. Το αποτέλεσμα τα κάνω σελίδες και τις σελίδες τις κάνω MB. Το ίδιο αλλά κάπως διαφοροποιημένο το κάνω και για τους indexes. Το άθροισμα όλων αυτών μου δίνει το αρχικό μέγεθος δημιουργίας της database μου.
    Δεν θα το αναλύσω περισσότερο εδώ απλά θα σας παραπέμψω στα books online όπου έχει αναλυτικά όλη την διαδικασία. Απλά κάντε αναζήτηση για Estimating the Size of a Database.
    Μέχρι τώρα δεν έχω αναφέρει τίποτα για το άλλο αρχείο που έχει μια database, το log αρχείο. Προσωπικά πιστεύω ότι είναι το σημαντικότερο αρχείο σε μία database. Σε αυτό καταγράφονται όλα εκτός από τα select statements και τα blob πεδία αν και αυτά έχω την επιλογή να πω να καταγράφονται. Συνήθως το αρχικό μέγεθος δημιουργίας του είναι το 30% του αρχικού μεγέθους του ή των data αρχείου (αρχείων). Δηλαδή αν έχω 100 ΜΒ data φτιάχνω log ίσο με 30ΜΒ. Βέβαια αυτό αν και είναι κανόνας, πάντα υπάρχει περιθώριο καταστρατήγησης του, όπως για παράδειγμα αν έχω heavily transactional databases, όπου σε αυτές τις περιπτώσεις είναι ανάλογα μεγαλύτερο. Έδω όμως θα τονίσω ότι χρειάζεται ιδιαίτερη προσοχή στην πολιτική για το πως αυτό θα μεγαλώνει όταν γεμίσει. Αυτό που έχω δει είναι ότι συνήθως βάζετε κάτι μικρό ή αφήνετε το default που είναι 10%. Αυτό είναι και το λάθος σας. Καλό είναι να βάζετε σαν πολιτική το αρχικό μέγεθος του. Δηλαδή αν έχω 30ΜΒ Log λέω στo file growth του 30ΜΒ ούτε 100% ούτε τίποτα σε ποσοστό. Και αυτό γιατί πολλοί θεωρούν το Log σαν έναν κουβά όμως τα πράγματα δεν είναι έτσι. Εσωτερικά το log χωρίζεται σε virtual logs. Ο σκοπός μου είναι να έχω πάντα ισομεγέθη virtual log ώστε να μην μου εμφανισθεί το φαινόμενο το Log file να είναι μεγαλύτερο από το ή τα data file(s). Σε κάποιο άλλο post μου θα σας πω περισσότερα για αυτό.
    Τώρα βέβαια θα πρέπει να πω ότι αν παίρνω backup την βάση μου το log γίνεται truncate. Προσοχή δεν μειώνεται σαν μέγεθος στον δίσκο αλλά εσωτερικά γίνεται truncate. Αυτό σημαίνει ότι ο εσωτερικός χώρος που ελευθερώνεται επαναχρησιμοποιείται. Λογικά αν έχω εκτιμήσει σωστά το αρχικό μέγεθος δημιουργίας του log file και κάνω καθημερινό backup δεν θα το δω ποτέ να μεγαλώνει πέρα από το αρχικό του μέγεθος.
    Για να δούμε πως χρησιμοποιείται το log. Τρεις είναι οι βασικοί παράγοντες που επηρεάζουν το performance, CPU, MEMORY, DISK IO. Δυστυχώς όμως το χειρότερο από όλα σε performance σε σχέση με τα άλλα είναι ο δίσκος. Σκοπός του SQL Server είναι να έχει το μικρότερο δυνατό disk io. Για να το κάνει αυτό χρησιμοποιεί μνήμη και φυσικά το log file.
    O SQL Server δεσμεύει ένα ποσό από την διαθέσιμη μνήμη του συστήματος. Ένα ποσό από αυτή την μνήμη ανήκει στη buffer cache. Για να δούμε όλη την διαδικασία με ένα παράδειγμα.
    Είπαμε πιο πάνω ότι τα δεδομένα μου είναι αποθηκευμένα σε σελίδες των 8KB. Έτσι όταν κάποιος χρήστης κάνει ένα select, insert, update, delete αυτό που κάνει ο SQL Server είναι να δει αν υπάρχουν στην buffer cache οι σελίδες που θα επηρεαστούν από την ενέργεια αυτή. Εάν δεν είναι τότε τις διαβάζει από τον δίσκο και τις βάζει στην buffer cache. Το επόμενο βήμα του είναι να εκτελέσει την ενέργεια πάνω στις σελίδες που είναι στην buffer cache. Για το μεν select επιστρέφει τα δεδομένα στον χρήστη, για τις άλλες ενέργειες όμως κάνει ακόμα ένα βήμα γράφει την ενέργεια στο log file. Γιατί το κάνει αυτό; Η απάντηση είναι απλή κάνει minimize το ΙΟ. Στην ουσία οι ενέργειες θα γραφτούν στο data file όταν γίνει η διαδικασία που φέρει το όνομα checkpoint process. Τι κάνει αυτή; Σε τακτά χρονικά διαστήματα έρχεται ο SQL Server και διαβάζει το log είτε από την αρχή εάν είναι η πρώτη φορά είτε από το σημείο που είχε σταματήσει την προηγούμενη φορά η διαδικασία αυτή. Και όσα transactions είναι commited βλέπει ποιες είναι οι σελίδες που έχουν επηρεασθεί από αυτά και τις γράφει στον data file δηλαδή στον δίσκο. Τώρα κάποιος θα αναρωτηθεί και αν πέσει το ρεύμα πριν γίνει η διαδικασία αυτή; ΔΕΝ ΥΠΑΡΧΕΙ ΚΑΝΕΝΑ ΠΡΟΒΛΗΜΑ. Διότι κάθε φορά που ο SQL Server ξεκινάει κοιτάζει το Log file και ότι transaction είναι ολοκληρωμένο και δεν υπάρχει στα data file αναπαράγετε, ότι ήταν σε εξέλιξη γίνεται rollback. Η διαδικασία αυτή λέγεται recovery process. Άρα θα έχω όλα μου τα δεδομένα εκτός βέβαια από αυτά που δεν είχαν προλάβει να ολοκληρωθούν.
    Σας χρωστάω μια απάντηση από πιο πάνω. Θυμηθείτε το παράδειγμα με τις 10.000 σελίδες όπου στην κάθε μία έχω ένα record. Βάλτε με το μυαλό σας τι θα γίνει αν απλά θελήσω να διαβάσω όλα τα δεδομένα του πίνακα. Απλά θα έχω "τσακίσει" τους τρεις βασικούς παράγοντες που επηρεάζουν την απόδοση του SQL Server, και αυτό γιατί για να κινηθούν οι κεφάλες στον δίσκο θα πρέπει να δώσει η εντολή η CPU αυτές θα διαβάσουν τα δεδομένα , άρα μεγάλο ΙΟ και αυτά θα πρέπει αν μεταφερθούν στην μνήμη.
    Έτσι απλά! τα φόρτωσα όλα, και αυτό γιατί όταν σχεδίαζα την βάση μου δεν έλαβα υπόψη μου την αρχιτεκτονική του SQL Server.
  18. antonch
    Θα επανέλθω στο συγκεκριμένο θέμα μετά από απαίτηση συναδέλφων που είδαν την παρουσίαση που έχω κάνει για το 34ο dotNetZone Event και την οποία θα βρείτε εδώ. Υπήρχαν κάποιες απορίες και ερωτήσεις που φιλοδοξώ να τις απαντήσω με το παρόν post μου. Ας πάρουμε λοιπόν τα πράγματα από την αρχή μιας και διακαείς πόθος όλων μας είναι να έχουμε την τέλεια απόδοση στα queries μας δηλαδή όλα να εκτελούνται σε χρόνο μηδέν και να επιστρέφουν δισεκατομμύρια εγγραφές (καλά πλάκα κάνω ).
    Όμως για όλα αυτά υπάρχουν κάποιες προϋποθέσεις όπως:
    1. Tuning queries κάνει αυτός που είναι database developer ΚΑΙ όχι ένας developer γενικά. Δεν σημαίνει ότι όποιος ξέρει να γράφει C++, C#, VB, Java ξέρει και τι σημαίνει query execution. Εξάλλου όλοι πλέον οι developers δεν θέλουν να γράφουν queries μέσα από τις εφαρμογές τους (συμφωνώ και εγώ με αυτό) αλλά να χρησιμοποιούν εργαλεία όπως LINQ to SQL, Entity Framework κ.α σωστά;
    2. Αυτός που θα αναλάβει να κάνει την συγκεκριμένη εργασία δεν φτάνει να ξέρει πως τα queries, οι indexes δουλεύουν. Πρέπει να ξέρει την γενικότερη αρχιτεκτονική του SQL Server καθώς επίσης και πως ΟΛΑ του τα κομμάτια παντρεύονται μεταξύ τους.
    3. Θα πρέπει να είναι βαθύς γνώστης του σχεδιασμού της βάσης στην οποία θα κάνει tuning το ή τα queries. Όπως επίσης θα πρέπει να ξέρει την ποιότητα και την ποσότητα των δεδομένων τα οποία θα χρησιμοποιήσει για να βελτιώσει την απόδοση στο query του.
    Αν εκπληρώνονται οι παραπάνω προϋποθέσεις θα πρέπει να γνωρίζει τα εξής:
    1. Τι είναι το Query Performance και πως αυτό μετριέται.
    Βασική αρχή στην βελτίωση της απόδοσης ενός query είναι να βρω το τι φταίει και για να γίνει αυτό πρέπει να έχω μια μονάδα μέτρησης για να μπορέσω να εξάγω ένα συμπέρασμα το οποίο θα μου δείξει το δρόμο που θα με οδηγήσει στην επίλυση του προβλήματος. Στον SQL Server έχουμε τρεις βασικές μονάδες μέτρησης αυτές είναι query cost, page reads, query execution time.
    Για να τις δούμε όμως λίγο καλύτερα μήπως και βγάλουμε κάποιο συμπέρασμα
    Query Cost: Συνήθως είναι η βασική μας μονάδα μέτρησης όταν θέλουμε να συγκρίνουμε την απόδοση ενός ερωτήματος. Είναι μια εσωτερική μετρική του SQL Server η οποία λαμβάνει υπόψη της το πόσο CPU και Ι/Ο χρησιμοποιεί το ερώτημα μας. 'Οσο μικρότερη είναι η τιμή του (σε %) τόσο καλύτερη είναι και η απόδοση στο ερώτημα μας. Αλλά δεν λαμβάνει υπόψη θέματα που αφορούν πχ Waiting Locks ή Resource Contention. Επίσης όταν μέσα στο query μας υπάρχουν user defined functions (UDFs) ή Common Language Runtime (CLR) ρουτίνες αυτές δεν λαμβάνονται υπόψη στον υπολογισμό του query cost και αυτός είναι και ο λόγος που ονομάζεται estimated query cost. Αυτό το βλέπουμε από το Εxecution Plan

    Page Reads: Είναι ο αριθμός των σελίδων (8KB/page) που το Storage Engine του SQL Server διαβάζει κατά την διάρκεια εκτέλεσης του query. Αυτό το βλέπουμε εάν στο session στο οποίο εκτελούμε το query μας και πριν την εκτέλεση τους εκτελέσουμε την SET STATISTICS IO ON. To αποτέλεσμα θα είναι το παρακάτω

    Από το output καταβαίνουμε ότι έχει κάνει logical reads 27 pages δηλαδή τις διάβασε από την μνήμη (buffer cache, δείτε παλαιότερο post μου). Σε αυτές συμπεριλαμβάνονται και τα data και τα index pages. Με τα physical read και read-ahead reads βλέπουμε πόσα pages διάβασε από τον δίσκο και τις ανέβασε στην buffer cache. Με το Scan count βλέπουμε το πόσες φορές έκανε scan τις σελίδες αυτές για να βγάλει το αποτέλεσμα (εάν δούμε πάνω από 1 σημαίνει ότι κάτι δεν πάει καλά δηλαδή ξαναδιαβάζει τα ίδια data). Με το lob physical reads και lob read-ahead reads βλεπουμε τον αριθμό των σελίδων που χρειάσθηκε να διαβάζει για να μας φέρει δεδομένα τύπου varchar(max), nvarchar(max), varbinary(max), text, image, xml. Αλλά δεν συμπεριλαμβάνεται το CPU Cost όπως επίσης δεν συμμετέχουν οι UDFs και CLR ρουτίνες.
    Query Execution Time: Αυτή είναι η ποιο επηρεαζόμενη μετρική, και αυτό διότι μπορεί να έχω locks, resource contention στον server κατά την διάρκεια της εκτέλεσης του query. Αυτή την μετρική την βλέπουμε εάν στο session στο οποίο εκτελούμε το query μας και πριν την εκτέλεση τους εκτελέσουμε την SET STATISTICS ΤΙΜΕ ON. To αποτέλεσμα θα είναι το παρακάτω

    Από το output βλέπουμε τους χρόνους που χρειάσθηκε για την προεργασία (parse and compile) και τον χρόνο εκτέλεσης.
    2. Να γνωρίζει πως εκτελείτε λογικά ένα query.
    Έχω ήδη γράψει για αυτό σε παλαιότερο άρθρο μου το οποίο μπορείτε να βρείτε εδώ.
    3. Να μπορεί να διαβάσει και να ερμηνεύσει το Graphical ή μη Execution Plan

    Για όσους δεν ξέρουν πώς να το κάνουν αυτό θα του πω απλά ότι διαβάζεται από δεξιά προς τα αριστερά και κάθε σύμβολο σημαίνει μια διαφορετική διαδικασία εκτέλεσης. Υπάρχουν αρκετά τέτοια σύμβολα, τα οποία και μπορείτε να τα βρείτε όλα μέσα στο SQL Server Books Online (BOL). Θα σας δώσω όμως μερικά hints.
    a. Εάν βλέπετε παχιά βελάκια τα οποία συνδέουν το ένα task με κάποιο άλλο, αυτό σημαίνει ότι μεγάλος όγκος δεδομένων μεταφέρεται από task σε task. Γενικότερα θέλουμε να βλέπουμε λεπτά βελάκια.
    b. Εάν στο query μας έχουμε join ή group by και βλέπουμε ότι αυτό υλοποιείτε με Hash Match (Inner Join) αυτό συχνά σημαίνει ότι δεν υπάρχει ο κατάλληλος index ώστε να γίνει γρηγορότερα η διαδικασία πχ. Index στο foreign key στο join ή στο πεδίο με το οποίο κάνουμε group by.
    c. Εάν βλέπουμε το σύμβολο του Sort και το ποσοστό του κόστους που έχει είναι υψηλό, τότε καλό είναι να εξετάζουμε την περίπτωση να γίνει κάποιος Index στo πεδίο αυτό ώστε να απαλοιφή η διαδικασία Sort.
    d. Τα μεγάλα excecution plans δεν σημαίνει ότι είναι κακά αλλά καλό είναι να έχω μικρά execution plans και αυτό διότι έχω λιγότερα βήματα εκτέλεσης στο query μου.
    e. Επίσης όταν στον execution plan δω σε κάποιο task Table Scan ή Clustered Index Scan σημαίνει ότι δεν υπάρχει Index τέτοιος ώστε να γίνει καλύτερη υλοποίηση εκτέλεσης. (Σημείωση: Αν ο πίνακας σας έχει δεδομένα που είναι κάτω από 8ΜΒ τότε ακόμα και αν υπάρχει index ο SQL Server επιλέγει την συγκεκριμένη υλοποίηση διότι έχει λιγότερο κόστος σε resources και χρόνο).
    Βέβαια υπάρχουν και άλλα που πρέπει να γνωρίζουμε αλλά αυτά είναι τα βασικά. Ας αρχίσουμε με αυτά και επιφυλάσσομαι στο μέλλον να γράψω και για τα άλλα
  19. antonch
    Ένα από τα πράγματα τα οποία προσωπικά θεωρώ από τα διαμάντια του SQL Server που έχουμε διαθέσιμα από την έκδοση του SQL Server 2005 (Standard & Enterprise) SP1 είναι το Database Mirroring.

    Μέχρι την εμφάνιση του για να έχω αυτό που όλοι θέλουμε και δεν είναι άλλο από το database availability έπρεπε να καταφύγουμε σε κάποιες λύσεις όπως:

    1. Clustering

    Ιδανική λύση αλλά στοιχίζει αρκετά σε χρήμα τόσο για την υλοποίηση της όσο και στη διαχείριση του.

    2. Replication

    Αξιόπιστη λύση, με κάποια θεματάκια στην υλοποίηση της, αλλά και με κάποιες παραδοχές που πρέπει να λάβουμε ανάλογα με το τι είδος replication θα υλοποιήσουμε και αφορούν στην παλαιότητα της πληροφορίας που θα έχουμε στην replica.

    3. Log Shipping

    Μια ιδανική λύση για αυτούς που δεν έχουν χρήματα για να υλοποιήσουν τις παραπάνω λύσεις, αλλά έχουν την δυνατότητα να υποστούν απώλεια δεδομένων πχ τα transactions των τελευταίων 15 λεπτών. Αλλά έχουν και την πολυτέλεια να έχουν ένα αρκετά υψηλό downtime μέχρι το αντίγραφο να έρθει σε κατάσταση λειτουργίας και να ενημερωθούν όλες οι εφαρμογές να χρησιμοποιούν αυτό πλέον.

    Προσωπικά και χαριτολογώντας πάντα αυτό το ονομάζω το cluster του φτωχού.

    Έχοντας υλοποιήσει στο παρελθόν και τις τρεις λύσεις, έχω μόνο καλά λόγια να πω και για τις τρεις. Απλά θέλω να υποσημειώσω ότι κάθε μία έχει υλοποιηθεί με οδηγό τις ανάγκες του κάθε πελάτη. Για παράδειγμα αν αυτός δεν μπορεί να έχει data loss και θέλει zero downtime δεν θα του βάλεις την 2 και την 3 λυση.



    Σημείωση

    Αν και με την λύση 2 έχοντας ένα transactional replication σε ένα πολύ γρήγορο δίκτυο μπορείς να έχεις παλαιότητα πληροφορίας κάτω από 1 sec

    Τι είναι όμως το Database Mirroring;

    Είναι δυνατότητα που μας δίνει ο SQL Server ώστε να έχω ένα hot standby αντίγραφο της βάσης μου σε ένα άλλο server που είναι σε διαφορετική γεωγραφική περιοχή. Η μόνη απώλεια δεδομένων που θα έχω θα είναι μόνο τα transactions τα οποία ήταν σε εξέλιξη (δεν είχαν προλάβει να γίνουν commit) κατά την στιγμή της καταστροφής της βάσης ή του server, όπως ακριβώς συμβαίνει και με την λύση του cluster.

    Σημείωση

    Το Database Mirroring είναι σε επίπεδο database και όχι σε επίπεδο server όπως το clustering. Αυτό σημαίνει ότι αν στο server της παραγωγής έχω 10 databases θα πρέπει να κάνω Mirror κάθε μια ξεχωριστά.



    ΔΕΝ ΜΠΟΡΩ ΝΑ ΚΑΝΩ MIRROR ΣΤΙΣ SYSTEM DATABASES (MASTER, MSDB, MODEL, TEMPDB)

    Ποια είναι η Αρχιτεκτονική του Database Mirroring;

    Η αρχιτεκτονική του database mirroring είναι σχετικά απλή όπως φαίνεται στο παρακάτω σχεδιάγραμμα.



    Αυτή είναι η απλή λύση όπου έχω τους δύο servers μου. Τον Server της παραγωγής με την βάση μου το ονομάζουν Principal Server ενώ το mirror server τον ονομάζουν Mirror Server. Σε αυτή την περίπτωση όμως αν "σκάσει" ο Principal θα πρέπει με το "χέρι" να φέρω στην ζωή τον Mirror ο οποίος θα γίνει μετά από αυτή την διαδικασία Principal.

    Μα καλά θα πει κάποιος τι σόι διαθεσιμότητα είναι αυτή αν πρέπει να το κάνω με το "χερι"; Είπαμε αυτή είναι η απλή λύση.

    Υπάρχει η κανονική λύση η οποία είναι η παρακάτω:



    Σε αυτή την περίπτωση εμπλέκεται ακόμα ένας server o Witness Server του οποίου η δουλειά είναι να παρακολουθεί το database mirroring έτσι ώστε αν "πέσει" o principal αυτόματα να κάνει principal τον mirror server.

    Σημείωση

    Συνηθίζουμε να ονομάζουμε τους τρεις αυτούς servers (Principal, Mirror, Witness) σαν Mirror Partners ή Partners.



    Προϋποθέσεις και μικρά αλλά σημαντικά μυστικά για την Υλοποίηση

    1. Όλοι οι servers πρέπει να είναι στην ίδια version του SQL Server π.χ 2008. Δεν μπορεί κάποιος από αυτούς να είναι σε 2005

    2. Ο Principal και ο Mirror πρέπει να είναι στην ίδια edition πχ. Standard ή Enterprise

    3. O Witness πρέπει να είναι σε ένα reliable computer system και μπορεί να είναι Standard, Enterprise, Workgroup ή Express edition.

    4. O Mirror server πρέπει να έχει τον χώρο που χρειάζεται η βάση που έχουμε στον Principal.

    5. Στην περίπτωση που θα υλοποιήσουμε το σενάριο high safety with automatic failover σε όλους τους partners το cpu load να είναι μικρότερο από το 50% (ειδικά για τον principal και τον mirror). Και αυτό διότι αν έχουμε cpu overload o failover partner μπορεί να μην μπορέσει να κάνει ping τα άλλα server instances με αποτέλεσμα να έχω unnecessary failover!!!. Εάν αυτό δεν μπορεί να γίνει τότε διαλέξετε κάποιο άλλο από τα διαθέσιμα σενάρια (high safety without automatic failover ή high performance).

    6. Όλοι οι SQL Servers που συμμετέχουν στο database mirroring πρέπει στην master database να έχουν το ίδιο code page και collation, αλλιώς θα έχουμε προβλήματα κατά την διάρκεια του database mirroring setup!.

    7. Για καλύτερο performance είναι καλύτερα να χρησιμοποιηθεί dedicated network adapter για το mirroring.

    8. Εάν έχουμε 32bit system, τότε το database mirroring μπορεί να υποστηρίξει μέχρι 10 databases ανά SQL Server instance εξαιτίας του αριθμού των worker threads τα οποία καταναλώνονται για κάθε database mirroring session.

    9. Το database mirroring υποστηρίζει οποιοδήποτε database compatibility level.

    10. To RESTORE του BACKUP από την principal server στον mirror server πρέπει να γίνει με WITH NORECOVERY.

    11. Μόνο το FULL RECOVERY model υποστηρίζεται στο database mirroring. Αυτό σημαίνει ότι η database που θα κάνω Mirror θα πρέπει να έχει αυτό το recovery model, κανένα

    12. Databases που έχουν Filestream enable δεν μπορούν να γίνουν mirror.

    Ποια είναι τα πλεονεκτήματα του Database Mirroring;

    1. Αυξάνει την ασφάλεια των δεδομένων

    2. Αυξάνει την διαθεσιμότητα της database.

    3. Αυξάνει την διαθεσιμότητα του παραγωγικού περιβάλλοντος στην περίπτωση που θελήσουμε σε αυτό να κάνουμε upgrades.

    Πώς δουλεύει το Database Mirroring;



    Ο principal και ο mirror server έχοντας ένα database mirror session μεταξύ τους και στην ουσία του πράγματος ότι γίνεται στον principal server γινεται redo στον mirror server. Αυτό γίνεται όσο το δυνατό γρηγορότερα και στην πραγματικότητα ο principal server στέλνει κάθε ενεργό transaction log record στον mirror server, ο οποίος με την σειρά του το κάνει στην mirror database.

    Σημείωση

    Δεν είναι όπως στο transactional replication το οποίο είναι σε logical level, εδώ είναι σε physical log record level.

    Το database mirroring session τρέχει είτε σύγχρονα είτε ασύγχρονα. Το αν θα χρησιμοποιήσω κάποιο από αυτά εξαρτάται από τον τρόπο με τον οποίο θέλω να γίνεται το database mirroring, επιλέγοντας ένα από τους παρακάτω:

    1. High Safety (synchronous operation)

    Κάθε φορά που ένα transaction γίνεται στην database του principal, αυτός στέλνει τα active log στον mirror server. O mirror με την σειρά του γράφει το active log που πήρε το δυνατόν γρηγορότερα και αφού το κάνει αυτό τότε o principal server παίρνει ένα μήνυμα ότι mirror server έχει κάνει την δουλειά του και κάνει commit (ο principal).



    Σημείωση

    Ο Mirror δεν κάνει commit απλά γράφει το log στο δίσκο μιας και όπως θα δούμε παρακάτω η database στον mirror server είναι σε φάση restore.

    Μετά από αυτό το status τον database είναι synchronized και όσο διατηρείται το database mirroring session αυτό θα είναι πάντα το ίδιο.

    Στον συγκεκριμένο τρόπο έχω δύο ακόμα επιλογές και αυτές είναι αν θα έχω automatic failover ή όχι. Απλά στην πρώτη περίπτωση πρέπει να έχω και witness server, ο οποίος κοιτάζει αν ο principal είναι στην ζωή και αν δεν τότε σηκώνει τον mirror μόνος του. Στην άλλη περίπτωση θα πρέπει να τον σηκώσω εγώ κάνοντας αλλαγή ρόλου είτε με Τ-SQL είτε μέσα από τα UI

    Σημείωση για τους Developers

    Εάν θέλετε οι εφαρμογές που έχετε φτιάξει αυτόματα να βλέπουν τον mirror όταν πέσει ο principal τότε καλό είναι να χρησιμοποιήσετε το SQL Native Client Access σαν provider στο connection string βάζοντας ακόμα μέσα σε αυτό την επιλογή failover parter στην οποία θα βάζεται το όνομα του Mirror Server ενώ στο Server/Data Source θα έχετε κανονικά τον principal π.χ



    Provider=SQLNCLI10.1; Data Source=ac-demosrv; Failover Partner=ac-demosrv\inst01;…

    Στην περίπτωση που έχετε legacy εφαρμογές υπάρχουν δύο λύσεις που σας προτείνω:

    a) Να έχετε 2 connection strings το ένα να δείχνει στον principal και το άλλο στον mirror. Πάντα χρησιμοποιούμε το πρώτο και αν αυτό είναι κάτω πιάνουμε το timeout error και μετά χρησιμοποιούμε το άλλο

    Με κάποιον τρόπο έχουμε εκτός εφαρμογής το connection string και το αλλάζουμε με το χέρι πχ udl file , registry entry με ότι βέβαια συνεπάγεται αυτό από κόστος σε χρόνο για να γίνει.



    Σημείωση

    Εάν θέλω να αλλάξω τους ρόλους με t-sql θα πρέπει να εκτελέσω το εξής command

    ALTER DATABASE SET PARTNER FAILOVER;

    To ίδιο μπορώ να το κάνω από εάν κάνω δεξί κλικ Properties πάνω στην βάση μου και πάω στην επιλογή Mirroring και πατήσω το κουμπί Failover



    Εδώ πρέπει να τονίσω ότι υπάρχουν και μερικές παραλλαγές αλλά για αυτές δείτε τα BOL.



    Ο τρόπος αυτός προσφέρει ασφάλεια και εγγυάται ότι δεν θα έχω απώλεια δεδεμένων στον mirror server όμως έχω κάποιο performance penalty στον principal όσον αφορά το transaction

    2. High Performance (asynchronous operation)

    Εδώ αν και έχω καλύτερο performance μιας και ή όλη διαδικασία γίνεται ασύγχρονα υπάρχει η πιθανότητα να έχω απώλεια δεδομένων στον Mirror Server. Και αυτό διότι όταν γίνεται το transaction στον Principal Server αυτός στέλνει στον Mirror Server το log αλλά δεν περιμένει να πάρει απάντηση για αν το έκανε ή όχι apply αλλά κάνει commit και φυσικά στέλνει στον client ότι όλα είναι ΟΚ. Αυτό σημαίνει ότι ο Mirror Server μπορεί να είναι πίσω όσον αφορά τα δεδομένα που έχει πάρει και σε περίπτωση που πέσει ο Principal και έρθει στην ζωή θα έχει λιγότερα δεδομένα. Στην περίπτωση αυτή ο Mirror Server είναι DISCONNECTED.

    Αυτός ο τρόπος είναι ιδανικός στην περίπτωση που έχω Principal που είναι oveloaded ή στην περίπτωση που έχω τον Mirror σε WAN το οποίο δεν έχει κάλο δίκτυο.

    Υπάρχει ακόμα ένα θέμα στο τρόπο αυτό που έχει να κάνει με τη χρήση του Witness Server. Αν και μπορεί να υπάρχει σε αυτό υπάρχουν περιπτώσεις όπου μπορεί να δημιουργήσει πρόβλημα όπως


    Εάν χαθεί ο Mirror Server ο Principal πρέπει να κάνει connect στον Witness αν δεν τα καταφέρει τότε ο Principal κάνει την database offline ακόμα και αν μετά συνδεθεί είτε ο Mirror είτε ο Witness.
    Στην περίπτωση που χαθεί ο Principal ο Mirror με την σειρά του πρέπει να κάνει connect στον Witness και αν αυτός είναι μη διαθέσιμος τότε η database δεν θα γίνει διαθέσιμη.
    Bήματα Yλοποίησης του Database Mirrorning με T-SQL (εύκολος τρόπος ;-) )

    Aς δούμε την υλοποίηση με κώδικα t-sql στο demo που ακολουθεί

    Demo Video

    Τον κώδικα της παρουσίασης μπορείτε να τον κάνετε download από εδώ

    Bήματα Yλοποίησης του Database Mirrorning με την χρήση του SSMS (δύσκολος τρόπος ;-) )

    Aφού είδαμε το εύκολο τρόπο ας δούμε τον δυσκολο τρόπο υλοποίησης

    Demo Video

    Σας ευχαριστώ για την προσοχή σας

    Αντώνης
  20. antonch
    Είμαι φανατικός αναγνώστης του, έχω διαβάσει σχεδόν όλα του τα βιβλία όχι μια αλλά πολλές φορές, τα οποία κοσμούν την βιβλιοθήκη μου. Προσπαθώ να βρω τα άρθρα του όπου και αν γράφει. Δεν υπάρχει περίπτωση να μην τον αναφέρω μέσα στην τάξη όταν κάνω μάθημα. Αλλά ποτέ μέχρι σήμερα δεν τον είχα δει να κάνει μάθημα. Γενικά δεν κάνει πολλές εμφανίσεις που να μπορώ να βρώ στο web.
     
    Κυρίες και Κύριοι έχω την τιμή να σας παρουσιάσω στο παρακάτω video έναν εκ των σηματικότερων θεμελιωτών των RDBMS τον one and only
    C. J. Date
    Απολαύστε τον στο παρακάτω videο. Ίσως να μην έχει η παρουσίαση αυτή να σας μάθει κάτι που να μην γνωρίζετε, αλλά αξίζει να δείτε αυτόν τον μεγάλο επιστήμονα.
    (κάντε click στο παρακάτω image για να δείτε το video)

  21. antonch
    Αρκέτες φορές έχω δεχθεί ερωτήσεις σχετικά με τα database Schemas. Οι πιο δημοφιλείς ερωτήσεις είναι;
    Τι είναι τα Schemas; Ποιά είναι η πρακτική αξία τους; Γιατί τα έβαλαν στον SQL Server τόσο αργά; η Oracle τα έχει χρόνια τώρα! Θα ξεκινήσω με την τελευταία.
    Μπορεί τα schemas να εμφανίστηκαν από τον SQL Server 2005 αλλά αυτό δεν σημαίνει ότι δεν υπήρχαν και πριν. Υπήρχαν αλλά δεν ήταν “ορατά”. Στην πραγματικότητα με το που έφτιαχνες έναν user, αυτόματα δημιουργόνταν το αντίστοιχο schema.
    Στην Oracle υπήρχαν, αλλά ο σκοπός τους ήταν καλύψουν μια ανάγκη που βασικά δεν την είχαμε, ούτε την έχουμε, στον SQL Server. Όπως θα γνωρίζεται κάθε database στην Oracle είναι ένα διαφορετικό service. Έτσι εάν θέλω να έχω μια βάση για το ERP μου, μια βάση για το Web Site μου, και μία βάση για κάτι άλλο, θα πρέπει να έχω τρία διαφορετικά services να τρέχουν. Στο SQL Server δεν το έχουμε ανάγκη αυτό, διότι με ένα service μπορώ να έχω 32767 διαφορετικές βάσεις. Με την χρήση λοιπόν των schemas στην Oracle έλυνα το παραπάνω θέμα. Έφτιαχνα μια βάση στην οποία έφτιαχνα τα αντίστοιχα schemas μέσα σε αυτή ώστε να καλύψω τις τρεις διαφορετικές δομές που είχα σε κάθε βάση.
    Η εμφάνιση των Schemas στον SQL Server είναι για να λύσουν άλλα θέματα. Αλλά ας πάρουμε τα πράγματα από την αρχή.
    Ένα Schema στον SQL Server δεν είναι τίποτα άλλο από ένα named container για τα database objects (tables,views,stored procedures κλπ).
    Βασικός λόγος που έκαναν την εμφάνιση τους τα Schemas από τον SQL Server 2005 και μετά ήταν για να λύσουν ένα βασικό administration πρόβλημα.
    Έστω λοιπόν ότι έχω τον χρήστη Nasos. Δεν είναι administrator, δεν είναι database owner, έχει όμως δικαιώμα να φτιάχνει tables. Έστω ότι αυτός έφτιαξε τους πίνακες Ν1 και Ν2. Άρα έχω dbname.Nasos.N1 και dbname.Nasos.N2.
    Εάν τώρα έσβηνα τον user Nasos είτε κατά λάθος είτε διότι έπρεπε, επειδή ο Νάσος έφυγε από την εταιρεία, στις προηγούμενες εκδόσεις τα object αυτά έμεναν ορφανά, το αποτέλεσμα ήταν ότι είτε δεν λειτουργούσαν stored procedures που πατούσαν πάνω σε αυτά, είτε δεν μπορούσα να προσπελάσω απευθείας, και έπρεπε να μπω στην διαδικασία να πάρω το ownership με ότι αυτό συνεπάγεται.
    Από τον SQL Server 2005 και μετά αυτό δεν συμβαίνει διότι πολύ απλά οι πίνακες αυτοί δεν ανήκουν στον user αλλά στο Schema.
    Αυτός ήταν και ο βασικός λόγος που σαν DBA δεν έδινα δικαιώματα στο user να φτιάχνει δικά του objects ή αν πραγματικά ήθελα να κάνω κάτι τέτοιο έβαζα όλους τους χρήστες με ένα user name.
    Κάθε user ανήκει σε κάποιο Schema. Ακόμα και αν δεν πω κατά την δημιουργία του user στην βάση σε ποιο Schema ανήκει, by default αυτός θα ανήκει στο Schema dbo.
    Επίσης όταν δημιουργώ ένα object πχ έναν πίνακα και δεν λέω κατά την στιγμή της δημιουργίας του σε ποιο Schema αυτό θα ανήκει, by default αυτό πάει στο Schema στο οποίο ανήκει ο user που το δημιούργησε.
    Κάθε Schema έρχεται και κάθεται μεταξύ του database level του object level στην ιεραρχία του SQL Server. Αυτό σημαίνει ότι μπορώ να έχω περισσότερες δυνατότητες στον ορισμό του security πάνω στα objects της βάσης μου. Κάποια στιγμή θα σας πω για το security στον SQL Server και θα σας το εξηγήσω καλύτερα.
    Κάθε Schema επίσης έχει συγκεκριμένο owner. O οποίος μπορεί να είναι είτε συγκεκριμένος user είτε database role είτε application role.
    To Schema πλέον αντικαθιστά τον owner στο multi-part object name. Αυτό σημαίνει ότι στο παράδειγμα SELECT * FROM Northwind.antonis.Customers μέχρι την έλευση του SQL Server 2005 λέγαμε ότι ο antonis είναι ο χρήστης τώρα δεν είναι ο χρήστης αλλά το schema.
    Μερικές χρήσιμες συμβουλές για τα Schemas
    Ομαδοποίηση αντικειμένων σύμφωνα με το σκοπό τους πχ schema το οποίο περιέχει όλα τα αντικείμενα που αφορούν μια συγκεκριμένη ενότητα (π.χ web site). Διαχείριση του security σε επίπεδο βάσης με την χρήση των Schemas. Δηλαδή αντί να δίνω δικαιώματα σε επίπεδο χρήστη δίνω σε επίπεδο schema και ο χρήστης κληρονομεί αυτά μιας και ανήκει στο schema αυτό. Καλό είναι να μην είναι όλος ο κόσμος Schema owner. Ένας χρήστης είναι το ιδανικό. Επισης καλό θα είναι να μην είναι ο dbo ο owner για τα schemas τα οποία έχω μέσα στην βάση μου. Αυτά τα λίγα για τα Schemas και την πρακτική τους αξία στον SQL Server. Ελπίζω να έδωσα απαντήσεις στα ερωτήματα σας.
  22. antonch
    Πότε τελικά λέμε ότι αυτός ο άνδρας είναι φαλακρός;
    Η φαλάκρα είναι κάτι το οποίο σε αρκετούς άνδρες δημιουργεί ψυχολογικά προβλήματα σε άλλους πάλι κανένα, και σε κάποιους άλλους μερικές φορές τους ακουμπάει για κάποια χρονικά διαστήματα αλλά γενικά εχουν συμβιβαστεί μαζί της.
    Πέρα όμως από αυτό το ερώτημα παραμένει. Πότε τελικά κάποιος είναι φαλακρός;
    Αν για παράδειγμα από τα μαλλιά μου βγάλω μια τρίχα είμαι φαλακρός; Η προφανής απάντηση είναι όχι. Αν βγάλω και δεύτερη και τρίτη και τέταρτη αυτό με κάνει φαλακρό; Προφανώς όχι.
    Αν όμως χάσω εκατοντάδες τρίχες αυτό με κάνει φαλακρό; Ποιός μπορεί να το απαντήσει αυτό με σιγουριά.
    Αν συνεχίσω να χάνω εκατοντάδες τρίχες καθημερινά και ένα πρωινό ο γιός μου φωνάξει «Πατέρα είσαι φαλακρός» είμαι τελικά φαλακρός ή έχω αρχίσει να φαλακρένω.
    Υπάρχει τελικά κάτι το οποίο μπορεί να ορίσει το αν είμαι φαλακρός ή όχι;
    Στην φιλοσοφία αυτο είναι το Σόφισμα του σωρείτη το οποίο είναι ένα λογικό παράδοξο που ασχολείται με την ασάφεια των κατηγορημάτων σε μία λογική πρόταση δείχνοντας ότι είναι πιθανό να μην υπάρχει ένα σαφές όριο ανάμεσα σε ένα κατηγόρημα και την άρνησή του.
    Στον προγραμματισμό έχουμε μάθει να λαμβάνουμε αποφάσεις με βάση την δυαδική λογική (if/else). Έχουμε δομήσει το τρόπο σκέψης μας με αυτό το pattern. Οτιδήποτε πέρα από αυτό μας ξενίζει, μας δημιουργεί πρόβλημα, μας ανατρέπει τον τρόπο σκέψης μας.
    Θυμάμαι χαρακτηριστικά το CheckBox Control το οποίο όταν πρωτοβγήκε δημιούργησε θύελα αντιδράσεων και αυτό γιατί είχε τρεις διαφορετικές περιπτώσεις checked/unchecked/indeterminate. Η τελευταία ήταν αυτή που δημιουργούσε το πρόβλημα. Δεν μπορούσαμε να καταλάβουμε το λόγο ύπαρξης της. Τι σημαίνει ούτε ναι ούτε όχι. Ταλαιπώρησε και συνεχίζει να ταλαιπωρεί αρκετό κόσμο της πληροφορικής.
    Το να γράψεις λογισμικό τελικά δεν είναι μια δυαδική λογική ή είναι;
    Η τρίτη επιλογή πρέπει να λαμβάνεται υπόψη ή όχι;
    Ποιος μπορεί να δώσει ασφαλή απάντηση, και μιλάω για μια απάντηση η οποία να είναι αποδεκτή από όλους. (Δύσκολο)
    Προς τους αδερφούς developers
    Την επόμενη φορά που θα γράψετε software μη σκεφτήτε το άλλο το περίεργο.
    Σκεφτήτε με την λογική του if/else αλλά αλλάξτε το στη τελική σας υλοποίηση με τη switch αφήνοντας ένα παράθυρο στην πιθανότητα
    Καλές Διακοπές
  23. antonch
    Περιμένοντας να έρθουν συγγενείς και φίλοι σπίτι να μου ευχηθούν για την γιορτή μου, σκεφτόμουν πώς να ευχαριστήσω όλους που σήμερα είτε με email είτε με μηνύματα στο facebook ή στο messenger μου ευχήθηκαν χρόνια πολλά.
    Στο μυαλό μου ήρθε κάτι το οποίο το είδα πάλι να χρησιμοποιείται αλλά όχι και τόσο καλά το αντίθετο θα έλεγα. Aυτό δεν είναι από το sql server data type uniqueidentifier σαν primary key σε table.
    Ας δούμε λοιπόν τα πράγματα με την σειρά για να καταλάβουμε το πρόβλημα
    Φτιάχνουμε μια νέα βάση που θα γίνει το πείραμα μας
    USE master
    GO

    CREATE DATABASE test
    GO

    Σε αυτή φτιάχνουμε ένα πίνακα με την εξής δομή


    USE test
    GO
    CREATE TABLE t1
    (
    id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY DEFAULT(NEWID()),
    aa INT IDENTITY NOT NULL,
    otherdata NCHAR(2000) DEFAULT (N'other data')
    )

    Ο πίνακας αυτός είναι έτσι φτιαγμένος ώστε να χωράνε δύο rows ανά page (1 page = 8K)


    Όπως βλέπετε έχω το πεδίο id το οποίο είναι uniquidentifier και το έχω ορίσει σαν primary key και επειδή αυτό πρέπει να παίρνει τιμές έχω βάλει σαν default την function NEWID() η οποία σε κάθε εκτέλεση της μας δίνει ένα μοναδικό GUID.


    Επίσης έχω βάλει ένα πεδίο aa το οποίο όπως βλέπετε είναι identity που σημαίνει ότι για κάθε έγγραφή δίνει σειριακό αριθμό ξεκινώντας από το ένα.


    Τέλος για φτιάξω τη λογική που ανέφερα παραπάνω να χωράνε δηλαδή δύο rows ανά σελίδα βάζω το πεδίο otherdata nchar(2000) ώστε να έχω ένα record length 4024 bytes απλά του βάζω μια default τιμή ώστε να έχει κάτι.


    Εκτελώντας το παραπάνω create command φτιάχνω τον πίνακα μου.


    Όπως είναι γνωστό ο SQL Server όταν φτιάχνω ένα πίνακα και του ορίζω το primary key αυτός φτιάχνει ένα unique index για να το υλοποιήσει. Στη περίπτωση δε που δεν υπάρχει ήδη clustered index αυτός είναι unique clustered index, όπως και στο παράδειγμα μας.


    Αυτό είναι εύκολα μπορούμε να το επιβεβαιώσουμε εκτελώντας την


    sp_helpindex t1
    GO

    index_name index_description index_keys
    ------------------------ ------------------------------------------------- ----------
    PK__t1__3213E83F7F60ED59 clustered, unique, primary key located on PRIMARY id

    Αφού λοιπόν έχουμε επιβεβαιώσει την υπαρξη του index ας έρθουμε να δούμε πως αυτό είναι φυσικά δομημένος, θυμίζω ότι ακόμα δεν έχουμε βάλει δεδομένα στον πίνακα μας. Αυτό μπορούμε να το δούμε εύκολα με τη χρήση ενός DMV το οποίο δεν είναι άλλο από το sys.dm_db_index_physical_stats.


    Έτσι εκτελώντας την παρακάτω εντολή


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t1'),1,NULL,'LIMITED')
    GO


    έχουμε το παρακάτω αποτέλεσμα


    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 0 0

    Παρατηρούμε ότι όλα τα πεδία μετά από index_id είναι μηδενικά και αυτό είναι σωστό καθώς δεν έχουμε ακόμα βάλει δεδομένα στο πίνακα μας.


    Ας έρθουμε όμως να βάλουμε δύο γραμμές σε αυτόν


    INSERT INTO t1 DEFAULT VALUES
    INSERT INTO t1 DEFAULT VALUES
    GO

    Και ας ξαναδούμε πως είναι τώρα ο index μας


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t1'),1,NULL,'LIMITED')
    GO

    Το αποτέλεσμα θα είναι το παρακάτω


    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 0 1

    Θα παρατηρήσουμε ότι το page_count είναι ίσο με το ένα. Άρα έχουμε δύο rows/page. Περίφημα μέχρι έδω!!!


    Ας βάλουμε ακόμα δύο γραμμές στον πίνακα μας


    INSERT INTO t1 DEFAULT VALUES
    INSERT INTO t1 DEFAULT VALUES
    GO

    Και ας ξαναδούμε την δομή του index μας.


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t1'),1,NULL,'LIMITED')
    GO

    Το αποτέλεσμα είναι το παρακάτω


    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 66,6666666666667 3

    Εδώ τώρα παρατηρώ κάτι πολύ περίεργο πρώτα από όλα ότι το row_count είναι ίσο με τρία!!! Και το avg_fragmentation_in_percent είναι ίσο με 66,6666666666667


    Αυτό δεν μου αρέσει καθόλου μα καθόλου και φυσικά δεν έχει καμία πιθανότητα να έχω ένα σωστά δομημένο index καθώς επίσης δεν πρόκειται να έχω το επιθυμητό αποτέλεσμα από την χρήση του index.


    Πριν προχωρήσω στην αποκάλυψη του γιατί αυτό έχει συμβεί θα βάλω ακόμα μερικές γραμμές στο πίνακα μου ώστε στο σύνολο να είναι εκατό


    INSERT INTO t1 DEFAULT VALUES
    GO 96

    Ας έρθω να δω τι γίνεται με τον index μου


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t1'),1,NULL,'LIMITED')
    GO

    Το αποτέλεσμα είναι το παρακάτω και είναι άκρως απογοητευτικό


    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 98,4615384615385 65

    Το fragementation είναι στο θεό αλλά έχω 65 σελίδες αντί για 50 που θα έπρεπε να έχω. Οι 15 αυτές σελίδες διαφορά είναι μόνο για τις 100 γραμμές και φυσικά είναι ένα αρκετά σημαντικό performance penalty καθώς αυτό σημαίνει ΙΟ.


    Ας δούμε όμως τα δεδομένα μας


    SELECT * FROM t1
    GO

    Το αποτέλεσμα είναι το παρακάτω


    id aa otherdata
    ------------------------------------ ----------- -----------
    F028B3AF-1415-4081-A006-06DDA3D9FAD2 66 other data
    D58551AF-B632-4297-90A2-10D97AB854D0 51 other data
    A28C272C-1FA3-4506-8C62-1230E0E6926F 75 other data
    2B510EF2-E831-4CC0-BF7C-18B08C932934 24 other data
    394B5429-C8CD-4360-9768-18CE5775A8AE 73 other data
    5950B503-923D-4F33-9B21-1BEECBA1286C 79 other data
    16132ED8-B445-4217-B1E0-1E2D9FA40302 84 other data
    9887FCE3-F229-4236-AEA0-1EBFB5A73CA5 25 other data
    147DEAA4-8725-4797-9EBC-2B341889F9BD 57 other data
    BACC0008-F522-44EE-8A76-2E11BFAFCF80 33 other data
    5DEFA505-EFC3-44AB-9746-2F3AF2838E2D 20 other data
    5BD55DF8-757D-47FA-A53C-35E9AE7B428C 87 other data
    17F09983-33CF-4C48-8D06-36B64CDE3371 98 other data
    9EF858ED-E7B1-4A57-9491-38CB31F62844 90 other data
    3D26646B-DBFC-4D7C-8D9B-419BFF7D0350 47 other data
    7E04E79D-B728-4D8C-8F9E-47700C332433 97 other data
    8B4D0F05-B1F8-40BD-A32C-4D039BACB511 21 other data
    B065A2C4-C4C2-4375-93C8-4DDEF719A278 67 other data
    21C7F7A4-5120-4690-A5B8-5022BDE15233 7 other data
    56ACA620-B7C0-47D1-8821-53D3F2FBDA71 22 other data
    F3D55D7F-F089-424C-AFC8-53E5A76F2B23 31 other data
    2DF49ED6-3113-43AD-A7F4-556CC3328DD8 27 other data
    A7770D81-6818-4A9E-B3FA-57F682DAFFE9 63 other data
    02A840B7-B761-477D-9EB3-598A07A2401F 13 other data
    483B2183-5E03-4618-A39E-5BEF31470CFF 48 other data
    0C702D0F-E9D2-4E63-A5FB-5F66395CA97B 85 other data
    84A84DC9-4F78-4F98-A676-61B89B045A06 76 other data
    03CF97FA-0232-4107-A4FF-61E6FC01CEEA 28 other data
    B1BFB116-44B2-4D2E-BAF6-629854E236EC 64 other data
    95D0F80C-D07D-48B5-93EE-63C1CF67E1DF 95 other data
    BAA630CA-1A98-4DDA-B69B-650FF989B623 55 other data
    9FB18286-7BE6-4BBF-A75D-663961D11F30 71 other data
    D7D4335E-33B7-4FBB-995F-66E15D770284 41 other data
    EBF8C763-6425-4078-9BEF-6A7ADB618F49 58 other data
    2DF2516A-076C-458E-A61C-6A8D379A0F8A 78 other data
    85745D8E-122C-458A-A25C-6B203EEDDDD0 6 other data
    3B53D6A3-5E3D-4672-8FF5-6B725DD50094 77 other data
    F2A31C7E-F409-4FA6-A5AB-6F86F5F0CD21 42 other data
    A4318A22-9055-4FB1-B8BE-71497C1B203B 43 other data
    73923CE8-1F21-471D-BE58-7173C47B8633 68 other data
    76A8FFB8-5D92-4E55-9910-71D773935753 70 other data
    2EB0B127-FF41-478D-A8EC-727FCD0420AB 4 other data
    846E931D-A3E8-4885-BF15-73DCDD8BF06E 49 other data
    5426A25C-B419-4BD0-A8AE-76DEF2BD22B8 91 other data
    3925CE47-DDD1-4689-A4D6-7A8EB3004D63 86 other data
    8176A903-4C10-4DDC-97B4-7C0889442F12 23 other data
    0F436108-401D-40A7-8136-7ED7D91A6A2E 39 other data
    FF52B83A-8FF2-4706-80C3-7F00DE29E0B3 54 other data
    3E29EB19-E709-45F1-9CDD-7FDBBB489DE1 36 other data
    379DEFC6-7757-4A85-B82F-839F88C30AE8 96 other data
    AC7C56BF-3C5E-4EFA-B7A2-83D5E6D18D9A 15 other data
    7543C2EA-EC03-486B-9AB4-84483430E610 74 other data
    83AAC12E-F4C3-48A8-9BF9-8869E6390A4B 8 other data
    4F502BD4-5C99-4DC3-8352-88DA9591B27A 18 other data
    20C8395B-2E91-43C7-8FE1-8B4298BCD4B9 16 other data
    45989F49-574A-4438-A20F-8B609DBF7586 88 other data
    4EB6040C-AC60-42A0-94C9-8BB9542EF541 100 other data
    6476E0AB-7121-4DA2-8888-8CCD5C40CA87 12 other data
    3AA1154A-F650-44DC-B88E-91837A65E60D 19 other data
    228578BB-ABAF-4706-8CE7-93DE05FC3DF2 45 other data
    5A1689CC-7BDA-40A5-82FC-9650D58739AE 30 other data
    058F97B1-7AF7-4F32-801F-9AE3B284A9BB 80 other data
    07E106C4-0307-4ABE-AF02-9C734506BAC8 92 other data
    636CD13F-212B-4EAF-86C2-9EC3A8733DF6 40 other data
    23C8B3AE-77F2-45DC-BCE3-A0442EC0F8B3 52 other data
    E35160E1-FAD0-4CB3-9FCF-A2B2278EC4AA 61 other data
    758B3131-4329-46B2-A6D8-A2DC2A56ED8B 29 other data
    DA36FFC6-ADBD-4A38-B236-A3DFA3B7828B 94 other data
    98E8BC0F-4ABF-4CB5-BF9A-A4616CB51C7A 34 other data
    1A95A814-4D04-4C76-B378-A7E56CE4C685 82 other data
    D78D5EB7-9516-44E2-A196-A94205B35A8A 3 other data
    2E8B0887-CEB2-4487-B6F0-AE614E587488 59 other data
    67CA7AF8-18D4-45A3-9D53-AEB9A0EF97C8 99 other data
    A35AB10B-3F47-4184-A6FA-B8E38495A8C0 5 other data
    FFBCDA30-3134-49AB-8CD3-B9C512E50B0B 56 other data
    567EAD09-BF58-4027-9ABC-BCB62767DFE4 93 other data
    F0BAA52F-E5CA-42C0-A6D3-BFB81531CAFA 62 other data
    972F5A38-4F74-43E4-BCE6-C357E7D953D5 65 other data
    3258AB31-3A35-46BA-A053-C7686F3281BD 37 other data
    D3C00476-8582-4D61-B198-C7B5711BEF00 81 other data
    F521BBF6-770A-41D0-8FA5-C8E0684FD7D5 26 other data
    0BAE24EF-E7FE-42DD-BEA1-CA02A4024D5B 69 other data
    AA6BD0D8-EC8C-4B21-B21B-D429331C56B8 11 other data
    998FDC1B-7CAE-40DF-9AFA-DA3FD8CDCADB 89 other data
    4352A25D-A50B-42C3-A241-DC19BD7207D3 60 other data
    3F393EF3-3A26-4D36-BD00-DE9B9C63097E 38 other data
    B8A37EFF-5121-499E-AA22-E0ABE9C6293D 9 other data
    20B7C188-751F-4D90-8B9E-E1D4889A3795 1 other data
    CB2765CC-0FDA-423F-A5FE-E420A37BF2D5 72 other data
    2CE206A3-5481-4DC8-A725-E497611DD8BF 14 other data
    B1F04446-A9FB-4A64-8462-E5D02E4B5C71 17 other data
    89E50E9D-D1F9-488F-ADF0-EB13BD8B9D3F 83 other data
    3189B3B3-3892-4307-916F-EBC3E77573EE 44 other data
    3427F572-F535-4BF4-ADCC-EDC7E4C98E39 46 other data
    C93EDF08-35B4-430F-80B3-EE2605439DEE 2 other data
    15E0FBF2-18EF-41E4-938F-EFE7F0CA76F5 53 other data
    F665CAF8-C9BC-45D3-AD2B-F3E630FD8C5F 10 other data
    17C1CB7F-6C98-4A3D-9CD5-F883312B47C8 50 other data
    0191B624-C765-4C41-8FE3-FC0C07B82609 35 other data
    006D1851-23AC-4325-AD48-FE76F73F31BC 32 other data

    Αν προσεκτικά παρατηρήσουμε το αποτέλεσμα θα δούμε ότι τα rows έχουν ταξινομηθεί με βάση το id αλλά δεν έχουν μπει όμως το ένα κάτω από το άλλο καθώς αυτό φαίνεται από το πεδίο aa το οποίο μας δείχνει την σειρά εισαγωγής του row στο πίνακα.


    Αν μείνουμε μέχρι εδώ βγαίνει το συμπέρασμα ότι η χρήση του sql server data type σαν primary key σε ένα πίνακα είναι κακή, κάκιστη θα έλεγα επιλογή. Όμως δεν είναι έτσι τα πράγματα. Για αυτό ας κάνουμε ακόμα ένα πείραμα.


    Ας φτιάξουμε ένα ακόμα πίνακα όπως παρακάτω


    USE test
    GO

    CREATE TABLE t2
    (
    id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY DEFAULT(NEWSEQUENTIALID()),
    aa INT IDENTITY NOT NULL,
    otherdata NCHAR(2000) DEFAULT (N'other data')
    )

    Αυτός είναι ακριβώς ίδιος με το προηγούμενο αλλά υπάρχει μια σημαντική διαφορά αντί να χρησιμοποιώ την NEWID() χρησιμοποιώ την NEWSEQUENTIALID() (είναι διαθέσιμη από την έκδοση του SQL Server 2005!!!).


    Aς επιβεβαιώσουμε ότι έχουμε τον index μας


    sp_helpindex t2
    GO

    index_name index_description index_keys
    ------------------------ ------------------------------------------------- ----------
    PK__t2__3213E83F0519C6AF clustered, unique, primary key located on PRIMARY id

    Και ας δούμε την δομή του index μας


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t2'),1,NULL,'LIMITED')
    GO

    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 0 0

    Ας βάλουμε τις δύο πρώτες γραμμές στο πίνακα μας


    INSERT INTO t2 DEFAULT VALUES
    INSERT INTO t2 DEFAULT VALUES
    GO

    Και ας δούμε την δομή του index μας


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t2'),1,NULL,'LIMITED')
    GO

    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 0 1

    Όλα όπως βλέπουμε είναι φυσιολογικά. Ας βάλουμε τις επόμενες δύο γραμμές στον πίνακα μας


    INSERT INTO t2 DEFAULT VALUES
    INSERT INTO t2 DEFAULT VALUES
    GO

    Και να δούμε τη δομή του index


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t2'),1,NULL,'LIMITED')
    GO

    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 50 2

    Όλα πάλι είναι φυσιλογικά έχω page_count ίσο με 2 που είναι και το φυσιλογικό μιας και έχω τέσσερεις γραμμές. Το avg_fragmentation_in_percent είναι υψηλό (50%) αλλά για να δούμε τι θα γίνει με αυτό αν βάλουμε στο πίνακα μας ακόμα 96 εγγραφές ώστε στο σύνολο να γίνουν 100?


    INSERT INTO t2 DEFAULT VALUES
    GO 96

    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t2'),1,NULL,'LIMITED')
    GO

    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 4 50

    Όπως βλέπουμε αυτό έχει μειωθεί σημαντικά αλλά το περισσότερο σημαντικό είναι ότι ο αριθμός των σελίδων (page_count) έχει παραμείνει σωστός (50 σελίδες) δηλαδή 2 rows/page.


    Για να δούμε και τα δεδομένα μας στο πίνακα


    SELECT * FROM t2
    GO

    id aa otherdata
    ------------------------------------ ----------- ----------
    A6D5C824-7922-E011-A143-00155D020606 1 other data
    A7D5C824-7922-E011-A143-00155D020606 2 other data
    D439AA51-7922-E011-A143-00155D020606 3 other data
    D539AA51-7922-E011-A143-00155D020606 4 other data
    2C2FEF8C-7922-E011-A143-00155D020606 5 other data
    2D2FEF8C-7922-E011-A143-00155D020606 6 other data
    2E2FEF8C-7922-E011-A143-00155D020606 7 other data
    2F2FEF8C-7922-E011-A143-00155D020606 8 other data
    302FEF8C-7922-E011-A143-00155D020606 9 other data
    312FEF8C-7922-E011-A143-00155D020606 10 other data
    322FEF8C-7922-E011-A143-00155D020606 11 other data
    332FEF8C-7922-E011-A143-00155D020606 12 other data
    342FEF8C-7922-E011-A143-00155D020606 13 other data
    352FEF8C-7922-E011-A143-00155D020606 14 other data
    362FEF8C-7922-E011-A143-00155D020606 15 other data
    372FEF8C-7922-E011-A143-00155D020606 16 other data
    382FEF8C-7922-E011-A143-00155D020606 17 other data
    392FEF8C-7922-E011-A143-00155D020606 18 other data
    3A2FEF8C-7922-E011-A143-00155D020606 19 other data
    3B2FEF8C-7922-E011-A143-00155D020606 20 other data
    3C2FEF8C-7922-E011-A143-00155D020606 21 other data
    3D2FEF8C-7922-E011-A143-00155D020606 22 other data
    3E2FEF8C-7922-E011-A143-00155D020606 23 other data
    3F2FEF8C-7922-E011-A143-00155D020606 24 other data
    402FEF8C-7922-E011-A143-00155D020606 25 other data
    412FEF8C-7922-E011-A143-00155D020606 26 other data
    422FEF8C-7922-E011-A143-00155D020606 27 other data
    432FEF8C-7922-E011-A143-00155D020606 28 other data
    442FEF8C-7922-E011-A143-00155D020606 29 other data
    452FEF8C-7922-E011-A143-00155D020606 30 other data
    462FEF8C-7922-E011-A143-00155D020606 31 other data
    472FEF8C-7922-E011-A143-00155D020606 32 other data
    482FEF8C-7922-E011-A143-00155D020606 33 other data
    492FEF8C-7922-E011-A143-00155D020606 34 other data
    4A2FEF8C-7922-E011-A143-00155D020606 35 other data
    4B2FEF8C-7922-E011-A143-00155D020606 36 other data
    4C2FEF8C-7922-E011-A143-00155D020606 37 other data
    4D2FEF8C-7922-E011-A143-00155D020606 38 other data
    4E2FEF8C-7922-E011-A143-00155D020606 39 other data
    4F2FEF8C-7922-E011-A143-00155D020606 40 other data
    502FEF8C-7922-E011-A143-00155D020606 41 other data
    512FEF8C-7922-E011-A143-00155D020606 42 other data
    522FEF8C-7922-E011-A143-00155D020606 43 other data
    532FEF8C-7922-E011-A143-00155D020606 44 other data
    542FEF8C-7922-E011-A143-00155D020606 45 other data
    552FEF8C-7922-E011-A143-00155D020606 46 other data
    562FEF8C-7922-E011-A143-00155D020606 47 other data
    572FEF8C-7922-E011-A143-00155D020606 48 other data
    582FEF8C-7922-E011-A143-00155D020606 49 other data
    592FEF8C-7922-E011-A143-00155D020606 50 other data
    5A2FEF8C-7922-E011-A143-00155D020606 51 other data
    5B2FEF8C-7922-E011-A143-00155D020606 52 other data
    5C2FEF8C-7922-E011-A143-00155D020606 53 other data
    5D2FEF8C-7922-E011-A143-00155D020606 54 other data
    5E2FEF8C-7922-E011-A143-00155D020606 55 other data
    5F2FEF8C-7922-E011-A143-00155D020606 56 other data
    602FEF8C-7922-E011-A143-00155D020606 57 other data
    612FEF8C-7922-E011-A143-00155D020606 58 other data
    622FEF8C-7922-E011-A143-00155D020606 59 other data
    632FEF8C-7922-E011-A143-00155D020606 60 other data
    642FEF8C-7922-E011-A143-00155D020606 61 other data
    652FEF8C-7922-E011-A143-00155D020606 62 other data
    662FEF8C-7922-E011-A143-00155D020606 63 other data
    672FEF8C-7922-E011-A143-00155D020606 64 other data
    682FEF8C-7922-E011-A143-00155D020606 65 other data
    692FEF8C-7922-E011-A143-00155D020606 66 other data
    6A2FEF8C-7922-E011-A143-00155D020606 67 other data
    6B2FEF8C-7922-E011-A143-00155D020606 68 other data
    6C2FEF8C-7922-E011-A143-00155D020606 69 other data
    6D2FEF8C-7922-E011-A143-00155D020606 70 other data
    6E2FEF8C-7922-E011-A143-00155D020606 71 other data
    6F2FEF8C-7922-E011-A143-00155D020606 72 other data
    702FEF8C-7922-E011-A143-00155D020606 73 other data
    712FEF8C-7922-E011-A143-00155D020606 74 other data
    722FEF8C-7922-E011-A143-00155D020606 75 other data
    732FEF8C-7922-E011-A143-00155D020606 76 other data
    742FEF8C-7922-E011-A143-00155D020606 77 other data
    752FEF8C-7922-E011-A143-00155D020606 78 other data
    762FEF8C-7922-E011-A143-00155D020606 79 other data
    772FEF8C-7922-E011-A143-00155D020606 80 other data
    782FEF8C-7922-E011-A143-00155D020606 81 other data
    792FEF8C-7922-E011-A143-00155D020606 82 other data
    7A2FEF8C-7922-E011-A143-00155D020606 83 other data
    7B2FEF8C-7922-E011-A143-00155D020606 84 other data
    7C2FEF8C-7922-E011-A143-00155D020606 85 other data
    7D2FEF8C-7922-E011-A143-00155D020606 86 other data
    7E2FEF8C-7922-E011-A143-00155D020606 87 other data
    7F2FEF8C-7922-E011-A143-00155D020606 88 other data
    802FEF8C-7922-E011-A143-00155D020606 89 other data
    812FEF8C-7922-E011-A143-00155D020606 90 other data
    822FEF8C-7922-E011-A143-00155D020606 91 other data
    832FEF8C-7922-E011-A143-00155D020606 92 other data
    842FEF8C-7922-E011-A143-00155D020606 93 other data
    852FEF8C-7922-E011-A143-00155D020606 94 other data
    862FEF8C-7922-E011-A143-00155D020606 95 other data
    872FEF8C-7922-E011-A143-00155D020606 96 other data
    882FEF8C-7922-E011-A143-00155D020606 97 other data
    892FEF8C-7922-E011-A143-00155D020606 98 other data
    8A2FEF8C-7922-E011-A143-00155D020606 99 other data
    8B2FEF8C-7922-E011-A143-00155D020606 100 other data

    Αν παρατηρήσουμε τα δεδομένα θα δούμε ότι αυτά είναι ταξινομημένα με το id αλλά έχουμε σωστή και την σειρά καταχωρήσης, δηλαδή το πρώτο record μπήκε πρώτο και το id στην ταξινόμηση είναι πρώτο το δεύτερο το τρίτο, …, το εκατοστό.


    Όμως αυτό δεν είναι ο λόγος που τώρα είναι όλα καλά.


    Όπως γνωρίζουμε στο leaf level του index είναι ο index ταξινομημένος. Επίσης επειδή ο index μας είναι clustered στην ουσία το leaf level και τα data pages είναι το ίδιο και το αυτό.


    Στην πρώτη περίπτωση με την χρήση της NEWID(), η οποία κάθε φορά επιστρέφει ένα τυχαίο GUID, σαν default value έχω το εξής φαινόμενο


    Με το που βάζω το πρώτο record αυτό μπαίνει στη σελίδα πχ 1 το δεύτερο μπαίνει και αυτό σε αυτή τη σελίδα, έτσι η σελίδα μας είναι ως εξής


    id aa otherdata
    ------------------------------------ ----------- ----------------------
    20B7C188-751F-4D90-8B9E-E1D4889A3795 1 other data
    C93EDF08-35B4-430F-80B3-EE2605439DEE 2 other data

    Όταν πάμε να βάλουμε το τρίτο record το οποίο είναι το παρακάτω


    D78D5EB7-9516-44E2-A196-A94205B35A8A 3 other data

    Το οποίο πρέπει να μπει πριν από το πρώτο έτσι πρέπει να γίνει page split στην πρώτη σελίδα και επειδή δεν έχουμε ορίζει κάποιο fillfactor στον index (default ίσο 0 που σημαίνει ότι αφήνει χώρο ώστε να μπορεί να μπει ακόμα μία έγγραφή) δημιουργούνται τρεις σελίδες όπου η κάθε μια έχει από ένα record


    Page 1
    id aa otherdata
    ------------------------------------ ----------- ------------
    D78D5EB7-9516-44E2-A196-A94205B35A8A 3 other data
    Page 2
    id aa otherdata
    ------------------------------------ ----------- ------------
    20B7C188-751F-4D90-8B9E-E1D4889A3795 1 other data
    Page 3
    id aa otherdata
    ------------------------------------ ----------- ------------
    C93EDF08-35B4-430F-80B3-EE2605439DEE 2 other data

    Βάζοντας και την τέταρτη εγγραφή η δομή του index είναι η παρακάτω


    Page 1
    id aa otherdata
    ------------------------------------ ----------- ---------------
    D539AA51-7922-E011-A143-00155D020606 4 other data
    D78D5EB7-9516-44E2-A196-A94205B35A8A 3 other data
    Page 2
    id aa otherdata
    ------------------------------------ ----------- ---------------
    20B7C188-751F-4D90-8B9E-E1D4889A3795 1 other data
    Page 3
    id aa otherdata
    ------------------------------------ ----------- --------------
    C93EDF08-35B4-430F-80B3-EE2605439DEE 2 other data

    Εξου και το αποτέλεσμα που πήραμε και είδαμε ότι είχαμε 3 σελίδες. Αυτό συμβαίνει σε όλες τις άλλες εγγραφές με την ίδια λογική και έτσι έχουμε το τελικό αποτέλεσμα με το μεγάλο fragmentation και τις 65 σελίδες.


    Στο δεύτερο πίνακα όμως δεν συμβαίνει κάτι τέτοιο καθώς η NEWSEQUENTIALID() δίνει σειριακά GUIDs. Έτσι δεν έχουμε ούτε ουσιαστικά μεγάλο fragmentation ούτε παραπάνω αριθμό σελίδων.


    Συμπέρασμα


    Το πεδίο uniqueidentifier δεν έχει απολύτως κανένα πρόβλημα να είναι primary key σε ένα πίνακα (κατ’ επέκταση θα έλεγα ότι δεν έχει κανένα πρόβλημα να είναι clustered key) ΑΡΚΕΙ να γεμίζει με την NEWSEQUENTIALID() function και όχι με την NEWID() function.
  24. antonch
    Πριν προχωρήσω στο αντικείμενο που θέλω να παρουσιάσω θα ήθελα να περιγράψω κάποιες γνωστές έννοιες που θεωρώ ότι είναι καλό να επαναληθούν, μιας και η επανάληψη είναι η μητέρα της μάθησης όπως έλεγαν οι πρόγονοι μας.
    Κάθε application ( και με αυτό τον όρο συμπεριλαμβάνω και τα services ) μπορεί να εκτελεσθεί πολλές φορές. Κάθε εκτέλεση του application είναι ένα instance.
    Κάθε instance που τρέχει είναι ένα process, το οποίο στην ουσία είναι ένας container από ένα ή πολλα threads και τα οποία είναι προγραμματισμένα να κάνουν χρήση του επεξεργαστή για ένα συγκεκριμένο χρονικό διάστημα (time slice) το οποίο είναι οριζόμενο από το λειτουργικό σύστημα.
    Τα threads επιτρέπουν στην εφαρμογή να χρησιμοποιεί με το καλύτερο δυνατό τρόπο τον επεξεργαστή, είτε αυτός είναι ένας, είτε περισσότεροι.
    Τα Windows είναι ένα preemptive (με προτίμηση λόγω κατοχής) multitasking (πολυεπεξεργαστικό) λειτουργικό σύστημα.
    Αυτό σημαίνει ότι σε κάθε περίπτωση που τρέχουμε ένα application το λειτουργικό σύστημα δίνει ένα χρονικό διάστημα στο thread για να τρέξει. Όταν αυτό το χρονικό διάστημα περάσει ή κάποιο άλλο thread με προτεραιότητα μεγαλύτερη (high-priority) από αυτό που τρέχει την δεδομένη χρονική στιγμή, θέλει να τρέξει, τότε το λειτουργικό σύστημα αποθηκεύει την contextual information (συναφή πληροφορία) του τρέχοντος thread, το αντικαθιστά ή το σταματά και φορτώνει την contextual information από κάποιο άλλο thread ώστε αυτό να τρέξει με την σειρά του για το χρονικό διάστημα που του αναλογεί. Αυτό είναι το γνωστό σαν context switching και όλη η διαδικασία σαν Windows preemptive scheduling.
    User Mode Scheduler (UMS)
    Από την έκδοση 6.5 ο SQL Server χρησιμοποιεί ιδανικά το Windows scheduling. Όμως αυτό είναι γενικού σκοπού και για όλα τα application και όπως είναι φυσικό περιορίζει τo scalability (κλιμάκωση) που ο SQL Server προσπαθεί να επιτύχει. Η προσέγγιση του Windows preemptive sheduling έχει σαν αποτέλεσμα το context switching το οποίο είναι ακριβό σαν διαδικασία μιας και εναλλάσετε μεταξύ του user και του kernel mode του επεξεργαστή.
    Έτσι ένα process όπως του SQL Server το οποίο δημιουργεί και χρησιμοποιεί πολλά threads η εκτεταμένη χρήση του context switching έχει αρνητική επίδραση στο γενικότερο performance αλλά και το scalability του. Αυτός είναι και ο λόγος για τον οποίο η ομάδα ανάπτυξης του SQL Server αποφάσισε ότι ο ίδιος o SQL Server πρεπει να κάνει δικό του scheduling. Έτσι ο SQL Server γνωρίζει και ορίζει καλύτερα τις δικιές του ανάγκες για scheduling από ότι το λειτουργικό. Ακόμα θα πρέπει να τονίσω ότι ο SQL Server κάνει καλύτερη υλοποίηση όσον αφορά το thread scheduling και αποφεύγει σχεδόν πάντα το context switching.
    Από την έκδοση 7.0 του SQL Server παρουσιάστηκε η έννοια του User Mode Scheduler (UMS) o οποίος είναι ένα thin layer πάνω από το λειτουργικό σύστημα με πρωταρχικό σκόπο να βελτιστοποιήσει το thread management του SQL Server, ελαχιστοποιόντας το context switching και προσπαθώντας να κρατήσει όσο το δυνατόν τα SQL Server schedules στο user mode.
    Έτσι στο binn folder της εγκατάστασης του SQL Server 7.0 θα βρείτε ένα αρχείο to ums.dll, το οποίο είναι η υλοποίηση του UMS.
    Εδώ θα πρέπει να τονίσω ότι δεν θα γινόταν και τίποτα σπουδαίο αν το UMS εκτός από το scheduling δεν αφαιρούσε από το λειτουργικό χαρακτηριστικά που είναι system dependent όπως τα fibers και asynchronous Ι/Ο.
    Αλλά για να δούμε πως το UMS καταφέρνει να επιβληθεί πάνω στο Windows scheduling;
    UMS έχει την "εμπιστοσύνη" των Windows ότι όλα τα threads εκτός από αυτό που το ίδιο θέλει δεν είναι εφαρμόσιμο ("not viable"). Εαν ένα thread βρήσκεται σε ένα infinite wait state, αυτό σημαίνει ότι εάν ένα thread καλέσει την WaitForSingleObject και περάσει INFINITE σαν timeout value, τα Windows εκλαμβάνουν το thread αυτό σαν "not viable" για scheduling και το αγνοούν. Ο μόνος τρόπος για να ξυπνήσει ένα τέτοιο thread είναι στείλω ένα σήμα στο thread's event object.
    Στο λειτουργικό σύστημα φαίνεται μόνο ένα SQL Server thread ανα επεξεργαστή να είναι ενεργό κάθε φορά. Έτσι ακόμα και εάν έχουμε εκατοντάδες worker threads σε μια δεδομένη χρονική στιγμή, μόνο ένα από αυτά για κάθε επεξεργαστή εμφανίζετε στα Windows να κάνει κάτι.
    Ο Windows scheduler είναι όπως είπαμε preemptive. Από την άλλη μεριά ο UMS προσπαθεί και επιτυχώς ακολουθεί ένα cooperative model και είναι ένας non-preemptive scheduler.
    Ο UMS βασίζεται σε αυτό που λεμε "on threads to yield voluntarily" το οποίο σε ελεύθερη μετάφραση θα το πω "στην εθελούσια παραίτηση του thread".
    O UMS χρησιμοποιεί την προσέγγιση αυτή ώστε να χρησιμοποιήσει τον Windows kernel μόνο όταν είναι πραγματικά απαραίτητος.
    Το UMS cooperative scheduling απαίτησε να γραφτεί τέτοιος κώδικας από το SQL Server development team που θα μου επιτραπεί να τον χαρακτηρίσω λίρα εκατό. Αλλά αυτό άξιζε τον κόπο μιας καταφέρνει να είναι πιο αποτελεσματικο από του λειτουργικού αφού είναι κομένο και ραμένο στα μέτρα του SQL Server.
    Έτσι όταν ένα thread υποχωρεί είτε επειδή τελειώσε το task του είτε επειδή εκτέλεσε κώδικα που αφορούσε κλήση σε μια απο τις yield functions του UMS, αυτός ελέγχει την λίστα με τα threads τα οποία είναι έτοιμα να τρέξουν και λέει στο πρώτο διαθέσιμο οτι μπορεί να τρέξει. Έτσι με αυτό τον τρόπο όλα γίνονται στο user mode και αποφεύγεται το switching στο kernel mode.
    Όταν ο SQL Server ξεκινά, για κάθε επεξεργαστή δημιουργήτε ένας UMS. Κάθε UMS συντηρεί τα ακόλουθα 5 πράγματα:
    Worker List Η λίστα αυτή είναι τα διαθέσιμα threads ή fibers. Ο αριθμός των διαθέσιμων threads ορίζεται από αυτόν που έχουμε ορίσει στο max worker threads option κάνοντας χρήση της sp_configure. Εαν για παράδειγμα έχουμε ορίσει το max worker threads σε 255 σε ένα σύστημα με 8 επεξεργαστές , κάθε επεξεργαστής και κατα συνέπεια κάθε UMS μπορεί να έχει 32 workers (UMS workers). Κάθε ένας UMS worker μπορεί να υποστηρίξει πολλαπλούς χρήστες ή SPIDs.
    Runnable List Η λίστα αυτή δείχνει τους UMS workers που είναι έτοιμοι να εκτελέσουν κάποιο υπαρκτό request. Έτσι όταν ένας UMS worker υποχωρήσει σαν αποτέλεσμα της διαδικασίας υποχώρησης που περιέγραψα πιο πάνω ελεγχετε η λίστα και δίνει σήμα στο επόμενο UMS worker να προχωρήσει με αυτό που έχει να κάνει.
    Resource Waiter List Όταν ένας UMS worker ζητήσει κάποιο resource το οποίο είναι δεσμευμένο από κάποιον άλλον UMS worker μπαίνει στην συγκεκριμένη λίστα οικιοθελώς και μπαίνει σε infinite wait state. Όταν ο UMS worker που έχει το δεσμευμένο resource είναι έτοιμος να το αποδεσμεύσει, κοιτάζει την συγκεκριμένη λίστα και βρήσκει τον πρώτο διαθέσιμο UMS worker που ενδιαφέρεται για το συγκεκριμένο resource τον μεταφέρει στην Runnable List και «ξυπνάει» τον πρώτο διαθέσιμο UMS worker της Runnable List.
    I/O List Είναι η λίστα με τα outstanding asynchronous I/O requests.
    Timer List Είναι η λίστα με τα UMS timer request η οποία στην ουσία λέει πόσο χρόνο θα περιμένω ένα resource μέχρι να γίνει time out.
    Στον SQL Server 7.0 & 2000, μπορείς να χρησιμοποιήσεις τo DBCC SQLPERF(umsstats) undocumented statement για να κάνεις monitor κάθε visible UMS στο σύστημα σου.
    Στον SQL Server 2005 & 2008, μπορείς να χρησιμοποιήσεις την sys.dm_os_schedulers dynamic management view (DMV) για να δεις statistics για τους visible αλλά και για τους hidden schedulers στο σύστημα σου. Για περισσότερες λεπτομέρειες σχετικά με την DMV δείτε στο books online του SQL Server
    Τί είναι το SQLOS;
    Όπως είδαμε το UMS thread management έδωσε την δυνατότητα στον SQL Server να είναι self-managed και εύκολα μπορεί να γίνει scale εάν ένας νέος επεξεργαστής προστεθεί στο σύστημα μας. Από την έκδοση 7.0 του SQL Server όπου για πρώτη φορά παρουσιάσθηκε ο UMS πολλά εσωτερικά engines όπως , ο memory manager, το storage engine, και το relational engine έγιναν upgrade με built-in adaptive algorithms και self-tuning capabilities. Αυτό έδωσε την δυνατότητα στον SQL Server 7.0 όταν βγήκε στην αγορά το 1998 να είναι το πρώτο enterprise database engine το οποίο ήταν δυνατόν να κάνει automatic configuration και dynamic self-tuning.
    Στον SQL Server 2005 η Microsoft πήγε το self-managing και το self-tuning σε ακόμα υψηλότερο επίπεδο. Αυτό είχε σαν αποτέλεσμα ένα νέο component το SQLOS να γεννηθεί..
    Το SQLOS είναι ένα layer το οποίο κάθεται πάνω από το λειτουργικό και είναι υπεύθυνο για την διαχείριση των πόρων του λειτουργικού συστήματος που είναι όμως για τον SQL Server.
    To SQLOS δίνει στον SQL Server την δυνατότητα να εξυπηρετεί τις εσωτερικές του ανάγκες για πόρους καλύτερα και σε μεγαλύτερο εύρος.
    Κάθε component μέσα στο SQLOS είναι έτσι φτιαχμένο ώστε να κάνει κάποιο συγκεκριμένη εργασία καθώς επίσης να συνεργάζεται ομαλά με όλα τα άλλα componets του SQLOS, ώστε να δίνεται ένα large-scale performance προσαρμοζόμενο φυσικά σε διαφορετικά hardware configurations, όπως 32-bit, 64-bit, x64, dual core chips, και large memory addressability.
    Με άλλα λόγια δεν χρειάζεται να γίνουν αλλαγές στο SQL Server configuration ώστε να γίνει το SQLOS adapt to hardware resources ενώ από την άλλη παρέχει ένα άνευ προηγουμένου scalability.
    Εδώ θα πρέπει να επισημάνω ότι στον SQL Server 2005 δεν υπάρχει το ums.dll μιας και αυτό είναι ένα από τα components του SQLOS και αναφέρεται πλέον σαν "Non Preemptive Scheduler"

    Όπως βλέπουμε στην παραπάνω είκόνα υπάρχουν δύο μεγάλα components του SQLOS το non-preemptive scheduling (το γνωστό UMS) και το memory management. Υπάρχουν όμως και άλλα components όπως το resource monitor, ο exception handler, και τα hosting subsystems. Το SQLOS παντρεύει όλα αυτά τα system components και δίνει την δυνατότητα ένος συνεκτικού API που δίνει την δυνατότητα στο SQL Server development team εύκολα να εκμεταλλευθεί τα χαρακτηριστικά του hardware και του λειτουργικού συστήματος.
    Non-Preemptive Scheduling
    Το non-preemptive scheduling component του SQLOS χρησιμοποιήτε για να κάνει scheduling και synchronizing τα concurrent tasks χωρίς να χρειασθεί να κάνει κλήση στο Windows kernel. Είναι υπεύθυνο για το ιδανικό scheduling των Windows threads ή των fibers.
    Ο scheduler είναι υπέθυνος για την διαχείριση των scheduling process και διασφαλίζει οτι μόνο ένα thread processor είναι ενεργό σε κάθε δεδομένη χρονική στιγμή.
    Η sys.dm_os_schedulers DMV είναι αυτή που δείχνει την λίστα των schedulers.
    Ένα collection από schedulers το οποίο παρέχει ένα abstraction layer πάνω από ένα group επεξεργαστών ονομάζεται scheduling node. Με τον όρο αυτός αναφερόμαστε σε ένα unit of work το οποίο είναι scheduled από τον SQL Server.
    Ένα Transact-SQL (T-SQL) statement batch μπορεί να δείχνει σε ένα ή περισσότερα tasks.
    Για παράδειγμα ένα parallel query εκτελείται με πολλαπλά tasks, τα οποία εκτελούνται μέσω worker threads.
    Ένα worker thread αντιπροσωπεύει ένα logical thread στον SQL Server το οποίο εσωτερικά αντιστοιχίζεται (1:1) είτε σε ένα Windows thread είτα σε fiber (εαν έχω ενεργοποιήσει το lightweight pooling στον SQL Server).
    H αντιστοιχία του worker thread σε Windows thread υπάρχει μέχρι το worker thread να γινει deallocated ή επειδή υπάρχει έλειψη μνήμης ή είναι idle για μεγάλο χρονικό διάστημα. Τα Worker threads εκτελούνται και διαχειρίζονται από τα system threads.
    The max worker threads Option
    Κάθε instance του SQL Server συντηρεί ένα pool από Windows threads ή fibers με σκοπό να κάνει processing τα user queries. Αυτό το thread pool βοηθάει στο να γίνει optimize το performance όταν ένας μεγάλος αριθμός από clients είναι συνδεδεμένοι στον server. Το maximum size του pool αυτού ορίζεται από το max worker threads στα server configuration option.
    Η minimum και τα default values για το max worker threads option (το οποίο για να μην ξεχάσω είναι στα advanced options) έχει αλλάξει στον SQL Server 2005.
    Στον Server 2000 η minimum τιμή που μπορούσες να ορίσεις είναι 32, στον SQL Server 2005 είναι 128.
    Στον SQL Server 2000 η default τιμή είναι 255, στον SQL Server 2005 είναι 0. Αυτό σημαίνει ότι μόνος του ο SQL Server θα κάνει αυτόματα configure τον αριθμό των worker threads κατα το startup του service. Εάν είναι 0 τότε ο SQL Server 2005 χρησιμοποιεί την παρακάτω formula ανάλογα με το είμαι σε x32 ή x64 πλατφόρμα.
    Η formula είναι
    # of CPUs
    x32
    x64

    256
    512
    >4
    256 + ( (#CPUs - 4) * 8 )
    512 + ( (#CPUs – 4) * 8 )
    Ενδεικτικά αναφέρω

    Number of CPUs
    32-bit computer
    64-bit computer

    256
    512
    8 processors
    288
    576
    16 processors
    352
    704
    32 processors
    480
    960
    Εδώ είναι θεωρώ σημαντικό να αναφέρω ότι ο SQL Server στην πραγματικότητα δεν δημιουργεί όλα τα threads αλλα δεσμεύει την μνήμη που χρειάζεται για την δημιουργία του αριθμού των threads που έχουν ορισθεί με την max worker threads.
    Εαν τώρα ο αριθμός των χρηστών που είναι συνδεδεμένος την δεδομένη χρονική στιγμή είναι μικρότερος από τον αριθμός που έχει ορισθεί στην max worker threads τότε ένα thread κάνει handle ένα user connection (1:1). Εάν είναι μεγαλύτερος τότε ο SQL Server κάνει pooling ώστε το επόμενο διαθέσιμο worker thread να ικανοποιήσει το request.
    Routing Tasks to a Scheduler
    O SQL Server 2000 χρησιμοποιούσε για τον scheduler τον αλγόριθμο round-robin κατά την στιγμή του connection. Έτσι όλα τα batches στο ίδιο connection τρέχαν στον ίδιο scheduler, αυτό όπως γίνεται κατανοητό μπορούσε να οδηγήσει σε αστάθεια ιδιαίτερα με τα long-lived connections.
    Στον SQL Server 2005, η απόφαση για την επιλογή του scheduler βασίζεται στο πόσο απασχολημένος είναι αυτός.
    Με άλλα λόγια ένα task στον SQL Server 2000 πήγαινε πάντα σε έναν συγκεκριμένο scheduler, στον SQL Server 2005, η επιλογή για το σε ποιον θα πάει το task γίνεται με το ποσο φορτωμένος είναι.
    Κατά τη στιγμή του connection o SQL Server 2005 διαλέγει τον λιγότερο φορτωμένο scheduler. Στο επόμενο batch στο ίδιο connection χρησιμοποιεί τον ίδιο αλογόριθμο έτσι εαν ο τρεχων scheduler είναι ο ποιο φορτωμένος διαλέγει το λιγότερο φορτωμένο.
    Τα πεδία όπως το load_factor και runnable_tasks_count της sys.dm_os_schedulers μπορουν να χρησιμοποιηθούν για την εύρεση του πόσο απασχολημένος είναι ένας scheduler.
    Συνεχίζεται…
  25. antonch
    Τόσα χρόνια στον χώρο της πληροφορικής έχω μάθει να προσπαθώ να καταλάβω τι γίνεται πίσω από την σκηνή με αυτό που ασχολούμαι. Ο SQL Server είναι ένα από αυτά, και στο οποίο έχω αφιερώσει αρκετές τρίχες της κεφαλής μου. Σήμερα θα σας πάρω λίγο από τον πολύτιμο χρόνο σας για να σας μεταφέρω μια γνώση που θα σας φανεί αρκετά χρήσιμη.
    Όλοι λίγο ή πολύ έχετε γράψει ένα sql query. Άλλες φορές αυτό λειτούργησε άψογα άλλες φορές όχι. Το μυστικό για να γράψεις ένα καλό sql query είναι να έχεις κατανοήσει πως αυτό λογικά εκτελείται, ιδιαίτερα στην μηχανή που χρησιμοποιείς, στην δικιά μας περίπτωση ο SQL Server.
    Ένα sql query περιέχει κάποιες εκφράσεις μέσα του, εδώ θα ασχοληθούμε με τι βασικές, Έτσι ένα sql query είναι της μορφής αυτής. Στους αριθμούς μέσα στις παρενθέσεις φαίνεται η σειρά, βήμα, φάση εκτέλεσης.
    Κάθε βήμα δημιουργεί έναν virtual table ο οποίος είναι το input για το επόμενο βήμα. Αυτά το virtual tables δεν είναι διαθέσιμα σε κανένα πλήν του SQL Server, εκτός του τελευταίου που είναι και αυτό που παίρνουμε σαν απάντηση. Εάν στο sql query μας δεν έχουμε κάποια έκφραση απλά αυτή αγνοείτε και πάει στο επόμενο βήμα.
     
    (8) SELECT (9) DISTINCT (11)
    (1) FROM
    (3) JOIN
    (2) ON
    (4) WHERE
    (5) GROUP BY
    (6) WITH {CUBE | ROLLUP}
    (7) HAVING
    (10) ORDER BY
     
    Ας δούμε τα πράγματα λίγο αναλυτικά με ένα παράδειγμα που θα δημιουργήσουμε με το παρακάτω script
    SET NOCOUNT ON;
    USE tempdb;
    GO
    IF OBJECT_ID('dbo.Orders') IS NOT NULL
    DROP TABLE dbo.Orders;
    GO
    IF OBJECT_ID('dbo.Customers') IS NOT NULL
    DROP TABLE dbo.Customers;
    GO
    CREATE TABLE dbo.Customers
    (
    customerid CHAR(5) NOT NULL PRIMARY KEY,
    city VARCHAR(10) NOT NULL
    );

    INSERT INTO dbo.Customers(customerid, city) VALUES('ANTON', 'Athens');
    INSERT INTO dbo.Customers(customerid, city) VALUES('NASOS', 'Athens');
    INSERT INTO dbo.Customers(customerid, city) VALUES('FANIS', 'Athens');
    INSERT INTO dbo.Customers(customerid, city) VALUES('CHRIS', 'Salonica');

    CREATE TABLE dbo.Orders
    (
    orderid INT NOT NULL PRIMARY KEY,
    customerid CHAR(5) NULL REFERENCES Customers(customerid)
    );

    INSERT INTO dbo.Orders(orderid, customerid) VALUES(1, 'NASOS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(2, 'NASOS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(3, 'FANIS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(4, 'FANIS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(5, 'FANIS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(6, 'CHRIS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(7, NULL);
    Μετά από την εκτέλεση του θα έχουμε δύο πίνακες τους Customers, Orders οι οποίοι είναι related μεταξύ τους και τα δεδομένα τους θα είναι τα εξής
    Customers Table Data
    customerid
    city
    ANTON
    Athens
    CHRIS
    Salonica
    FANIS
    Athens
    NASOS
    Athens
     
    Orders Table Data
    Orderid
    customerid
    1
    NASOS
    2
    NASOS
    3
    FANIS
    4
    FANIS
    5
    FANIS
    6
    CHRIS
    7
    NULL
     
    Ας πάρουμε σαν σενάριο ότι θέλουμε να βούμε τους πελάτες της Αθήνας που έχουν κάτω από τρεις παραγγελίες.
    Έτσι το sql query μας θα είναι σαν αυτό.
    SELECT C.customerid, COUNT(O.orderid) AS numorders
    FROM dbo.Customers AS C
    LEFT OUTER JOIN dbo.Orders AS O
    ON C.customerid = O.customerid
    WHERE C.city = 'Athens'
    GROUP BY C.customerid
    HAVING COUNT(O.orderid) 3
    ORDER BY numorders;
    Το αποτέλεσμα της εκτέλεσης του είναι το παρακάτω
    Customerid
    numorders
    ANTON
    0
    NASOS
    2
     
    Ποιά είναι όμως η λογική του εκτέλεση; Ας δούμε λοιπόν τα βήματα της εκτέλεσης.
    Βήμα 1ο - Cross Join
    FROM dbo.Customers AS C ... JOIN dbo.Orders AS O
     
    Εδώ φτιάχνει το καρτεσιανό γινόμενο των δύο πινάκων και το βάζει στο 1ο virtual table (VT1). Το περιεχόμενο του πίνακα αυτού είναι 28 γραμμές ( 4x7).
    Customerid
    City
    Orderid
    customerid
    ANTON
    Athens
    1
    NASOS
    ANTON
    Athens
    2
    NASOS
    ANTON
    Athens
    3
    FANIS
    ANTON
    Athens
    4
    FANIS
    ANTON
    Athens
    5
    FANIS
    ANTON
    Athens
    6
    CHRIS
    ANTON
    Athens
    7
    NULL
    CHRIS
    Salonica
    1
    NASOS
    CHRIS
    Salonica
    2
    NASOS
    CHRIS
    Salonica
    3
    FANIS
    CHRIS
    Salonica
    4
    FANIS
    CHRIS
    Salonica
    5
    FANIS
    CHRIS
    Salonica
    6
    CHRIS
    CHRIS
    Salonica
    7
    NULL
    FANIS
    Athens
    1
    NASOS
    FANIS
    Athens
    2
    NASOS
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    FANIS
    Athens
    6
    CHRIS
    FANIS
    Athens
    7
    NULL
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    NASOS
    Athens
    3
    FANIS
    NASOS
    Athens
    4
    FANIS
    NASOS
    Athens
    5
    FANIS
    NASOS
    Athens
    6
    CHRIS
    NASOS
    Athens
    7
    NULL
     
    Βήμα 2ο - Apply Join condition ON Filter
    ON C.customerid = O.customerid
    Στο βήμα αυτό εφαρμόζεται το ON που υπάρχει στο Join και μόνο τα rows εκείνα τα οποία ικανοποιούν το βήμα πηγαίνουν στον VT2 που θα είναι το αποτέλεσμα του βήματος αυτού.
    Έτσι αν πάρω τον VT1 και εφαρμόσω στο παράδειγμα μας το ON θα έχω το εξής αποτέλεσμα
    Customerid
    City
    Orderid
    customerid
    ΟΝ Filter
    ANTON
    Athens
    1
    NASOS
    FALSE
    ANTON
    Athens
    2
    NASOS
    FALSE
    ANTON
    Athens
    3
    FANIS
    FALSE
    ANTON
    Athens
    4
    FANIS
    FALSE
    ANTON
    Athens
    5
    FANIS
    FALSE
    ANTON
    Athens
    6
    CHRIS
    FALSE
    ANTON
    Athens
    7
    NULL
    UNKNOWN
    CHRIS
    Salonica
    1
    NASOS
    FALSE
    CHRIS
    Salonica
    2
    NASOS
    FALSE
    CHRIS
    Salonica
    3
    FANIS
    FALSE
    CHRIS
    Salonica
    4
    FANIS
    FALSE
    CHRIS
    Salonica
    5
    FANIS
    FALSE
    CHRIS
    Salonica
    6
    CHRIS
    TRUE
    CHRIS
    Salonica
    7
    NULL
    UNKNOWN
    FANIS
    Athens
    1
    NASOS
    FALSE
    FANIS
    Athens
    2
    NASOS
    FALSE
    FANIS
    Athens
    3
    FANIS
    TRUE
    FANIS
    Athens
    4
    FANIS
    TRUE
    FANIS
    Athens
    5
    FANIS
    TRUE
    FANIS
    Athens
    6
    CHRIS
    FALSE
    FANIS
    Athens
    7
    NULL
    UNKNOWN
    NASOS
    Athens
    1
    NASOS
    TRUE
    NASOS
    Athens
    2
    NASOS
    TRUE
    NASOS
    Athens
    3
    FANIS
    FALSE
    NASOS
    Athens
    4
    FANIS
    FALSE
    NASOS
    Athens
    5
    FANIS
    FALSE
    NASOS
    Athens
    6
    CHRIS
    FALSE
    NASOS
    Athens
    7
    NULL
    UNKNOWN
     
    Δηλαδή ο VT2 θα είναι τελικά ο παρακάτω
    Customerid
    City
    Orderid
    customerid
    CHRIS
    Salonica
    6
    CHRIS
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
     
    Βήμα 3ο - Apply OUTER Join
    FROM dbo.Customers AS C LEFT OUTER JOIN dbo.Orders AS O
    Στην περίπτωση μας μόνο ένας είναι ο πελάτης από την πίνακα τον πελατών που δεν υπάρχει στον VT2 o ΑΝΤΟΝ οπότε μπαίνει και αυτό στον VT3 που είναι το αποτέλεσμα του βήματος αυτού
    Customerid
    City
    Orderid
    customerid
    CHRIS
    Salonica
    6
    CHRIS
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    ΑΝΤΟΝ
    Athens
    NULL
    NULL
     
    Βήμα 4ο - Apply WHERE filter
    WHERE C.city = 'Athens'
    Το αποτέλεσμα είναι ο VT4
    Customerid
    City
    Orderid
    customerid
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    ΑΝΤΟΝ
    Athens
    NULL
    NULL
     
    Βήμα 5ο - Apply Grouping
    GROUP BY C.customerid
    Το αποτέλεσμα είναι ο VT5
    Customerid
    City
    Orderid
    customerid
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    ΑΝΤΟΝ
    Athens
    NULL
    NULL
     
    Βήμα 6ο - Apply Cube or Rollup
    Δεν έχουμε κάτι τέτοιο στο query μας οπότε πάει στο επόμενο βήμα.
    Βήμα 7ο - Apply HAVING Filter
    HAVING COUNT(O.orderid) 3
    Το αποτέλεσμα είναι ο VT7
    Customerid
    City
    Orderid
    customerid
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    ΑΝΤΟΝ
    Athens
    NULL
    NULL
     
    Βήμα 8ο - Apply SELECT List
    SELECT C.customerid, COUNT(O.orderid) AS numorders
    Το αποτέλεσμα είναι ο VT8
    Customerid
    numorders
    NASOS
    2
    ANTON
    0
    Βήμα 9ο - Apply DISTINCT
    Δεν έχουμε κάτι τέτοιο στο query μας οπότε πάει στο επόμενο βήμα.
    Βήμα 10ο - Apply ORDER BY
    SELECT C.customerid, COUNT(O.orderid) AS numorders
    Το αποτέλεσμα είναι ο VT10
    Customerid
    numorders
    ANTON
    0
    NASOS
    2
    Βήμα 11ο - Apply TOP
    Δεν έχουμε κάτι τέτοιο στο query μας οπότε καταλήγουμε στο τελικό μας αποτέλεσμα.
    ΥΓ. Επίτηδες παρέλειψα κάποια βήματα για να μην σας κουράσω. Αν υπάρχει απαίτηση από εσάς μέσω των σχολίων σας θα προχωρήσω βαθύτερα και σε αυτά
×
×
  • Create New...