Da, moj stari laptop je nekoliko puta moćniji od vašeg proizvodnog servera

Upravo su to žalbe koje sam čuo od naših programera. Najzanimljivije je da se to pokazalo tačnim, što je dovelo do dugotrajne istrage. Govorit ćemo o SQL serverima koji rade na VMware-u.

Da, moj stari laptop je nekoliko puta moćniji od vašeg proizvodnog servera

Zapravo, lako je osigurati da proizvodni server beznadežno stoji iza laptopa. Izvršite (ne na tempdb i ne na bazi podataka sa omogućenom odloženom izdržljivošću) kod:

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

Na mom desktopu to traje 5 sekundi, a na proizvodnom serveru 28 sekundi. Zato što SQL mora čekati fizički kraj unosa u dnevnik transakcija, a mi ovdje radimo vrlo kratke transakcije. Grubo govoreći, ubacili smo veliki, snažan kamion u gradski saobraćaj, i gledali kako ga hrabro prestižu dostavljači pizze na skuterima - ovdje nije važna propusnost, važna je samo latencija. I nijedna mrežna pohrana, bez obzira koliko nula ima u svojoj cijeni, ne može pobijediti lokalni SSD u smislu kašnjenja.

(u komentarima se ispostavilo da sam lagao - imao sam odloženu trajnost na oba mjesta. Bez odložene trajnosti ispada:
Desktop - 39 sekundi, 15K tr/sec, 0.065ms/io povratno
PROD - 360 sekundi, 1600 tr/sec, 0.6 ms
Trebao sam primijetiti da je prebrzo)

Međutim, u ovom slučaju imamo posla s trivijalnim nulama Riemannove zeta funkcije sa trivijalnim primjerom. U primjeru koji su mi programeri donijeli bilo je drugačije. Uvjerio sam se da su u pravu i počeo iz primjera uklanjati sve njihove specifičnosti vezane za poslovnu logiku. U nekom trenutku sam shvatio da mogu potpuno odbaciti njihov kod i napisati svoj - što pokazuje isti problem - u produkciji radi 3-4 puta sporije:

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

Ako je sve u redu, tada će provjera primarnosti broja trajati 6-7-8 sekundi. Ovo se dogodilo na brojnim serverima. Ali kod nekih je provjera trajala 25-40 sekundi. Zanimljivo, nije bilo servera na kojima bi izvršenje trajalo, recimo, 14 sekundi – kod je radio ili vrlo brzo ili vrlo sporo, odnosno problem je bio, recimo, crno-bijeli.

Šta sam učinio? Korištene VMware metrike. Sve je bilo u redu - resursa je bilo na pretek, Ready time = 0, bilo je dovoljno svega, tokom testa i na brzim i na sporim serverima CPU = 100 na jednom vCPU-u. Uradio sam test da izračunam broj Pi - test je pokazao iste rezultate na bilo kom serveru. Miris crne magije postajao je sve jači i jači.

Kada sam stigao do DEV farme, počeo sam da se igram sa serverima. Ispostavilo se da vMotion od hosta do hosta može "izliječiti" server, ali može i pretvoriti "brzi" server u "spor". Čini se da je to to - neki domaćini imaju problem... ali... ne. Neka virtuelna mašina je bila spora na hostu, recimo A, ali je brzo radila na hostu B. A druga virtuelna mašina je, naprotiv, radila brzo na A i usporavala na B! I "brze" i "spore" mašine su se često vrtele na hostu!

Od tog trenutka u vazduhu se osećao izrazit miris sumpora. Na kraju krajeva, problem se nije mogao pripisati virtuelnoj mašini (Windows zakrpe, na primjer) - na kraju krajeva, on se pretvorio u "brzo" s vMotion-om. Ali problem se takođe ne može pripisati hostu - na kraju krajeva, on bi mogao imati i "brze" i "spore" mašine. Takođe, to nije bilo povezano sa opterećenjem - uspio sam nabaviti „sporu“ mašinu na hostu, gdje osim nje nije bilo ničega.

Iz očaja, pokrenuo sam Process Explorer sa Sysinternals-a i pogledao SQL stack. Na sporim mašinama red mi je odmah zapao za oko:

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

Ovo je već bilo nešto. Program je napisan:

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

Ovaj program je pokazao još izraženije usporavanje - na "brzim" mašinama pokazuje 16-18 miliona ciklusa u sekundi, dok na sporim mašinama pokazuje milion i po, ili čak 700 hiljada. Odnosno, razlika je 10-20 puta (!!!). Ovo je već bila mala pobjeda: u svakom slučaju, nije bilo prijetnje da se zaglavi između podrške Microsofta i VMwarea kako bi okrenuli strelice jedni na druge.

Tada je napredak stao - odmori, važne stvari, virusna histerija i naglo povećanje opterećenja. Često sam kolegama spominjao magični problem, ali se na momente činilo da mi ni ne vjeruju uvijek - izjava da VMware usporava kod 10-20 puta bila je previše monstruozna.

Pokušao sam da otkrijem šta me usporava. Ponekad mi se činilo da sam pronašao rješenje - uključivanje i isključivanje Hot plugova, mijenjanje količine memorije ili broja procesora često je mašinu pretvaralo u “brzu”. Ali ne zauvek. Ali ono što se pokazalo tačnim je da je dovoljno izaći i pokucati na volan – odnosno promijeniti se bilo koji parametar virtuelne mašine

Konačno, moje američke kolege su iznenada otkrile osnovni uzrok.

Da, moj stari laptop je nekoliko puta moćniji od vašeg proizvodnog servera

Domaćini su se razlikovali po učestalosti!

  • Po pravilu, to nije velika stvar. Ali: kada se prelazi sa 'nativnog' hosta na host sa 'drugačijom' frekvencijom, VMware mora prilagoditi GetTimePrecise rezultat.
  • Po pravilu, to nije problem, osim ako ne postoji aplikacija koja traži tačno vrijeme milione puta u sekundi, poput SQL servera.
  • Ali to nije strašno, jer SQL server to ne radi uvijek (pogledajte Zaključak)

Ali postoje slučajevi kada ova grabulja snažno udari. Pa ipak, da, kuckanjem po kotačiću (promjenom nečega u postavkama VM-a) prisilio sam VMware da 'ponovo izračuna' konfiguraciju, a frekvencija trenutnog hosta je postala 'prirodna' frekvencija mašine.

odluka

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

Kada onemogućite virtuelizaciju TSC-a, čitanje TSC-a iz virtuelne mašine vraća TSC vrednost fizičke mašine, a pisanje TSC-a iz virtuelne mašine nema efekta. Migracija virtuelne mašine na drugi host, njeno vraćanje iz suspendovanog stanja ili vraćanje na snimku uzrokuje da TSC skače neprekidno. Neki operativni sistemi za goste ne uspevaju da se pokrenu ili pokazuju druge probleme sa merenjem vremena kada je TSC virtuelizacija onemogućena. U prošlosti se ova funkcija ponekad preporučivala za poboljšanje performansi aplikacija koje često čitaju TSC, ali performanse virtualnog TSC-a su značajno poboljšane u trenutnim proizvodima. Ova funkcija je takođe preporučena za upotrebu prilikom izvođenja merenja koja zahtevaju precizan izvor realnog vremena u virtuelnoj mašini.

Ukratko, morate dodati parametar

monitor_control.virtual_rdtsc = FALSE

zaključak

Vjerovatno imate pitanje: zašto SQL tako često poziva GetTimePrecise?

Nemam izvorni kod SQL servera, ali logika kaže ovo. SQL je gotovo operativni sistem sa kooperativnom konkurentnošću, gdje svaka nit mora s vremena na vrijeme "popustiti". Gdje je najbolje to učiniti? Gdje postoji prirodno čekanje - zaključavanje ili IO. U redu, ali šta ako vrtimo računske petlje? Tada je očigledno i gotovo jedino mjesto u interpretatoru (ovo zapravo nije tumač), nakon izvršenja sljedećeg izraza.

Generalno, SQL server se ne koristi za čisto računarstvo i to nije problem. Ali petlje koje rade sa svim vrstama privremenih tabela (koje se odmah keširaju) pretvaraju kod u niz vrlo brzo izvršenih naredbi.

Usput, ako umotate funkciju u NATIVELY COMPILED, onda prestaje tražiti vrijeme, a brzina joj se povećava za 10 puta.Šta je sa kooperativnim multitaskingom? Ali za izvorno preveden kod morali smo da uradimo PREVENTIVNO MULTITASKING u SQL-u.

izvor: www.habr.com

Dodajte komentar