Dies sind genau die Beschwerden, die ich von unseren Entwicklern gehört habe. Das Interessanteste daran ist, dass sich dies als wahr herausstellte und eine lange Untersuchung auslöste. Wir werden ĂŒber SQL-Server sprechen, die wir auf VMware ausfĂŒhren.

TatsĂ€chlich kann es leicht passieren, dass der Produktionsserver hoffnungslos hinter dem Laptop zurĂŒckbleibt. FĂŒhren Sie den folgenden Code aus (nicht auf Tempdb und nicht auf einer Datenbank mit aktivierter verzögerter Haltbarkeit):
set nocount on
create table _t (v varchar(100))
declare @n int=300000
while @n>0 begin
insert into _t select 'What a slowpoke!'
delete from _t
set @n=@n-1
end
GO
drop table _t
Auf meinem Desktop dauert es 5 Sekunden und auf dem Produktionsserver 28 Sekunden. Weil SQL auf das physische Ende des Datensatzes im Transaktionsprotokoll warten muss und wir hier sehr kurze Transaktionen durchfĂŒhren. Grob gesagt fuhren wir mit einem groĂen, leistungsstarken LKW in den Stadtverkehr und beobachteten, wie Pizzalieferanten auf Motorrollern ihn schnell ĂŒberholten â der Durchsatz ist hier nicht wichtig, nur die Latenz ist wichtig. Und kein Netzwerkspeicher, egal wie viele Nullen sein Preis hat, kann eine lokale SSD in puncto Latenz schlagen.
(in den Kommentaren stellte sich heraus, dass ich gelogen hatte â ich hatte an beiden Stellen eine verzögerte Haltbarkeit. Ohne verzögerte Haltbarkeit stellt sich heraus:
Desktop â 39 Sekunden, 15 tr/s, 0.065 ms/io Roundtrip
PROD â 360 Sekunden, 1600 tr/s, 0.6 ms
Ich hĂ€tte merken mĂŒssen, dass es zu schnell war)
Allerdings handelt es sich in diesem Fall um triviale Nullstellen der Riemannschen Zetafunktion mit einem trivialen Beispiel. Das Beispiel, das mir die Entwickler gaben, war anders. Ich war davon ĂŒberzeugt, dass sie Recht hatten und begann, alle ihre Besonderheiten in Bezug auf die GeschĂ€ftslogik aus dem Beispiel zu entfernen. Irgendwann wurde mir klar, dass ich ihren Code komplett wegwerfen und meinen eigenen schreiben könnte â der das gleiche Problem aufweist â in der Produktion lĂ€uft er 3-4 Mal langsamer:
create function dbo.isPrime (@n bigint)
returns int
as
begin
if @n = 1 return 0
if @n = 2 return 1
if @n = 3 return 1
if @n % 2 = 0 return 0
declare @sq int
set @sq = sqrt(@n)+1 -- check odds up to sqrt
declare @dv int = 1
while @dv < @sq
begin
set @dv=@dv+2
if @n % @dv = 0 return 0
end
return 1
end
GO
declare @dt datetime set @dt=getdate()
select dbo.isPrime(1000000000000037)
select datediff(ms,@dt,getdate()) as ms
GOWenn alles in Ordnung ist, dauert die PrimzahlprĂŒfung 6-7-8 Sekunden. Genau das ist in der Serie passiert. ServerAuf einigen Servern dauerte die ĂberprĂŒfung jedoch 25 bis 40 Sekunden. Interessanterweise gab es keine Server, auf denen die AusfĂŒhrung beispielsweise 14 Sekunden dauerte â der Code lief entweder sehr schnell oder sehr langsam, was bedeutete, dass das Problem sozusagen eindeutig war.
Was habe ich getan? Ich habe mir die VMware-Metriken angesehen. Dort war alles in Ordnung â es gab genĂŒgend Ressourcen, Bereitschaftszeit = 0, alles war ausreichend, wĂ€hrend des Tests sowohl auf schnellen als auch auf langsamen Servern CPU = 100 auf einer vCPU. Ich habe einen Test zur Berechnung der Zahl Pi durchgefĂŒhrt â der Test zeigte auf jedem Server die gleichen Ergebnisse. Der Geruch schwarzer Magie wurde stĂ€rker.
Nachdem ich die DEV-Farm erreicht hatte, begann ich mit den Servern zu spielen. Es stellte sich heraus, dass vMotion von Host zu Host einen Server âheilenâ kann, aber auch einen âschnellenâ Server in einen âlangsamenâ verwandeln kann. Es scheint, als wĂ€re es das â einige Hosts haben ein Problem ⊠aber ⊠nein. Eine virtuelle Maschine war beispielsweise auf Host A langsam, lief aber auf Host B schnell. Und eine andere virtuelle Maschine hingegen lief auf A schnell und war auf B langsamer! Der Host hatte oft sowohl âschnelleâ als auch âlangsameâ Maschinen am Laufen!
Von diesem Moment an roch die Luft deutlich nach Schwefel. SchlieĂlich konnte das Problem nicht an der virtuellen Maschine liegen (z. B. an Windows-Patches), sondern war wĂ€hrend vMotion zu einer âschnellenâ Maschine geworden. Das Problem konnte aber auch nicht am Host liegen â schlieĂlich konnte dieser sowohl ĂŒber âschnelleâ als auch ĂŒber âlangsameâ Maschinen verfĂŒgen. Es lag auch nicht an der Auslastung â ich habe es geschafft, eine âlangsameâ Maschine auf einem Host zu installieren, auf dem es sonst ĂŒberhaupt nichts gab.
Aus Verzweiflung habe ich den Process Explorer von Sysinternals gestartet und mir den SQL-Stack angesehen. Auf langsamen Maschinen fiel mir sofort die folgende Zeile ins Auge:
ntoskrnl.exe!KeSynchronizeExecution+0x5bf6
ntoskrnl.exe!KeWaitForMultipleObjects+0x109d
ntoskrnl.exe!KeWaitForMultipleObjects+0xb3f
ntoskrnl.exe!KeWaitForSingleObject+0x377
ntoskrnl.exe!KeQuerySystemTimePrecise+0x881 < â !!!
ntoskrnl.exe!ObDereferenceObjectDeferDelete+0x28a
ntoskrnl.exe!KeSynchronizeExecution+0x2de2
sqllang.dll!CDiagThreadSafe::PxlvlReplace+0x1a20
âŠ ĂŒbersprungen
sqldk.dll!SystemThread::MakeMiniSOSThread+0xa54
KERNEL32.DLL!BaseThreadInitThunk+0x14
ntdll.dll!RtlUserThreadStart+0x21
Das war schon mal was. Das Programm wurde geschrieben:
class Program
{
[DllImport("kernel32.dll")]
static extern void GetSystemTimePreciseAsFileTime(out FILE_TIME lpSystemTimeAsFileTime);
[StructLayout(LayoutKind.Sequential)]
struct FILE_TIME
{
public int ftTimeLow;
public int ftTimeHigh;
}
static void Main(string[] args)
{
for (int i = 0; i < 16; i++)
{
int counter = 0;
var stopwatch = Stopwatch.StartNew();
while (stopwatch.ElapsedMilliseconds < 1000)
{
GetSystemTimePreciseAsFileTime(out var fileTime);
counter++;
}
if (i > 0)
{
Console.WriteLine("{0}", counter);
}
}
}
}Dieses Programm zeigte eine noch dramatischere Verlangsamung: Auf âschnellenâ Maschinen zeigte es 16â18 Millionen Zyklen pro Sekunde, wĂ€hrend es auf langsamen Maschinen eineinhalb Millionen oder sogar 700 anzeigte. Das heiĂt, der Unterschied betrĂ€gt das 10- bis 20-fache (!!!). Dies war bereits ein kleiner Sieg: Zumindest bestand nicht die Gefahr, dass es zwischen dem Microsoft- und dem VMware-Support zu einer Pattsituation kommt und man sich gegenseitig mit dem Finger zeigt.
Dann kam der Fortschritt zum Stillstand â Urlaub, wichtige Angelegenheiten, Virushysterie und ein starker Anstieg der Arbeitsbelastung. Ich habe Kollegen gegenĂŒber oft von diesem magischen Problem erzĂ€hlt, aber manchmal schien es, als ob sie mir nicht einmal immer glaubten â die Aussage, dass VMware den Code um das 10- bis 20-fache verlangsamt, war einfach zu ungeheuerlich.
Ich habe versucht, selbst herauszufinden, was mich bremste. Manchmal dachte ich, ich hĂ€tte eine Lösung gefunden â durch das Ein- und Ausschalten von Hot Plugs, das Ăndern der Speichermenge oder der Anzahl der Prozessoren wurde die Maschine oft âschnellâ. Aber nicht fĂŒr immer. Aber es stellte sich heraus, dass es genĂŒgt, hinauszugehen und an das Rad zu klopfen - das heiĂt, VerĂ€nderung keine Parameter der virtuellen Maschine
SchlieĂlich fanden meine amerikanischen Kollegen plötzlich die Grundursache.

Die HĂ€ufigkeit der Gastgeber war unterschiedlich!
- In der Regel ist es nicht beĂ€ngstigend. Aber: Beim Wechsel vom ânativenâ Host zu einem Host mit einer âanderenâ Frequenz muss VMware das GetTimePrecise-Ergebnis anpassen.
- Dies ist normalerweise keine groĂe Sache, es sei denn, Sie haben eine Anwendung, die die genaue Zeit Millionen Mal pro Sekunde anfordert, wie beispielsweise ein SQL-Server.
- Dies ist jedoch nicht beÀngstigend, da SQL Server dies nicht immer tut (siehe Schlussfolgerung).
Aber es gibt FĂ€lle, in denen dieser Rechen hart zuschlĂ€gt. Und ja, durch Tippen auf das Rad (durch Ăndern von etwas in den VM-Einstellungen) habe ich VMware gezwungen, die Konfiguration âneu zu berechnenâ, und die Frequenz des aktuellen Hosts wurde zur ânativenâ Frequenz der Maschine.
Lösung
Wenn Sie die Virtualisierung des TSC deaktivieren, gibt das Lesen des TSC innerhalb der virtuellen Maschine den TSC-Wert der physischen Maschine zurĂŒck und das Schreiben des TSC innerhalb der virtuellen Maschine hat keine Auswirkungen. Das Migrieren der virtuellen Maschine auf einen anderen Host, das Fortsetzen aus dem angehaltenen Zustand oder das ZurĂŒckkehren zu einem Snapshot fĂŒhrt dazu, dass der TSC unregelmĂ€Ăig springt. Einige Gastbetriebssysteme können nicht gestartet werden oder weisen andere Zeitmessungsprobleme auf, wenn die TSC-Virtualisierung deaktiviert ist. In der Vergangenheit wurde diese Funktion manchmal empfohlen, um die Leistung von Anwendungen zu verbessern, die den TSC hĂ€ufig lesen, aber die Leistung des virtuellen TSC wurde in aktuellen Produkten erheblich verbessert. Die Funktion wird auch fĂŒr die DurchfĂŒhrung von Messungen empfohlen, die eine prĂ€zise Echtzeitquelle in der virtuellen Maschine erfordern.
Kurz gesagt, Sie mĂŒssen einen Parameter hinzufĂŒgen
monitor_control.virtual_rdtsc = FALSE
Fazit
Sie haben wahrscheinlich eine Frage: Warum muss SQL GetTimePrecise so oft aufrufen?
Ich habe den SQL Server-Quellcode nicht, aber die Logik sagt Folgendes. SQL ist fast wie ein Betriebssystem mit kooperativer ParallelitĂ€t, bei dem jeder Thread von Zeit zu Zeit ânachgebenâ muss. Wo kann man das am besten machen? Wo es eine natĂŒrliche Wartezeit gibt â Sperre oder IO. Okay, aber was wĂ€re, wenn wir die Rechenzyklen drehen wĂŒrden? Der offensichtlichste und fast einzige Ort ist dann der Interpreter (es ist nicht genau ein Interpreter), nachdem der nĂ€chste Operator ausgefĂŒhrt wurde.
Normalerweise wird SQL Server nicht zum Einschlagen rein rechnerischer NĂ€gel verwendet und dies stellt kein Problem dar. Aber Zyklen mit der Arbeit mit allen möglichen temporĂ€ren Tabellen (die sofort zwischengespeichert werden) verwandeln den Code in eine Folge sehr schnell ausgefĂŒhrter Operatoren.
Ăbrigens: Wenn Sie eine Funktion in NATIVELY COMPILED einschlieĂen, benötigt sie keine Zeit mehr und ihre Geschwindigkeit erhöht sich um das Zehnfache. Aber was ist mit kooperativem Multitasking? Aber fĂŒr nativ kompilierten Code mussten wir in SQL PREEMPTIVE MULTITASKING durchfĂŒhren.
Source: habr.com
