Da, moje staro prijenosno računalo nekoliko je puta snažnije od vašeg proizvodnog poslužitelja.

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

Da, moje staro prijenosno računalo nekoliko je puta snažnije od vašeg proizvodnog poslužitelja.

Zapravo, lako je postaviti produkcijski poslužitelj beznadno iza prijenosnog računala. Pokrenite (ne na tempdb i ne na bazi podataka s omogućenom odgođ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

Potrebno je 5 sekundi na mojoj radnoj površini i 28 sekundi na proizvodnom poslužitelju. Budući da SQL mora čekati fizički kraj pisanja u dnevnik transakcija, a mi ovdje radimo vrlo kratke transakcije. Ugrubo rečeno, veliki moćni kamion smo uletjeli u gradsku gužvu i gledamo kako ga famozno pretječu dostavljači pizze na skuterima - tu nije bitna propusnost, bitna je samo latencija. I niti jedna mrežna pohrana, koliko god nula bila u njenoj cijeni, neće moći nadmašiti lokalni SSD u latenciji.

(u komentarima se pokazalo da sam lagao - imao sam odgođenu trajnost na oba mjesta. Bez odgođene trajnosti ispada:
Stolno računalo - 39 sekundi, 15K tr/s, 0.065 ms /io povratno putovanje
PROD - 360 sekundi, 1600 tr/sek, 0.6 ms
Trebao sam primijetiti da je prebrzo)

Međutim, u ovom slučaju radi se o trivijalnim nulama Riemannove zeta funkcije s 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 čistiti sve njihove specifičnosti vezane uz 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 s vama sve u redu, tada će provjera jednostavnosti broja trajati 6-7-8 sekundi. To se dogodilo na brojnim poslužiteljima. Ali na nekima je provjera trajala 25-40 sekundi. Zanimljivo, nije bilo servera na kojima bi izvršenje trajalo recimo 14 sekundi - kod je radio ili jako brzo ili jako sporo, odnosno problem je bio, ajmo reći, crno-bijeli.

Što sam učinio? Ušao u VMware metrics. Tu je sve bilo u redu - bilo je dosta resursa, Ready time = 0, svega je bilo dovoljno, tijekom testa i na brzim i na sporim poslužiteljima CPU = 100 na jednom vCPU-u. Napravio sam test za izračunavanje broja Pi - test je pokazao iste rezultate na svim poslužiteljima. Miris crne magije postajao je sve jači i jači.

Nakon što sam izašao na DEV farmu, počeo sam se igrati s poslužiteljima. Pokazalo se da vMotion od hosta do hosta može “izliječiti” server, ali isto tako može “brzi” server pretvoriti u “spori”. Čini se da je to to - neki hostovi imaju problem ... ali ... ne. Neki virtualni stroj je usporio na hostu, recimo A, ali je radio brzo na hostu B. A drugi virtualni stroj je, naprotiv, radio brzo na A i usporio na B! Na domaćinu su se često vrtjeli i “brzi” i “spori” automobili!

Od tog trenutka u zraku se osjećao jasan miris sumpora. Uostalom, problem se ne može pripisati nijednom virtualnom stroju (npr. zakrpe za Windows) - uostalom, pretvorio se u "brzi" s vMotionom. Ali problem se također ne može pripisati hostu - na kraju krajeva, mogao je imati i "brze" i "spore" strojeve. Također nije bilo povezano s opterećenjem - uspio sam dobiti "spori" stroj na hostu, gdje nije bilo ničega osim njega.

Iz očaja sam pokrenuo Sysinternalsov Process Explorer i pogledao SQL stog. Na sporim strojevima linija mi je odmah zapela 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

To 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 pokazao je još izraženije usporavanje - na "brzim" strojevima pokazuje 16-18 milijuna ciklusa u sekundi, dok na sporim - milijun i pol, ili čak 700 tisuća. Odnosno, razlika je 10-20 puta (!!!). To je već bila mala pobjeda: u svakom slučaju nije prijetilo da se zaglavimo između Microsoftove i VMware podrške pa da jedna drugoj prebacuju strelice.

Zatim je napredak stao - odmor, važne stvari, virusna histerija i naglo povećanje opterećenja. Kolegama sam često spominjao magični problem, ali ponekad se činilo da mi čak ni ne vjeruju uvijek - izjava da je VMware usporio kod za 10-20 puta bila je previše monstruozna.

Pokušao sam sam iskopati što ga usporava. Na trenutke mi se činilo da sam pronašao rješenje - paljenjem i gašenjem Hot plugova, mijenjanjem količine memorije ili broja procesora često je stroj postajao “brzi”. Ali ne zauvijek. No ono što se pokazalo točnim jest da je dovoljno izaći van i pokucati po volanu – odnosno promijeniti bilo koji parametar virtualnog stroja

Napokon su moji američki kolege iznenada pronašli temeljni uzrok.

Da, moje staro prijenosno računalo nekoliko je puta snažnije od vašeg proizvodnog poslužitelja.

Domaćini su se razlikovali po učestalosti!

  • U pravilu, to nije zastrašujuće. Ali: prilikom prelaska s 'nativnog' hosta na host s 'različitom' frekvencijom, VMware mora prilagoditi GetTimePrecise rezultat.
  • U pravilu to nije problem, osim ako ne postoji aplikacija koja traži točno vrijeme milijune puta u sekundi, poput SQL servera.
  • Ali ni to nije strašno, jer SQL poslužitelj to ne radi uvijek (pogledajte zaključak)

Ali postoje slučajevi kada ove grablje boli. I da, kuckanjem na kotačiću (promjenom nečega u postavkama VM-a), natjerao sam VMware da 'preračuna' konfiguraciju, a frekvencija trenutnog hosta postala je 'native' frekvencija stroja.

odluka

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

Kada onemogućite virtualizaciju TSC-a, čitanje TSC-a unutar virtualnog stroja vraća vrijednost TSC-a fizičkog stroja, a pisanje TSC-a unutar virtualnog stroja nema učinka. Migracija virtualnog stroja na drugo glavno računalo, njegovo ponovno pokretanje iz suspendiranog stanja ili vraćanje na snimku uzrokuje nekontinuirano skakanje TSC-a. Neki gostujući operativni sustavi ne mogu se pokrenuti ili pokazuju druge probleme s mjerenjem vremena kada je TSC virtualizacija onemogućena. U prošlosti se ova značajka ponekad preporučivala za poboljšanje performansi aplikacija koje često čitaju TSC, ali izvedba virtualnog TSC-a znatno je poboljšana u trenutnim proizvodima. Značajka se također preporučuje za korištenje pri izvođenju mjerenja koja zahtijevaju precizan izvor stvarnog vremena u virtualnom stroju.

Ukratko, trebate dodati parametar

monitor_control.virtual_rdtsc = FALSE

Zaključak

Vjerojatno imate pitanje: zašto bi SQL tako često pozivao GetTimePrecise?

Nemam izvore SQL servera, ali logika kaže ovako. SQL je gotovo operativni sustav s kooperativnom konkurentnošću, gdje svaka nit mora "popustiti" s vremena na vrijeme. Gdje je to najbolje učiniti? Gdje postoji prirodno očekivanje - zaključavanje ili IO. U redu, ali što ako vrtimo računalne cikluse? Tada je očito i gotovo jedino mjesto u interpreteru (ovo i nije baš interpreter), nakon izvršenja sljedećeg operatora.

SQL poslužitelj se u pravilu ne koristi za čisto računalstvo i to nije problem. Ali ciklusi s radom sa svim vrstama privremenih tablica (koje se odmah pohranjuju u predmemoriju) pretvaraju kod u slijed vrlo brzo izvršenih naredbi.

Usput, ako je funkcija umotana u NATIVELY COMPILED, tada prestaje zahtijevati vrijeme, a njezina brzina raste 10 puta. Ali što je s kooperativnim multitaskingom? Ali za nativno kompilirani kod, morao sam napraviti PREEMPTIVE MULTITASKING u SQL-u.

Izvor: www.habr.com

Dodajte komentar