Genau solche Beschwerden habe ich von unseren Entwicklern gehört. Am interessantesten ist, dass sich das als wahr herausstellte und ein langwieriges Ermittlungsverfahren einleitete. Es geht um SQL-Server, die bei uns auf VMware laufen.

Es ist tatsĂ€chlich einfach, dafĂŒr zu sorgen, dass der Production-Server hoffnungslos hinter dem Laptop zurĂŒckbleibt. FĂŒhren Sie (nicht auf tempdb und nicht auf einer Datenbank mit aktivierter verzögerter Dauerhaftigkeit) den Code aus:
set nocount on
create table _t (v varchar(100))
declare @n int=300000
while @n>0 begin
insert into _t select 'Was fĂŒr ein Langsamster!'
delete from _t
set @n=@n-1
end
GO
drop table _t
Auf meinem Desktop dauert es 5 Sekunden, auf dem Production-Server jedoch 28 Sekunden. Weil SQL auf das physische Ende der Aufzeichnung im Transaktionsprotokoll warten muss, wĂ€hrend wir hier sehr kurze Transaktionen durchfĂŒhren. Grob gesagt haben wir einen groĂen, leistungsstarken Lkw in den Stadtverkehr gelenkt und beobachten, wie ihn Pizzalieferanten auf Scootern geschickt ĂŒberholen â hier spielt der Durchsatz keine Rolle, nur die Latenz. Und kein Netzwerk-Speicher, egal wie viele Nullen er in seinem Preis hat, kann in Bezug auf die Latenz mit einem lokalen SSD mithalten.
(In den Kommentaren stellte sich heraus, dass ich gelogen habe â sowohl in meinem Desktop als auch im Production-Umfeld war verzögerte Dauerhaftigkeit aktiv. Ohne verzögerte Dauerhaftigkeit ergibt sich folgendes:
Desktop â 39 Sekunden, 15K tr/sec, 0.065ms /io roundtrip
PROD â 360 Sekunden, 1600 tr/sec, 0.6ms
Ich hÀtte darauf achten sollen, dass es viel zu schnell war)
Wir haben es jedoch in diesem Fall mit trivialen Nullen dieser Riemannschen Funktion mit einem trivialen Beispiel zu tun. In dem Beispiel, das mir die Entwickler gebracht haben, war es anders. Ich stellte fest, dass sie Recht hatten, und begann, die ganze spezifische Logik, die mit der GeschĂ€ftslogik verbunden war, aus dem Beispiel zu entfernen. Irgendwann bemerkte ich, dass ich ihren Code vollstĂ€ndig wegwerfen und meinen eigenen schreiben konnte â der das gleiche Problem demonstriert â im Production-Umfeld 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 -- gerade Zahlen bis zur Wurzel ĂŒberprĂŒfen
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 gut lĂ€uft, wird die PrimzahlprĂŒfung 6-7-8 Sekunden dauern. So war es an mehreren Server. Aber an einigen Orten dauerte die PrĂŒfung 25-40 Sekunden. Interessanterweise gab es keine Server, bei denen die AusfĂŒhrung, sagen wir, 14 Sekunden dauerte â der Code arbeitete entweder sehr schnell oder sehr langsam, das heiĂt, das Problem war, um es so zu sagen, schwarz-weiĂ.
Was habe ich gemacht? Ich habe in die VMware-Metriken geschaut. Dort war alles in Ordnung â es gab reichlich Ressourcen, Ready-Zeit = 0, alles war ausreichend, wĂ€hrend des Tests waren die CPU-Lasten auf einem vCPU bei 100 auf sowohl schnellen als auch langsamen Servern. Ich nahm einen Test zur Berechnung der Zahl Pi â der Test zeigte auf allen Servern die gleichen Ergebnisse. Es roch immer stĂ€rker nach schwarzer Magie.
Als ich auf die DEV-Farm kam, begann ich mit den Servern zu experimentieren. Es stellte sich heraus, dass vMotion von Host zu Host den Server "heilen" kann, aber auch den "schnellen" Server in einen "langsamen" verwandeln kann. Es scheint, das ist es â einige Hosts haben ein Problem⊠aber⊠nein. Eine bestimmte virtuelle Maschine war auf Host A langsam, arbeitete aber schnell auf Host B. Eine andere virtuelle Maschine dagegen arbeitete schnell auf A und war auf B langsam! Auf den Hosts waren oft sowohl "schnelle" als auch "langsame" Maschinen im Einsatz!
Von diesem Moment an roch es klar nach Schwefel in der Luft. Denn das Problem konnte weder der virtuellen Maschine (Windows-Patches zum Beispiel) zugeschrieben werden â sie wurde bei vMotion zu einer "schnellen". Aber das Problem konnte auch nicht dem Host zugeordnet werden â denn darauf konnten sowohl "schnelle" als auch "langsame" Maschinen sein. Es hatte auch nichts mit der Last zu tun â ich konnte eine "langsame" Maschine auf einem Host bekommen, wo auĂer ihr nichts war.
In meiner Verzweiflung startete ich den Process Explorer von Sysinternals und schaute mir den SQL-Stack an. Auf den langsamen Maschinen fiel mir sofort folgende Zeile auf:
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 etwas. Ein 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 0)
{
Console.WriteLine("{0}", counter);
}
}
}
}Dieses Programm zeigte eine noch drastischere Verlangsamung â auf den âschnellenâ Maschinen zeigte es 16-18 Millionen Zyklen pro Sekunde, wĂ€hrend es auf den langsamen eineinhalb Millionen oder sogar 700.000 anzeigte. Das heiĂt, der Unterschied betrĂ€gt 10-20 Mal (!!!). Das war bereits ein kleiner Sieg: jedenfalls gab es keine Gefahr, zwischen dem Microsoft- und dem VMware-Support festzusitzen, sodass sie sich gegenseitig die Schuld zuschoben.
Danach stellte der Fortschritt ein â Urlaub, wichtige Angelegenheiten, virale Hysterie und ein deutlicher Anstieg der Belastung. Ich erwĂ€hnte das magische Problem oft meinen Kollegen, aber manchmal schien es, als wĂŒrden sie mir nicht immer glauben â die Aussage, dass VMware den Code 10-20 Mal verlangsamt, war einfach zu absurd.
Ich versuchte selbst herauszufinden, was eigentlich bremst. Manchmal hatte ich das GefĂŒhl, dass ich die Lösung gefunden hatte â das Ein- und Ausschalten von Hot Plugs, das Ăndern des Speichervolumens oder der Anzahl der Prozessoren verwandelte oft die Maschine in eine âschnelleâ. Aber nicht fĂŒr immer. Was jedoch wahr war, ist, dass es genĂŒgt, hinauszugehen und auf das Rad zu klopfen â also zu Ă€ndern kann diese den Parameter der virtuellen Maschine
SchlieĂlich fanden meine amerikanischen Kollegen plötzlich die Hauptursache.

Die Hosts unterschieden sich in der Frequenz!
- Im Allgemeinen ist das nicht schlimm. Aber: beim Umzug von einem 'heimischen' Host zu einem Host mit 'anderer' Frequenz muss VMware das Ergebnis von GetTimePrecise korrigieren.
- Im Allgemeinen ist das nicht schlimm, es sei denn, es gibt eine Anwendung, die Millionen von Malen pro Sekunde die genaue Zeit abfragt, wie z.B. SQL-Server.
- Aber das ist auch nicht schlimm, denn SQL-Server tut dies lÀngst nicht immer (siehe Schlussfolgerung).
Es gibt jedoch FÀlle, in denen diese Stolpersteine schmerzhaft sind. Und ja, als ich auf das Rad klopfte (etwas in den VM-Einstellungen Ànderte), zwang ich VMware, die Konfiguration 'neu zu berechnen', und die Frequenz des aktuellen Hosts wurde zur 'heimischen' Frequenz der Maschine.
Lösung
Wenn Sie die Virtualisierung des TSC deaktivieren, gibt das Lesen des TSC aus der virtuellen Maschine den TSC-Wert der physischen Maschine zurĂŒck, und das Schreiben des TSC von der virtuellen Maschine hat keine Auswirkungen. Der Umzug der virtuellen Maschine zu einem anderen Host, das Fortsetzen aus dem Suspendierten Zustand oder das ZurĂŒcksetzen auf einen Snapshot fĂŒhrt dazu, dass der TSC sprunghaft springt. Einige Gastbetriebssysteme können nicht booten oder zeigen andere Zeitprobleme 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 den aktuellen Produkten erheblich verbessert. Die Funktion wurde auch empfohlen, wenn Messungen durchgefĂŒhrt werden, die eine prĂ€zise Quelle fĂŒr die reale Zeit in der virtuellen Maschine erfordern.
Kurz gesagt, es muss ein Parameter hinzugefĂŒgt werden.
monitor_control.virtual_rdtsc = FALSE
Fazit
Sie haben sich sicherlich gefragt: Warum wird GetTimePrecise so oft in SQL aufgerufen?
Ich habe keinen Zugang zu den SQL-Server-Quellcodes, aber die Logik sagt Folgendes. SQL ist fast ein Betriebssystem mit kooperativer NebenlĂ€ufigkeit, in dem jeder Thread von Zeit zu Zeit ânachgebenâ muss. Wo wĂ€re das besser? Dort, wo es ein natĂŒrliches Warten gibt â Lock oder IO. Gut, und was, wenn wir Berechnungsschleifen haben? Dann ist der offensichtlichste und fast einzige Ort â im Interpreter (es ist nicht ganz ein Interpreter), nach der AusfĂŒhrung der nĂ€chsten Anweisung.
In der Regel wird SQL-Server nicht fĂŒr reine Berechnungsarbeiten verwendet und das ist kein Problem. Aber Schleifen mit der Arbeit an temporĂ€ren Tabellen (die sofort zwischengespeichert werden) verwandeln den Code in eine Abfolge von sehr schnell ausfĂŒhrbaren Anweisungen.
Ăbrigens, wenn man die Funktion in NATIVELY COMPILED einwickelt, hört sie auf, Zeit abzufragen, und ihre Geschwindigkeit erhöht sich um das Zehnfache. Und wie steht es mit kooperativem Multitasking? Nun, fĂŒr nativ kompilierte Codes musste in SQL PREEMPTIVE MULTITASKING implementiert werden.
Quelle: habr.com
