Áno, môj starý laptop je niekoľkokrát výkonnejší než váš produkčný server

Takéto obavy som počul od našich vývojárov. Najzaujímavejšie je, že sa to ukázalo ako pravda, čo viedlo k dlhému vyšetrovaniu. Hovoríme o SQL serveroch, ktoré bežia na našom VMware.

Áno, môj starý laptop je niekoľkokrát výkonnejší než váš produkčný server

V skutočnosti je veľmi jednoduché dosiahnuť, aby production server úplne zaostával za laptopom. Spustite (nie na tempdb a nie na databáze s povolenou oneskorenou trvanlivosťou) kód:

set nocount on
create table _t (v varchar(100))
declare @n int=300000
while @n>0 begin 
  insert into _t select 'Aký pomalý!' 
  delete from _t
  set @n=@n-1
  end
GO
drop table _t

Na mojom desktopu to trvá 5 sekúnd, zatiaľ čo na production serveri 28 sekúnd. Pretože SQL musí čakať na fyzické dokončenie zápisu do transakčného logu a my tu robíme veľmi krátke transakcie. Zjednodušene povedané, dostali sme veľký silný kamión do mestského premávky a sledujeme, ako ho rýchlo predbehujú kuriéri na skutroch - tu nie je dôležitý throughput, dôležitá je len latencia. A žiadne sieťové úložisko, nech je jeho cena akokoľvek vysoká, nemôže vyhrať v latencii proti miestnemu SSD.

(v komentároch sa ukázalo, že som klamal - v oboch miestach mám oneskorenú trvanlivosť. Bez oneskorenej trvanlivosti to vychádza:
Desktop - 39 sekúnd, 15K tr/sec, 0.065ms /io roundtrip
PROD - 360 sekúnd, 1600 tr/sec, 0.6ms
Mal som si všimnúť, že bolo príliš rýchlo)

V tomto prípade však máme dočinenia s triviálnymi nulami z tejto Riemannovej funkcie s triviálnym príkladom. V tom príklade, ktorý mi priniesli vývojári, bolo niečo iné. Uistil som sa, že majú pravdu, a začal som čistiť z príkladu všetku ich špecifickosť súvisiacu s obchodnou logikou. V istom momente som si uvedomil, že môžem úplne vyhodiť ich kód a napísať svoj - ktorý demonštruje rovnaký problém - na production beží 3-4 krát pomalšie:

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 -- controluj nepárne čísla až do 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

Ak máte všetko v poriadku, kontrola prvočísel bude trvať 6-7-8 sekúnd. Tak to bolo na niekoľkých serverov. Ale na niektorých trvala kontrola 25-40 sekúnd. Zaujímavé je, že neexistovali servery, na ktorých by vykonávanie trvalo napríklad 14 sekúnd - kód fungoval buď veľmi rýchlo, alebo úplne pomaly, to znamená, že problém bol, povedzme, čierno-biely.

Čo som urobil? Pozrel som do metriky VMware. Všetko vyzeralo v poriadku – zdroje boli dostatočné, Ready time = 0, všetko stačilo, počas testu na rýchlych aj pomalých serveroch CPU=100 na jednom vCPU. Vzal som test na výpočet čísla Pi – test ukazoval rovnaké výsledky na akýchkoľvek serveroch. Čoraz viac to vonia po čiernej mágii.

Keď som sa dostal na DEV farmu, začal som experimentovať so servermi. Zistilo sa, že vMotion z hostiteľa na hostiteľa dokáže "vyliečiť" server, ale môže aj naopak, "rýchly" server premeniť na "pomalý". Zjavne toto bude – niektoré hostiteľské servery majú problém... ale... nie. Nejaká virtuálka spomaľovala na hostiteľovi, povedzme A, ale na hostiteľovi B fungovala rýchlo. A iná virtuálka naopak, fungovala rýchlo na A a pomaly na B! Na hostiteľovi často rotovali aj "rýchle" aj "pomalé" mašinky!

Od tohto momentu vzduch jasne zapáchal sírou. Veď problém nemohol byť pripísaný žiadnej virtuálke (napríklad windows patchy) – veď sa stala "rýchlou" pri vMotion. Ale problém nemohol byť ani na hostiteľovi – veď na ňom mohli byť ako "rýchle", tak aj "pomalé" mašinky. Taktiež to nebolo spojené s zaťažením – podarilo sa mi získať "pomalú" mašinu na hostiteľovi, kde okrem nej nebolo nič.

Z beznádeje som spustil Process Explorer od Sysinternals a pozrel som sa na zásobník SQL. Na pomalých mašinkách mi hneď padla do očí táto línia:

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
… preskočené
sqldk.dll!SystemThread::MakeMiniSOSThread+0xa54
KERNEL32.DLL!BaseThreadInitThunk+0x14
ntdll.dll!RtlUserThreadStart+0x21

To už bolo niečo. Program bol napísaný:

    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);
                }
            }
        }
    }

Tento program ukázal ještě výraznější zpomalení - na 'rychlých' strojích ukazuje 16-18 milionů cyklů za sekundu, zatímco na pomalých - jeden a půl milionu, ne-li 700 tisíc. To znamená, že rozdíl činí 10-20krát (!!!). To byla malá výhra: v každém případě tu nebyla hrozba uváznout mezi podporou Microsoftu a VMware, tak aby se vzájemně obviňovali.

Dále pokrok zastavil - dovolená, důležité záležitosti, virusová hysterie a náhlé zvýšení zátěže. Často jsem zmiňoval magický problém kolegům, ale občas se zdálo, že mi ani nevěří - příliš monstruózní bylo tvrzení, že VMware zpomaluje kód 10-20krát.

Snažil jsem se sám zjistit, co zpomaluje. Občas jsem měl pocit, že jsem našel řešení - zapnutí a vypnutí hot plugů, změna velikosti paměti nebo počtu procesorů často proměnila stroj v 'rychlý'. Ale ne navždy. Ale co se ukázalo jako pravda - stačilo vyjít a zaklepat na kolo - to znamená změnit ktokoľvek parametr virtuálního stroje

Nakonec moji američtí kolegové náhle našli hlavní příčinu.

Áno, môj starý laptop je niekoľkokrát výkonnejší než váš produkčný server

Hostitelé se lišili frekvencí!

  • Obvykle to není děsivé. Ale: při přesunu z 'domovského' hostitele na hostitele s 'jinou' frekvencí musí VMware upravit výsledek GetTimePrecise.
  • Obvykle to není problém, pokud nedochází k aplikaci, která žádá přesný čas milionykrát za sekundu, jako SQL server.
  • Ale ani to není problém, protože SQL server to nedělá vždy (viz Závěr)

Ale jsou případy, kdy tyto pastičky bolestivě udeří. A ano, zaklepáním na kolo (změnou něčeho v nastavení VM) jsem donutil VMware 'přepočítat' konfiguraci a frekvence aktuálního hostitele se stala 'domovskou' frekvencí stroje.

Riešenie

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

Když deaktivujete virtualizaci TSC, čtení TSC z virtuálního stroje vrací hodnotu TSC fyzického stroje a zapisování TSC z virtuálního stroje nemá žádný účinek. Migrace virtuálního stroje na jiný hostitel, obnovení ze zadržovacího stavu nebo návrat k snímku způsobí, že TSC nepřetržitě vyskočí. Některé hostitelské operační systémy se nespustí nebo vykazují další problémy s časoměřením, když je virtualizace TSC zakázána. V minulosti byla tato funkce někdy doporučována k zlepšení výkonu aplikací, které často čtou TSC., ale výkon virtuální TSC byl v aktuálních produktech výrazně zlepšen. Tato funkce byla také doporučena pro měření, která vyžadují přesný zdroj reálného času ve virtuálním stroji.

Stručně řečeno, je potřeba přidat parametr

monitor_control.virtual_rdtsc = FALSE

Záver

Určite sa pýtate: prečo je potrebné častejšie volať GetTimePrecise v SQL?

Nemám prístup k zdrojovým kódom SQL servera, ale logika hovorí toto. SQL je takmer operačný systém s kooperatívnym paralelizmom, kde každý vlákno by malo čas od času "ustúpiť". A kde je najlepšie to urobiť? Tam, kde je prirodzené čakanie — pri zámku alebo IO. Dobre, a čo ak vykonávame výpočtové cykly? Potom je evidentné a takmer jediné miesto, kde sa to deje — v interpreteri (to nie je úplne interpreter), po vykonaní nasledujúcej inštrukcie.

Vo všeobecnosti sa SQL server nepoužíva na vykonávanie čisto výpočtových úloh a to nie je problém. Ale cykly s manipuláciou s rôznymi dočasnými tabuľkami (ktoré sú ihneď do cache pamäte) transformujú kód na sekvenciu veľmi rýchlo vykonávaných príkazov.

Mimochodom, ak obalíte funkciu do NATIVELY COMPILED, prestane žiadať čas a jej rýchlosť sa zvýši desaťnásobne. A čo kooperatívne multitasking? Práve preto bol v SQL pre natively compiled code zavedený PREEMPTIVE MULTITASKING.

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster