Category: "Performance"
Performance overhead of tracing with Extended Event targets vs SQL Trace under CPU Load
Aug 14th
Leistungseinbußen beim Tracing, Extended Event Ziele gegen SQL Trace unter CPU Last
(DE) Inspiriert durch den Blog Post Post “Measuring Observer Overhead of SQL Trace vs. Extended Events” von Jonathan Kehayias, war ich neugierig, ein paar weitere Benchmarks durchzuführen. Die Resultate habe ich bereits in meinen SQL Server Master-Class Workshops zu XEvents und auf der PreCon des SQLSaturday #230 präsentiert. Nun teile ich sie endlich hier. Vor allem folgende Aspekte interessierten mich:
Da ich einmal das Setup bereit hatte, habe ich mich entschieden, alle anderen Ziele ebenfalls aufzunehmen, sowie einige grundlegende Options-Variationen, um einen kompletten Überblick zu erhalten, der in sich vergleichbar ist. |
(EN) Inspired by the Blog Post “Measuring Observer Overhead of SQL Trace vs. Extended Events” by Jonathan Kehayias, I was curious to do some more benchmarking. I was interested in the following aspects, in particular:
Besides that, once I had the setup ready, I decided to include all the other targets and some basic option-variations, to have a complete overview that is comparable in itself. |
Ich entschied mich für ein System, das bereits unter CPU Druck steht – die CPU Verwendung liegt fast vollständig bei 100%. |
I also decided for a test of a system that’s already under CPU pressure - so the Processor Usage is almost constantly hitting 100%. |
Test Umgebung “Einfach aber physikalisch” 1 Windows 2008 R2 Server mit SQL Server 2012 SP1. Keine laufenden Dienste abseits des minimal Notwendigem, um Seiten-Effekte zu verhindern. Die Arbeitslast war ein Mix von meist kurzen (Sub-Sekunden) aber auch einigen Abfragen mit mehreren Sekunden Laufzeit, (Ad Hoc und gespeicherte Prozeduren), um eine einigermaßen realistische Decision-Support-System Last zu simulieren – jedoch ohne Datenänderungen. |
Test Environment “Simple but physical” 1 Windows 2008 R2 Server with SQL Server 2012 SP1. No running services above the minimum required, to avoid side-effects. The workload was a mixture of mostly short (sub-second) but also several queries of several second runtime, (Ad Hoc and stored procedures) to simulate a somehow realistic decision-support-systems’ load – no data changes though. |
Trace-Konfigurationen Ich habe mich für eine einfache, aber nicht unübliche Trace entschieden, die typischerweise viele Daten sammelt. |
Trace-Configurations I decided for a simple but not uncommon trace that typically collects a lot of data. The trace collects only 2 events: |
- Lock acquired
- Lock released
Ausgefiltert: System-Sitzungen und andere Datenbanken als die, die hier unter Last steht. Gesammelte Daten: |
Filtered out: System-sessions and other databases than the one under load here. CollectedData: |
- Lock:Released BinaryData
- Lock:Released DatabaseID
- Lock:Released TransactionID
- Lock:Released SPID
- Lock:Released ObjectID
- Lock:Released Mode
- Lock:Released BigintData1
- Lock:Released IntegerData2
- Lock:Released ObjectID2
- Lock:Released Type
- Lock:Released OwnerID
- Lock:Released IsSystem
- Lock:Acquired BinaryData
- Lock:Acquired DatabaseID
- Lock:Acquired TransactionID
- Lock:Acquired SPID
- Lock:Acquired Duration
- Lock:Acquired ObjectID
- Lock:Acquired IsSystem
- Lock:Acquired Mode
- Lock:Acquired BigintData1
- Lock:Acquired IntegerData2
- Lock:Acquired ObjectID2
- Lock:Acquired Type
- Lock:Acquired OwnerID
Das gab mir den Vorteil, einen validen Test für alle Extended Event Targets, die bereitgestellt werden, zu haben (lediglich ETW-Tracing wurde ausgelassen) - speziell das Histogramm und die „Ereignispaarbildung“ (Pair-Matching)(Die wenigen Ereignisse von Lock-Escalation störten mich bewusst nicht). Die folgenden Trace Technologien und -Ziele wurden verwendet: |
This gave me the advantage to have a valid test for all Extended Event Targets that are provided (only ETW-Tracing was left out) - specifically histogram and Pair Matching (I did not care about the few occasions of lock escalation on purpose). The following Trace technologies and -Targets were used: |
- XEvent Trace, Target: None
- XEvent Trace, Target: Ring Buffer
- XEvent Trace, Target: File
- XEvent Trace, Target: Counter
- XEvent Trace, Target: Histogram
- Here I chose to filter on lock acquired and to bucketize on the lock mode
- XEvent Trace, Target: Pair Matching
- Guess, what I matched here ;-)
- SQL Trace, Target: None (I had to trick a little bit to get this to work, but I made sure, the behavior of this unsupported configuration did not distort the results: It’s just a SQL Trace with no Provider processing, so all events are lost by nature.)
- SQL Trace, Target: File
- SQL Profiler, remotely
Für jedes Ziel der Extended Events habe ich 4 Varianten getestet, basierend auf den Sitzungsoptionen: |
For each Extended Event Target I tested 4 variations, based on the session options: |
- Single event loss
- No event loss
- Multiple event loss
- Single event loss with causality tracking on
Alle anderen Optionen verwendeten die Standardwerte für diese Tests. |
All other options were using the defaults for these tests. |
Picture: the 24 prepared traces
Die Ergebnisse Ich habe die Gesamtlaufzeit für die Workload sowie Batches pro Sekunde und CPU Zeit % gemessen. Und hier ist die vollständige Tabelle mit den Ergebnissen: |
The Results I measured the total time for the workload to take as well as batches per second and CPU time %. And here is the complete Table of Results: |
EL = Ereignisverlust. S = Verlust einzelner Ereignisse, N = Kein Ereignisverlust, M = Verlust mehrerer Ereignisse CT = Kausalitätsverfolgung (Causality Tracking) Ein |
EL = Event Loss. S = Single event loss, N = No event loss, M = Multiple event loss CT = Causality Tracking On |
Um Zeit und Platz zu sparen, konzentriere ich mich auf die Benchmarks mit den Optionen single event loss und no event loss ohne Causality Tracking. Tatsächlich waren die Kosten von Causality Tracking weniger als 1% für alle Ziele. Hier ist daher das komprimierte Ergebnis: |
For the sake of saving space and time, I will focus on the benchmarks with the options single event loss and no event loss without causality tracking. In fact, the cost of causality tracking was less than 1% for all targets. So this is the more condensed result: |
Was wir demnach sagen können, ist: |
What we can tell from that, is: |
|
|
Ein schönes Bild der SQL Trace Architektur findet sich in den BOL: msdn.microsoft.com/en-us/library/hh245121.aspx |
You have a nice picture of the SQL Trace Architecture in BOL: msdn.microsoft.com/en-us/library/hh245121.aspx |
Hinzufügen von Filtern Wie ändert das Hinzufügen von Filtern den Beobachter-Overhead? Es ist wichtig, zu wissen, was ich als Filter verwendet habe: Die database_id / source_database_id. |
Adding Filters How does adding filters change the observer overhead? Now it is important to note what I actually used as a filter: The database_id / source_database_id. |
Dieses Mal konzentriere ich mich nur auf „single“ und „no event loss“. Die Resultate des gefilterten Tracing, ohne tatsächlich irgendwelche Events zu speichern/protokollieren, ist wie folgt: |
This time I only focused on “single” and “no event loss”. The results of Filtered Tracing, without actually storing/logging any events is as follows: |
Für diesen Test habe ich SQL Profiler nicht ausgeführt, Pardon. Sie wissen bis hierhin wahrscheinlich schon, warum. ;-) |
I did not run SQL Profiler for this Test, forgive me. You probably know why by now. ;-) |
Wait-Types für Extended Events Ein weiterer Aspekt, der mich interessierte, waren die XEvent Wait-Typen, die auftreten würden, wenn man Extended Event Sessions ausführt. (Die Warte-Statistiken sind oft die Basis für Performance-Analysen) Allgemein sieht man folgende: |
Wait-Types for Extended Events Another aspect I was interested in were the specific XEvent wait-types which would occur when running Extended Event sessions. In general, you will see the following: |
Beim Starten einer Sitzung: |
When starting a session: |
PREEMPTIVE_XE_TARGETINIT
PREEMPTIVE_XE_CALLBACKEXECUTE
PREEMPTIVE_XE_SESSIONCOMMIT
Beim Beenden einer Sitzung: |
When stopping a session: |
PREEMPTIVE_XE_SESSIONCOMMIT
XE_BUFFERMGR_ALLPROCESSED_EVENT
PREEMPTIVE_XE_CALLBACKEXECUTE
Während Sessions aktiv sind, sieht man: |
While running sessions you will see: |
XE_DISPATCHER_WAIT - From BOL: Occurs when a background thread that is used for Extended Events sessions is waiting for event buffers to process. - You should be able to safely ignore this unless you believe a problem is occurring with processing of events for async targets. Since this works on a queue you can have bursts of high wait times especially when no XEvent sessions are active.
XE_TIMER_EVENT – From BOL: Used to indicate a background task is waiting for "expired" timers for internal Xevent engine work. - You should be able to safely ignore this one. Just used by the Xevent engine for internal processing of its work. If something was possibly wrong with Xevent processing you might see if this thread ever "wakes up"
Beim Starten eines File-Targets sieht man außerdem: |
When starting the File target you will also see: |
PREEMPTIVE_XE_TARGETINIT
Wenn man eine Sitzung mit der No Event Loss Option ausführt, sieht man gegebenenfalls: |
If you run a session with the no event loss option you might see |
XE_BUFFERMGR_FREEBUF_EVENT - which by BOL means: An Extended Events session is configured for no event loss, and all buffers in the session are currently full. This can indicate that the buffers for an Extended Events session are too small, or should be partitioned.
So. Ich hoffe das war interessant. Man kann noch weitere und andere Schlüsse aus den Ergebnissen ziehen. Immer im Hinterkopf zu behalten, ist, dass das ein sehr spezielles Szenario ist, wo keine CPU Reserven zur Verfügung stehen, so das der Trace/Beobachter-Overhead sich manifestieren muss – keine Chance, als eben (CPU) Ressourcen wegzunehmen. |
So, I hope this was interesting for you. You may draw more and other conclusions out of these results. Remember though: this is a very specific scenario with no CPU reserves, so the Tracing/observer overhead has to show up – no choice but to take away (CPU) resources. |
Happy Tracing
Andreas
PS: I just discovered that my MCM and SQLSkills-class buddy Steven Ormrod also has recently blogged about the performance overhead from a SQL Trace to remote file share on production here: stevenormrod.com/2013/06/remote-trace-slowing-down-server/
Locking & READONLY Filegroups vs READONLY Databases // Sperren & READONLY Dateigruppen vs READONLY Datenbanken
Feb 22nd
(en) The Topic Locking and Read-Only for filegroups and databases is one of the ongoing myths around SQL Server in forums – and at least half of the information unfortunately wrong. Since I recently fell into the trap myself, I want to write down, how it really is. To have a definite picture, I made 3 test series under 3 different isolation levels:
|
(de) Das Thema Sperren im Zusammenhang mit Readonly Filegroups und Datenbanken geistert immer wieder durch die Foren - und mindestens zur Hälfte leider mit Falschinformationen angereichert. Da ich kürzlich selber in die Falle tappte, möchte ich hiermit schwarz auf weiß festhalten, wie es sich wirklich verhält. Um ein eindeutiges Bild zu erhalten, habe ich Testreihen unter 3 verschiedenen Isolation Levels durchgeführt:
|
USELockingDemo_RW
go
exec sp_help'BigTable'
Data_located_on_filegroup
PRIMARY
index_name index_description
PK__BigTable__3213E83FFF01B718 clustered, unique, primary key located on PRIMARY
USELockingDemo_RO
…
Data_located_on_filegroup
PRIMARY
index_name index_description
PK__BigTable__3213E83FFF01B718 clustered, unique, primary key located on PRIMARY
Identical structure so far except the database LockingDemo_RW_FG_RO – here the Table resides on filegroup FG_RO | Also ein identischer Aufbau, bis auf die Datenbank LockingDemo_RW_FG_RO – hier ist die Tabelle auf der Filegroup FG_RO |
USELockingDemo_RW_FG_RO
…
Data_located_on_filegroup
FG_RO
index_name index_description
PK__BigTable__3213E83FC5587D01 clustered, unique, primary key located on FG_RO
The first/upper query shows the total amount of data, the lower is used as the test query: | Die die erste/obere Abfrage zeigt die Gesamtdatenmenge, die untere wird als Testabfrage verwendet: |
1)
SELECT * FROM BigTable
(1000 row(s) affected)
Table 'BigTable'. Scan count 1, logical reads 36, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
2) – The Testquery / die Testabfrage
SELECT * FROM BigTable
WHERE id BETWEEN 100 AND 200
(101 row(s) affected)
Table 'BigTable'. Scan count 1, logical reads 6, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
This is what the test looked like (abbreviated): | So sah der Testlauf aus (abgekürzt): |
--====================
-- 1
--====================
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
USELockingDemo_RW
go
BEGINTRAN
SELECT * FROM BigTable
WHERE id BETWEEN 100 AND 200
COMMITTRAN
USELockingDemo_RO
go
BEGINTRAN
SELECT * FROM BigTable
WHERE id BETWEEN 100 AND 200
COMMITTRAN
USELockingDemo_RW_FG_RO
go
BEGINTRAN
SELECT * FROM BigTable
WHERE id BETWEEN 100 AND 200
COMMITTRAN
USEmaster
--====================
-- 2
--====================
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
...
--====================
-- 3
--====================
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
...
Parallel to that, an extended events session was running (Lock-escalation isn’t happening in this scenario) | Parallel dazu lief eine Extended Events session (Lock-Escalation tritt in diesem Szenario nicht auf) |
CREATE EVENT SESSION [Locking] ON SERVER
ADD EVENT sqlserver.lock_acquired(
ACTION(package0.event_sequence,sqlserver.database_id,sqlserver.is_system,sqlserver.session_id)
WHERE ([sqlserver].[database_id]>=(23) AND [sqlserver].[database_id]<=(25)))
ADD TARGET package0.event_file(SET filename=N'D:\SQLData\SQLData1\Locking.xel')
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=1 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=OFF)
GO
Results(Locks with granularity / resource_type = DATABASE left out) 1. Read Committed |
Resultate(Sperren mit Granularität / resource_type = DATABASE ausgelassen) 1. Read Committed |
2. Read Uncommitted(for obvious reason I left out the sub-totals :-) |
2. Read Uncommitted(aus offensichtlichem Grund habe ich die Zwischensummen weggelassen :-) |
3. Repeatable Read |
3. Repeatable Read |
A note about Statistics und eXclusive Locks on ReadOnly-Databases:Yes, one can indeed watch X-Locks on Read-Only databases. And this happens when auto-created stats jump in. |
Eine Bemerkung zu Statistiken und eXklusive-Sperren auf schreibgeschützten Datenbanken:Ja, tatsächlich kann man auch auf Read-Only Datenbanken hin und wieder X-Locks beobachten. Und zwar wenn auto-created Statistics einspringen. |
This is of course not the most common scenario, but it does happen (especially in AlwaysOn scenarios with read-only secondaries involved) and belongs to a complete picture. - Besides that one can see on first sight, that there is no diffference in the Locking behaviour beetween the table on a ReadWrite Filegroup (here Primary) and the table on the ReadOnly filegroup. Only if the whole database is ReadOnly, SQL Server saves himself the Page- and Key- locks. Conclusion:Putting Tables onto a ReadOnly-Filegroup does not save Locks.But it often does make a lot of sense, to break up databases in this manner. Just thinking of: less backup, faster restore, NTFS-compression etc. |
Das ist sicherlich nicht das am meisten übliche Szenario, aber es tritt auf (insbesondere in AlwaysOn Szenarien mit read-only Secondaries) und gehört zu einem vollständigen Bild. - Abgesehen davon erkennt man auf den ersten Blick, das kein Unterschied im Sperrverhalten zwischen der Tabelle auf einer ReadWrite Filegroup (hier Primary) und der Tabelle auf der ReadOnly Filegroup besteht. Nur wenn die gesamte Datenbank ReadOnly ist, spart sich SQL Server die Page- und Key- Locks. Selbst dort jedoch wird ein Intent-Share-Lock auf die Tabelle gesetzt. Fazit:Tabellen auf eine ReadOnly-Dateigruppe zu verlegen spart keine Sperren. |
Andreas