jeudi 20 août 2009

Lire les SqlServer logs depuis TSQL

Il est des cas où l'accès à l'interface du Console Management n'est pas disponible sur un serveur SQL donné.
Ou bien encore, le Sql Server Log est trop long pour être accédé depuis cette même interface graphique.
Dans ce cas, l'utilisation de la stored procedure sp_readerrorlog s'avère bien pratique.

Syntaxe
EXEC sp_readerrorlog Param1,Param2, Param3, Param4

Param 1: identification de l'archive (0=Current Log, 1=Archive 1, 2=Archive 2, etc)
Param 2: Type de log à lire (1 ou NULL= SqlServer Log, 2=Sql Agent Log)
Param 3: texte à rechercher dans le log.
Param 4: raffiner la recherche en cherchant un autre texte dans les résultats de Param3

Exemple
La requête suivante va lire le log courant (Param1=0) du "SqlServer log" (Param2=1) pour y rechercher le texte 'been using' (Param3='been using').
EXEC sp_readerrorlog 0,1 

Cette recherche permet d'être informer des jours de fonctionnement de SQL server. En effet, tous les jours à heure de démarrage, Sql Server affiche la ligne suivante dans le log "This instance of SQL Server has been using a process ID of xxx since ..."

Référence
Pour plus d'informations, voir l'article "Reading the SQL Server log files using T-SQL" sur MSSqlTips.

Capture et explications des Wait Times

Notes complémentaires faisant suite aux articles "SQL Server Monitoring Performance - Wait Stats" et "Utiliser le SQL Profiler pour optimiser une base de donnée" précédemment publiés.

Cet article visera principalement la définition de certains Wait Times et la présentation d'une autre méthode de capture des Wait Stats.
Commencé mi-mai, il m'aura fallut presque 3 mois pour organiser une synthèse correcte de mes recherches.

Définition des Wait Times
Sql Serveur peut encourir différents délais d'attentes sur des processus internes.
Ces délais d'attentes sont généralement les indicateurs de problèmes de performances.
En analysant de plus près les statistiques de Wait Types, il est possible d'identifier la ou les sources causant les dégradations de performances sur SQL serveur.
Les Wait Times peuvent être consultés depuis la Dynamic Management View Sys.dm_os_wait_stats.
Ces statistiques étant maintenue au niveau du serveur SQL (cumulant l'information au fur et à mesure) il est nécessaire d'effectuer plusieurs requêtes (dans le temps) et quelques opérations de soustractions pour obtenir des informations exploitables.
L'article "SQL Server Monitoring Performance - Wait Stats" écrit précédemment met en place une méthode de capture systématique des wait times toutes les minutes. Quelques scripts sql permettent également d'exploiter l'information au mieux.

Cas Pratique
Dans un cas récent, des délais importants sur le Wait Time "WRITELOG" (plusieurs secondes par minutes) nous indiquait un problème de "libre" accès au fichier de transaction sql sur les disques SAN.
Ce problème d'accès causant des temps de réponses vraiment importants (jusqu'à 30 secondes) pour les applications clientes.

Capture des Wait Types
SELECT wait_type, waiting_tasks_count, wait_time_ms, 
  max_wait_time_ms, signal_wait_time_ms 
FROM master.Sys.dm_os_wait_stats 
where wait_type like 'LogBuffer' or 
  Wait_Type like 'LOGMGR%' or 
  wait_type like 'WRITE%' or 
  wait_type like 'latch_%' or 
  wait_Type like 'IO_COMPLETION' or 
  Wait_type like 'SOS_SCH%' or
  wait_type like 'PAGEIOLATCH%' 

Reset des statistiques
DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

Signification de "signal_wait_time_ms"
Le champs signal wait time de sys.dm_os_wait_stats nécessite quelques explications complémentaire.
Ce temps représente le délai d'attente entre le moment où la ressource est signalée (ressource disponible) et le moment ou le thread de traitement ait été reschédulé pour l'execution.
Autrement dit le délai nécessaire pour que la tâche soit reprogrammée par le scheduler (le scheduler organise au mieux la distribution des ressources CPU aux différents processus Sql).
Sur les systèmes/schedulers chargés, le "signal_wait_time_ms" est une indication de pression sur le CPU (ressource CPU disponible de façon trop limitée pour les tâches).
Ce temps est déjà inclus dans "wait_time_ms" (ref).

This time represents the time that the actual wait event finished, and the time the task is scheduled to run on the scheduler again. On busy schedulers this signal wait time can be an indication of CPU pressure.

Monitoring de WaitStats selon Microsoft

Le SQL suivant originaire des laboratoires de Microsoft est extrait du forum de Sql Serveur Magazine. Il permet de tracker les wait stats pour Sql Serveur 2000.



if exists (select 1 from sysobjects where name = 'track_waitstats') 
drop proc track_waitstats 
go 
CREATE proc track_waitstats (@num_samples int=10,@delaynum int=1,@delaytype nvarchar(10)='minutes') 
as 
-- 
-- This stored procedure is provided "AS IS" with no warranties, and confers no rights. 
-- Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm 
-- 
-- T. Davidson 
-- @num_samples is the number of times to capture waitstats, default is 10 times 
-- default delay interval is 1 minute 
-- delaynum is the delay interval - can be minutes or seconds 
-- delaytype specifies whether the delay interval is minutes or seconds 
-- create waitstats table if it doesn't exist, otherwise truncate 
-- 
set nocount on 
if not exists (select 1 from sysobjects where name = 'waitstats') 
create table waitstats ([wait type] varchar(80), 
requests numeric(20,1), 
[wait time] numeric (20,1), 
[signal wait time] numeric(20,1), 
now datetime default getdate()) 
else truncate table waitstats 
dbcc sqlperf (waitstats,clear) -- clear out waitstats 
declare @i int,@delay varchar(8),@dt varchar(3), @now datetime, @totalwait numeric(20,1) 
,@endtime datetime,@begintime datetime 
,@hr int, @min int, @sec int 
select @i = 1 
select @dt = case lower(@delaytype) 
when 'minutes' then 'm' 
when 'minute' then 'm' 
when 'min' then 'm' 
when 'mm' then 'm' 
when 'mi' then 'm' 
when 'm' then 'm' 
when 'seconds' then 's' 
when 'second' then 's' 
when 'sec' then 's' 
when 'ss' then 's' 
when 's' then 's' 
else @delaytype 
end 
if @dt not in ('s','m') 
begin 
print 'please supply delay type e.g. seconds or minutes' 
return 
end 
if @dt = 's' 
begin 
select @sec = @delaynum % 60 
select @min = cast((@delaynum / 60) as int) 
select @hr = cast((@min / 60) as int) 
select @min = @min % 60 
end 
if @dt = 'm' 
begin 
select @sec = 0 
select @min = @delaynum % 60 
select @hr = cast((@delaynum / 60) as int) 
end 
select @delay= right('0'+ convert(varchar(2),@hr),2) + ':' + 
+ right('0'+convert(varchar(2),@min),2) + ':' + 
+ right('0'+convert(varchar(2),@sec),2) 
if @hr > 23 or @min > 59 or @sec > 59 
begin 
select 'hh:mm:ss delay time cannot > 23:59:59' 
select 'delay interval and type: ' + convert (varchar(10),@delaynum) + ',' + @delaytype + ' converts to ' + @delay 
return 
end 
while (@i <= @num_samples) 
begin 
insert into waitstats ([wait type], requests, [wait time],[signal wait time]) 
exec ('dbcc sqlperf(waitstats)') 
select @i = @i + 1 
waitfor delay @delay 
end 
select @now=max(now),@begintime=min(now),@endtime=max(now) 
from waitstats where [wait type] = 'TOTAL' 
--- subtract waitfor, sleep, and resource_queue from Total 
select @totalwait = sum([wait time]) + 1 from waitstats 
where [wait type] not in ('WAITFOR','SLEEP','RESOURCE_QUEUE','TOTAL', '***total***') and now = @now 

-- insert adjusted totals, rank by percentage descending 
insert into waitstats select '***total***',0,@totalwait,@totalwait,@now 
select 'start, end, duration'='start: ' + convert(varchar(20),@begintime,20) + ' end: ' + convert(varchar(20),@endtime,20) + ' duration (minutes): ' + convert(varchar(10), datediff(mi,@begintime,@endtime)) 
select 'waitstats samples'=@num_samples, 'delay, type'= convert (varchar(10),@delaynum) + ',' + @delaytype,'waitfor delay interval'=@delay 
select [wait type],[wait time],percentage=cast (100*[wait time]/@totalwait as numeric(20,1)) 
from waitstats 
where [wait type] not in ('WAITFOR','SLEEP','RESOURCE_QUEUE','TOTAL') 
and now = @now 
order by percentage desc 
go 
exec track_waitstats 20,15,'seconds' 

 



Voici quelques exemples d'utilisation et informations diverses
Pour executer une journée entière...
exec track_waitstats 20,15,'seconds'

Une capture toutes les 15 secondes pendant deux heures.
@num_samples = 7200
@delaynum - 15
@delaytype = 'seconds'


Le query suivant est relatif à l'activation de traces spécifiques permet avant l'execution de trackwaits.
Permet certainement d'activer des traces bien précises (non testé) 
DBCC TRACEON(818,-1)
DBCC TRACEON(806,-1)
DBCC TRACEON(3605,-1)  


Revue de quelques Wait Types
Une liste des différents wait_type est disponible sur le site MSDN de Microsoft (sys.dm_os_wait_stats).
Bien qu'en français, il est néanmoins regrettable que les définition ne soient pas plus précises.

LATCH_EX, PAGEIOLATCH_SH (CXPacket)
Les entrées LATCH_EX, PAGEIOLATCH_SH (CXPacket) identifie de faibles performances IO.
Plus de reférence pour cet article :-( .

IO_COMPLETION 
Indique généralement un problème de performance disque.
Indique qu'un processus SPID (requête) attend que les opérations IO soient terminées.
Ces requêtes IO ne sont pas liées aux pages (Data Pages) de données des DB mais plutôt aux processus internes de SqlServer.

Information complémentaire:
Une valeur de "disk seconds/read" (perfmon) supérieure à 15 ms de façon prolongée indique un étranglement au niveau des disques (même si Microsoft applique déjà une valeur critique plus élevée de 20 ms).
Les écritures disques doivent être aussi rapide que possible pour le Transaction Log (1 ms ou moins est idéal pour un système OLTP).

Plus d'info ici sur SqlCat (cet article analysant/diagnostiquant les différents types de bottleneck).

PAGEIOLATCH_*
Indique un problème de performance IO pour accéder aux données de la DB.
Les IO relatif aux chargement des pages de données (Data Pages) des DB.
Les PAGEIOLATCH_SH surviennent quand une tâche attend un latch sur un buffer exécutant/attendant la fin d'exécution d'une requête I/O.
Les PAGEIOLACTH_SH sont des "latch requests" en mode partagé (Shared mode) et _EX en mode exclusif (exclusive). Les attentes sur PAGEIOLATCH_SH sont très bref puisqu'il sont uniquement maintenu sur les pages le temps nécessaire à l'achèvement de l'operation I/O.
Le manque de ressource I/O (I/O bottleneck) peuvent être identifiés en examinant les temps d'attente des LATCHs.

Une grande quantité de PAGEIOLATCH_* et/ou CXPACKET peut également indiquer une forte fragmentation des indexes. Vérifiez donc cette fragmentation ainsi que l'update des statistiques.

Pour apporter une solution à ce type de problème, lire le document "Troubleshooting Performance Problems in SQL Server 2005" de Microsoft en recherchant les sections relatives à "I/O Bottleneck".
Considérer les options suivantes:
  • Réviser/modifier la configuration hardware pour améliorer les IO (part importante de l'article).
  • Considérer l'utilisation d' execution plan pour les requête SQL en vue de minimiser le nombre d'opérations IO nécessaires.
  • Vérifier si les indexes sont correctements définis (Utilisation de "Sql Tuning Advisor")

Plus d'info ici sur MSDN.

PageIOLatch_* occurs when a task is waiting on a latch for a buffer that is in an I/O request. PAGEIOLATCH_SH waits is very brief as they are only held on a page during IO operations.
These latch waits account for the physical I/O waits when a page is accessed for reading or writing and the page is not available in the buffer pool. When the page is not found in the buffer pool, an asynchronous I/O is posted and then the status of the I/O is checked. If I/O has already completed, the worker proceeds normally. Otherwise, it waits on PAGEIOLATCH_EX or PAGEIOLATCH_SH, depending upon the type of request.
More information here on the Rickie Lee's blog.


CXPACKET

CXPACKET démontre que plusieurs querys sont exécutés en parallele et que un ou plusieurs d'entre deux attendent la fin de l'exécution d'autres threads (sans doute des autres querys) 1*.
C'est une indication de mauvaise indexation, de possible table scan et/ou mauvaise performances disques. En général, une re-indexation ou forcer l'utilisation de plan d'exécution non-parallele pour ces querys (à faible performance) corrige ce problème.
Plus d'info ici sur le forum de Sql Serveur Magazine.

1*: one or more of the ecids is waiting on the other threads to complete.

Losrque CXPACKET and PAGEIOLATCH sont les deux plus important Wait Times, cela indique que les threads s'attendent l'un l'autre dans le UMS. Cela est causé par de mauvaises statistiques (causant l'utilisation de mauvais plan d'exécution).
Solution: Reindexer les tables (défragmenter les indexes, mettre les statistiques à jour).
Plus d'info ici sur le forum de Sql Serveur Magazine. Contient une excellente requête pour faire du monitoring de wait stats.

LATCH_EX
Si l'analyse des waits indique un grand pourentage de LATCH_EX, cela pourrait indiquer une contention (étranglement) des objets résidents en mémoire (rememory-resident objects)
Par exemple, la surcharge peut être causée les utilisateurs essayent tous de mettre à jour les (mêmes pages) de données critiques fréquemments utilisées (hot pages). Dans ce cas, ils essayent tous de prendre un latch exclusif sur le même ensemble de pages... et par conséquent... ils s'attendent tous les uns les autres.
Dans ce cas de figure la quantité de mémoire disponible (ou mis à disposition) n'est pas un facteur permettant de régler la situation.
Plus d'info ici sur le site My Database Support

SOS_SCHEDULER_YIELD
Une grande quantité de SOS_SCHEDULER_YIELD dans les Wait stats est un excellent indicateur de manque de ressource CPU (CPU pressure).
Ils surviennent lorsque la ressource CPU d'une tâche est volontairement cédée au scheduler pour d'autres tâches à executer.
Durant ce laps d'attente, la tâche est mise en suspend en attendant le renouvellement de son unité d'exécution.

SQLOS gère le threading avec une approche coopérative, les threads ne sont pas gérés pas le système d'exploitation.
Quand un processus Sql require trop de ressources pour être achevé, il va céder son temps de processing (au lieu de consommer toutes les ressources jusqu'a l'accomplissement de sa tâche).
Cette approche coopérative évite aux autres processus Sql d'être ralentis ou bloqués. Une fois qu'il a abandonné son unité de temps processing, le processus Sql va sagement attendre une nouvelle occasion de se voir octroyer des unités de processing complémentaires par scheduler de SQLOS.

Si vous constatez une valeur élevée de SOS_SCHEDULER_YIELD, utilisez l'outil PerfMon et vérifiez si les compteurs "% Processor Time" et "Processor Queue Length" sont maintenu à des valeurs élevées.
Il est également possible de valider le fait que Sql Serveur puisse être le responsable de la pression CPU; Pour ce faire, utilisez le compteur "% Processor Time" sur l'instance de SQL Server (ou tout autre processus/programme suspect).
Vous aurez aussi besoin de vérifier le nombre de "workers" en cours d'exécution en verifiant les vues sys.dm_os_workers et sys.dm_os_schedulers. Cela indiquera si un nombre significatif de tâches/processus Sql sont dans la queue d'attente pour obtenir une unité d'exécution de la part du scheduler. 

Plus d'info sur les liens suivants

Vérifier la pression CPU via une DMV


Il est possible de lancer une requête sur les "Dynamic Management View" pour vérifier l'état des Schedulers et s'assurer qu'ils n'attendent pas la libération de ressources CPU.
    SELECT scheduler_id, current_tasks_count, runnable_tasks_count
    FROM sys.dm_os_schedulers
    WHERE scheduler_id < 255
Inquiétez-vous si vous constatez des runnable_tasks_count au dessus de zéro. Et si durant une période quelconque vous constatez que runnable_tasks_count contient deux digits, alors dans ce cas, vous devriez être extrêmement inquiet!

Vérifier la pression CPU via le Signal_Wait_Time
Il est également possible de constater un manque de ressource CPU (CPU pressure) en analysant les Signal_Wait_Time.
Les "signal waits" mesure le temps perdu par un process en cours d'exécution (mais suspendu et entassé dans une queue) à attendre des ressources CPU pour continuer son exécution.

Consultez sys.dm_os_wait_stats pour obtenir le Signal waits et le total des waits.
Une grande valeur de "signal waits" indique un goulot d'étranglement au niveau CPU.
Info: Signal waits > 25% of total waits.


WRITELOG
WRITELOG indique que SQL serveur attend que les operations IO relatives au transaction log soient terminées. Cela indique généralement un problème de performance disque (soit les disques eux mêmes, soit le controleur ou tout système apparenté).

Tiré d'un cas réel, 400 ms pour un WriteLog est bien trop long et un symptôme clair d'engorgement des performances disques. Le temps d'accès au log file ne devrait pas excéder quelques milli-secondes, l'idéal est un temps inférieur ou égal à la milli-seconde (ce qui est possible avec certain SAN, ex: voir par exemple les produits NET App).
Pour l'accès aux fichiers de données, un temps d'accès de 15 ms (de façon soutenu) est déjà considéré comme un problème de performances disques.
Selon la documentation de Microsoft (ici), voici un tableau récapitulatif d'évaluation des performances des temps d'accès en lecture (via Performance counter: Avg.Disk Sec/Read).

Less than 10 ms - very good
Between 10 - 20 ms - okay
Between 20 - 50 ms - slow, needs attention
Greater than 50 ms – Serious I/O bottleneck


D'un autre côté, se concentrer sur les FileStats sys.dm_io_virtual_file_stats() en même temps que les Wait Stats démontrera (1) combien de tentative d'écriture seront faite sur le Log file, (2) le nombre de bytes écrits, (3) combien de temps a été nécessaire.
Il est ainsi possible de mettre en relation une augmentation des waits avec une chute des opérations disques  (bytes écrits) ou encore une brusque augmentation des IO_Stall (opérations IO retardée)

N'importe laquelle des actions suivantes réduira le temps d'attente du système I/O:
  • Augmentation de la bande passante I/O.
  • Load Balancing des I/O sur plusieurs disques.
  • Placer le transaction Log sur son propre disque.
Plus d'informations ici:
 Pour rappel, voici l'extraction des files stats:
select DB_NAME(database_id) DB_NAME, file_id,io_stall_read_ms ,num_of_reads
 ,cast(io_stall_read_ms/(1.0+num_of_reads) as numeric(10,1)) as 'avg_read_stall_ms'
 ,io_stall_write_ms,num_of_writes
 ,cast(io_stall_write_ms/(1.0+num_of_writes) as numeric(10,1)) as 'avg_write_stall_ms'
 ,io_stall_read_ms + io_stall_write_ms as io_stalls
 ,num_of_reads + num_of_writes as total_io
 ,cast((io_stall_read_ms+io_stall_write_ms)/(1.0+num_of_reads + num_of_writes) as numeric(10,1)) as 'avg_io_stall_ms'
 from sys.dm_io_virtual_file_stats(null,null)
 order by avg_io_stall_ms desc

mercredi 5 août 2009

Utiliser Varchar(Max) au lieu du type de donnée Text

Le type de donnée texte permet de stocker des données excédant 8Kb de stockage d'une data page Sql Serveur (8Kb étant la taille maximale d'une row).

Le principal désavantage des types de données Text, NText et Image c'est qu'ils:
  1. Ne sont pas utilisables comme paramètre de stored procedure
  2. Qu'ils ne peuvent pas être manipulés facilement dans des triggers (à moins d'utiliser des fonctions spécialisées comme UpdateText et consort).
  3. Qu'ils ne peuvent pas être utilisés avec des fonctions standard de manipulation de text.
Bref, un cauchemar pour la maintenance!

Depuis Sql2005, il existe le type de données Varchar(Max) qui permet d'outre passer toutes ces limitations techniques.

Pour plus d'information sur le l'article Understanding "VARCHAR(MAX) in SQL Server 2005" de TeraTrax.

Merci à Gerrit pour cette précieuse information.

Le guide de l'utilisateur UML

Guide pratique (en français) par les concepteurs d'UML (Grady Booch, James Rumbaugh, Ivar Jacobson).
Bien que datant un peu, puisqu'il s'adresse à une plus ancienne version d'UML, le contenu du livre se veut résolument pratique et exploitable rapidement.

Sommaire:
  • Premier pas
  • Modélisation de structures élémentaires
  • Modélisation de structures avancées
  • Modélisation de comportements élémentaires
  • Modélisation de comportements avancés
  • Modélisation d'architectures
  • Annexes (notations, éléments standards, stéréotypes, etc).

Nom: Le guide de l'utilisateur UML (plus d'info)
Editeur: Eyrolles
Code éditeur: G09103
ISBN: 2-212-09103-6

mardi 4 août 2009

Transformer des records en liste (comma separated)

Le script suivant montre comment utiliser l'instruction FOR XML PATH de Sql serveur 2005 pour produire une liste (comma separated) à partir d'un ensemble d'enregistrement.
La puissance de la syntaxe permettant même d'effectuer cette opération pour chacun des enregistrements d'une autre table/sélection.

Un concept vaguement comparable aux "nested tables" d'ADO et bigrement utile pour du reporting :-)

select top 5 usr.ID_User, 
  Left( usr.LoginName, 5 )+'****' as [Scrambled Login],
  ( 
    SELECT gList.GroupName +', ' as [text()]
    from tblSSlnkGtoU gtouList
    left join tblSSGroups gList 
        on gList.ID_Group = gtouList.ID_Group
    where gtouList.ID_User = usr.ID_User
    Order by glist.GroupName
    FOR XML PATH( '' )
   ) as [Scrambled GroupList]
  
FROM tblSSUsers usr 
where usr.ID_User &gt 100

Voici l'exemple issus de l'article d'Anatoly Lubarsky (voir référence ci-dessous).
SELECT CustomerID
      ,(SELECT CAST(OrderID AS VARCHAR(MAX)) + ',' AS [text()]
          FROM dbo.Orders AS O
         WHERE O.CustomerID = C.CustomerID
         ORDER BY OrderID
           FOR XML PATH('')) AS Orders
  FROM dbo.Customers AS C;

sources: 

lundi 3 août 2009

MonoDevelop sous Windows

MonoDevelop devient Cross-Platform. Il est maintenant possible de le compiler sur une plateforme Windows (et Mac).
Voir cette annonce sur le blog de Miguel de Icaza ou encore la marche a suivre sur le blog de Lluis pour compiler monoDevelop sur Visual Studio.

MonoDevelop est un environnement de developpement C# à la base destiné au projet Mono.
Il est a noter que pour la plateforme Microsoft, il existe déjà csharpDevelop.

Source: Blog de Miguel Icaza

dimanche 2 août 2009

Très bon achat pêle-mêle

Voila quelques petites surprises raménées du pêle-mêle de Waterloo.
Rien que des beaux livres dans un état impeccables et a un prix défiant toute concurrence (33% de la valeur d'origine).

 
Encore quelques belles heures en perspectives :-)