Lorsqu’on tente d’ouvrir une connexion d’administrateur dédiée (DAC dans le jargon) à partir de l’explorateur d’objets de SQL Server Management Studio (SSMS), on obtient l’erreur suivante :
Lire la suite
Archives pour la catégorie Moteur de base de données SQL Server
Mise au point à propos du compteur de performance Page Life Expectancy
Parmi les compteurs de performance qu’expose SQL Server, il en existe quelques-uns qui ne laissent aucun doute sur les performances courantes d’une instance SQL Server. Page Life Expectancy est l’un d’entre eux, puisqu’il nous renseigne sur la durée de vie moyenne d’une page de données en RAM, en secondes.
L’ensemble des opérations de manipulation de données se faisant exclusivement en mémoire par tout SGBDR digne de ce nom, on comprend aisément que plus la valeur exposée par ce compteur est élevée, plus les performances de l’instance sont correctes. En effet, comme un accès en RAM est en pratique au moins 1000 fois plus rapide qu’un accès à des disques mécaniques, plus on peut traiter de données en RAM, moins on est exposé à des problèmes de performances.
Cela étant, les serveurs physiques actuels disposant de plusieurs sockets pour accueillir leurs CPUs, ils sont tous une implémentation de NUMA (Non-Uniform Memory Access), dans lequel chaque processeur dispose d’un bus mémoire dédié. On appelle nÅ“ud NUMA l’ensemble composé par l’espace mémoire et le processeur situés sur le même bus. On désigne par nÅ“ud NUMA distant un nÅ“ud NUMA distinct de celui sur lequel la requête s’exécute.
Si par le passé on pouvait donc se référer directement à la valeur de ce compteur, puisque les serveur ne disposaient que d’un seul socket, qu’en est-il avec les architectures multi-socket ? Comment suivre cette valeur sous SQL Server ? Quelle valeur de ce compteur peut servir de seuil d’alerte ?
Je vous propose la réponse à ces questions dans cet article !
Lire la suite
Une procédure stockée pour connaître l’état des fichiers du journal des transactions
Ha ! le fichier du journal des transactions, l’option de récupération, son grossissement, … et bien sûr la fameuse transaction restée ouverte qui a provoqué une explosion de la taille de celui-ci, allant jusqu’à remplir le volume disque qui l’héberge : tout autant de paramétrages et d’investigations fastidieuses.
Alors comme je n’aime pas beaucoup répéter plusieurs fois les mêmes opérations sans avoir un script, ou mieux, une procédure stockée sous la main, j’ai créé la suivante, dont voici le script de création :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | USE master GO ----------------------------------------------------------------- -- Nicolas Souquet - 09/02/2013 -- LA PROCEDURE STOCKEE SUIVANTE EST DONNEE EN TANT QUE TELLE -- SON UTILISATION ET SA MODIFICATION EST A VOS RISQUES ET PÉRILS ----------------------------------------------------------------- CREATE PROCEDURE [dbo].[sp__log_space_get] @_database_name sysname = NULL AS BEGIN SET NOCOUNT ON DECLARE @sql varchar(256) -- Récupération de l'espace disque disponible -- sur tous les volumes visibles par l'instance SQL Server DECLARE @free_disk_space TABLE ( drive_letter char(1) NOT NULL , free_space_MB int NOT NULL ) INSERT INTO @free_disk_space EXEC master.dbo.xp_fixeddrives -- Récupération de l'occupation des fichiers des journaux de transaction -- de toutes les bases de données hébergées par l'instance SQL Server DECLARE @dbcc_sqlperf_logspace TABLE ( database_name sysname , log_size float , log_space_used_pct decimal (5,2) , status tinyint ) INSERT INTO @dbcc_sqlperf_logspace EXEC ('DBCC SQLPERF(logspace)') -- Récupération de la liste de tous les fichiers virtuels -- du fichier du journal des transactions de toutes les bases de données -- disponibles, hébergées par l'instance SQL Server DECLARE @dbcc_loginfo TABLE ( recovery_unit_id tinyint -- new column in SQL Server 2012 , fileid tinyint NOT NULL , file_size bigint NOT NULL , start_offset bigint NOT NULL , f_seq_no bigint NOT NULL , status tinyint NOT NULL , parity tinyint NOT NULL , create_LSN varbinary(max) NOT NULL ) -- Cas où l'on souhaite réaliser l'audit du fichier du journal des transactions -- de toutes les bases de données hébergées par l'instance SQL Server -- Dans ce cas le paramètre d'entrée @_database_name est à NULL IF @_database_name IS NULL BEGIN DECLARE @dbcc_loginfo_all_db TABLE ( database_name varchar(128) , log_file_id tinyint , vlf_count smallint ) -- Récupération de liste de toutes les bases de données disponibles -- (pas une capture iOFFLINE, SINGLE_USER, ou ... SUSPECT ;)) INSERT INTO @dbcc_loginfo_all_db ( database_name , log_file_id , vlf_count ) SELECT D.name , MF.file_id , 0 FROM sys.databases AS D INNER JOIN sys.master_files AS MF ON D.database_id = MF.database_id WHERE D.source_database_id IS NULL -- ce n'est pas une capture instantanée de base de données AND D.state_desc = 'ONLINE' -- la base de données est disponible AND D.user_access_desc = 'MULTI_USER' -- la base de données est à l'écoute de connexions utilisateur AND MF.type_desc = 'LOG' -- le type de fichier est "journal des transactions" -- Pour chaque base de données, récupération de la liste des fichiers virtuels -- du fichier du journal des transactions pour toutes les bases de données disponibles WHILE EXISTS ( SELECT * FROM @dbcc_loginfo_all_db WHERE vlf_count = 0 ) BEGIN SELECT TOP 1 @_database_name = database_name FROM @dbcc_loginfo_all_db WHERE vlf_count = 0 SET @sql = 'DBCC LOGINFO (''' + @_database_name + ''')' INSERT INTO @dbcc_loginfo EXEC (@sql) ;WITH CTE AS ( SELECT fileid , COUNT(*) AS vlf_count FROM @dbcc_loginfo GROUP BY fileid ) UPDATE @dbcc_loginfo_all_db SET vlf_count = C.vlf_count FROM @dbcc_loginfo_all_db AS DLAD INNER JOIN CTE AS C ON DLAD.log_file_id = C.fileid WHERE DLAD.database_name = @_database_name DELETE FROM @dbcc_loginfo END -- Résultat final, avec addition de quelques détails SELECT LS.database_name , D.recovery_model_desc , MF.name AS logical_name , FDS.drive_letter AS transaction_log_file_stored_on , FDS.free_space_MB AS volume_free_space_MB , CAST(LS.log_size AS decimal(38,2)) AS transaction_log_file_size_MB , LS.log_space_used_pct , CAST((LS.log_space_used_pct / 100.0) * LS.log_size AS decimal(38,2)) AS log_used_size_MB , CASE MF.is_percent_growth WHEN 1 THEN CAST(MF.growth AS varchar(3)) + ' %' ELSE CAST(MF.growth / 128 AS varchar(20)) + ' MB' END AS growth , D.log_reuse_wait_desc , DLAD.vlf_count , MF.physical_name FROM sys.master_files AS MF INNER JOIN @dbcc_sqlperf_logspace AS LS ON LS.database_name = DB_NAME(MF.database_id) INNER JOIN @free_disk_space AS FDS ON FDS.drive_letter = LEFT(MF.physical_name, 1) COLLATE database_default INNER JOIN sys.databases AS D ON D.name = LS.database_name COLLATE database_default INNER JOIN @dbcc_loginfo_all_db AS DLAD ON DLAD.database_name = D.name COLLATE database_default AND DLAD.log_file_id = MF.file_id AND DLAD.database_name = DB_NAME(MF.database_id) WHERE MF.type_desc = 'LOG' AND D.source_database_id IS NULL AND D.state_desc = 'ONLINE' AND D.user_access_desc = 'MULTI_USER' END ELSE BEGIN -- Si la base de données que l'on souhaite auditer n'existe pas ==> exception IF DB_ID(@_database_name) IS NULL BEGIN RAISERROR('The database named ''%s'' does not exist', 16, 1, @_database_name) RETURN END -- récupération de la liste des fichiers virtuels -- du fichier du journal des transactions de la base de données choisie DECLARE @vlf_count int SET @sql = 'DBCC LOGINFO (''' + @_database_name + ''')' INSERT INTO @dbcc_loginfo EXEC (@sql) -- Résultat final, avec addition de quelques détails ;WITH CTE AS ( SELECT fileid , COUNT(*) AS vlf_count FROM @dbcc_loginfo GROUP BY fileid ) SELECT LS.database_name , D.recovery_model_desc , MF.name AS logical_name , FDS.drive_letter AS transaction_log_file_stored_on , FDS.free_space_MB AS volume_free_space_MB , CAST(LS.log_size AS decimal(38,2)) AS transaction_log_file_size_MB , LS.log_space_used_pct , CAST((LS.log_space_used_pct / 100.0) * LS.log_size AS decimal(38,2)) AS log_used_size_MB , CASE MF.is_percent_growth WHEN 1 THEN CAST(MF.growth AS varchar(3)) + ' %' ELSE CAST(MF.growth / 128 AS varchar(20)) + ' MB' END AS growth , D.log_reuse_wait_desc , C.vlf_count AS VLF_count , MF.physical_name FROM sys.master_files AS MF INNER JOIN @dbcc_sqlperf_logspace AS LS ON LS.database_name = DB_NAME(MF.database_id) INNER JOIN @free_disk_space AS FDS ON FDS.drive_letter = LEFT(MF.physical_name, 1) COLLATE database_default INNER JOIN sys.databases AS D ON D.name = LS.database_name COLLATE database_default INNER JOIN CTE AS C ON C.fileid = MF.file_id WHERE MF.type_desc = 'LOG' AND LS.database_name = @_database_name AND D.source_database_id IS NULL AND D.state_desc = 'ONLINE' AND D.user_access_desc = 'MULTI_USER' END END GO -- On marque la procédure stockée comme objet système -- Dès lors, on pourra l'exécuter dans n'importe quel contexte de base de données EXEC sp_ms_marksystemobject 'sp__log_space_get' |
Elle donne un résultat comme le suivant :
à savoir :
– le nom de la base de données
– l’option de récupération de la base de données
– le nom logique du fichier du journal des transactions (utile pour un DBCC SHRINKFILE après que le journal ait explosé en taille)
– le volume sur lequel est hébergé le fichier du journal des transactions
– la quantité d’espace disque libre restant sur ce volume
– la taille actuelle du fichier du journal des transactions
– le pourcentage d’utilisation de ce fichier (taille occupée dans le fichier par rapport à la taille totale du fichier)
– la taille équivalente à ce pourcentage
– le paramètre de grossissement de ce fichier
– ce qui empêche actuellement la troncature de l’espace occupé dans ce fichier
– le chemin complet du fichier
On peut utiliser cette procédure stockée sous SQL Server 2005, 2008 et 2012.
Elle fonctionne également pour les bases de données ayant plusieurs fichiers du journal des transactions
Bonne gestion des fichiers du journal des transactions !
ElSüket
Reprise d’activité étape par étapes : le poster de Paul Randal
Paul Randal est très connu dans la communauté SQL Server puisqu’il publie très régulièrement des détails sur le fonctionnement interne de SQL Server, dont certains ne sont pas documentés, à travers son site SQLSkills.com (son blog et ceux d’autres experts sont référencés en bas à droite de la page).
Paul Randal vient donc de mettre à la disposition de la communauté un poster qui détaille les étapes de la reprise d’activité par un diagramme décisionnel.
Ce diagramme contient certaines étapes qui référencent un numéro, et chaque numéro correspond à un article publié par Paul Randal. Ils sont tous référencés sur cette page.
Toujours bon à avoir sous la main !
Cadeau de Noël de Kalen Delaney : un livre sur le verrouillage, le blocage et le versionnement de ligne
Le site de RedGate, créé par Bard Mc Gehee, publie depuis quelques années déjà de nombreux ouvrages, dont certains abordent des thèmes que l’on voit rarement explorés en détails, parfois même par les mêmes auteurs chez d’autres éditeurs.
Vous n’avez plus grand chose à vous mettre sous la dent après avoir lu comment fonctionne l’optimiseur de requêtes ? On ne peut pas déclarer cela sérieusement avec la sortie récente de SQL Server 2012, et de ses nombreuses innovations ou améliorations
Malgré toutes ces nouveautés à décortiquer, les fondamentaux du fonctionnement de SQL Server demeurent. Donc pour Noël, Kalen Delaney nous fait le cadeau de son dernier ouvrage, SQL Server Concurrency: Locking, Blocking and Row Versioning, et que l’on peut déjà télécharger gratuitement en PDF (d’habitude RedGate ne le permet qu’après quelques mois).
Bonne lecture de Noël à tous !
Trouver et corriger les index uniques (non filtrés) sans contrainte
Exception faite des index unique filtrées, il n’y aucune différence en termes de performances entre l’ajout d’un index unique, et l’ajout d’une contrainte d’unicité (qui est elle-même supportée par un index unique).
La contrainte d’unicité permet de signifier que l’intégrité des données est le but, alors que le but premier d’un index est l’accélération des requêtes.
La majorité des index unique étant ajoutés pour renforcer l’intégrité des données, voici donc un script pour trouver tous les index unique non filtrés qui n’ont pas de contrainte d’unicité.
Connaître la longueur maximale des lignes des tables, et calculs relatifs
Il est intéressant de connaître la longueur maximale qui peut être stockée par une table pour tenter de prévoir la taille d’une base de données lorsque le système sera arrivé à maturité.
Voici une petite requête pour nous y aider sous SQL Server 2005 et 2008…
Lire la suite
Une procédure stockée pour trouver sous quel compte de service s’exécute une instance SQL Server
Voici une petite procédure stockée qui affiche dans la console de SSMS le nom du compte de service sous lequel s’exécute une instance SQL Server :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | CREATE PROCEDURE spGetSQLServerServiceAccountName AS BEGIN DECLARE @nomInstance SYSNAME, @nomMachine SYSNAME SELECT @nomInstance = CAST(SERVERPROPERTY('InstanceName') AS SYSNAME), @nomMachine = @@SERVERNAME DECLARE @cle NVARCHAR(256), @compteDeService NVARCHAR(128) SELECT @cle = CASE WHEN @nomInstance IS NULL THEN N'SYSTEM\CurrentControlSet\Services\MSSQLServer' ELSE N'SYSTEM\CurrentControlSet\Services\MSSQL$' + @nomInstance END EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', @cle, N'ObjectName', @compteDeService OUTPUT, N'no_output' PRINT 'Le compte de service est ' + @compteDeService END |
ElSuket
Connaître les caractéristiques d’une trace SQL Profiler
Sur un serveur à la configuration sous-dimensionnée que je surveille de temps en temps, j’ai trouvé une trace SQL Profiler en cours d’exécution.
J’ai voulu en savoir plus sur les caractéristiques de cette trace.
Voici la requête :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ------------------------------- -- Nicolas SOUQUET - 16/06/2010 ------------------------------- SELECT C.name , FI.value , T.start_time , T.last_event_time , T.event_count , S.login_name , S.session_id , 'EXEC sp_trace_setstatus ' + CAST(T.id AS varchar(10)) + ', 0' AS stop_trace_stmt FROM sys.traces AS T CROSS APPLY sys.fn_trace_getfilterinfo (T.id) AS FI INNER JOIN sys.trace_columns AS C ON FI.columnid = C.trace_column_id INNER JOIN sys.dm_exec_sessions AS S ON S.session_id = T.reader_spid |
On sait tout de suite quel filtres ont été utilisés à l’aide des deux premières colonnes.
La dernière colonne contient l’instruction qui permet d’arrêter la trace : le développeur a donc toujours ses résultats.
Pour savoir comment créer une trace côté serveur, c’est par ici
Bon traçage !
ElSüket