Ja, mein alter Laptop ist um ein Vielfaches leistungsfÀhiger als Ihr Produktionsserver.

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.

Ja, mein alter Laptop ist um ein Vielfaches leistungsfÀhiger als Ihr Produktionsserver.

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
GO

Wenn 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.

Ja, mein alter Laptop ist um ein Vielfaches leistungsfÀhiger als Ihr Produktionsserver.

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

www.vmware.com/files/pdf/techpaper/Timekeeping-In-VirtualMachines.pdf

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

Kaufen Sie zuverlĂ€ssiges Hosting fĂŒr Websites mit DDoS-Schutz und VPS-VDS-Servern đŸ”„ Kaufen Sie zuverlĂ€ssiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server | ProHoster