Po, laptopi im i vjetër është disa herë më i fuqishëm se serveri juaj i prodhimit.

Këto janë pikërisht ankesat që kam dëgjuar nga zhvilluesit tanë. Gjëja më interesante është se kjo doli të ishte e vërtetë, duke shkaktuar një hetim të gjatë. Ne do të flasim për serverët SQL që funksionojnë në VMware.

Po, laptopi im i vjetër është disa herë më i fuqishëm se serveri juaj i prodhimit.

Në fakt, është e lehtë të sigurohet që serveri i prodhimit të jetë pa shpresë pas laptopit. Ekzekutoni (jo në tempdb dhe jo në një bazë të dhënash me qëndrueshmëri të vonuar të aktivizuar) kodin:

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

Në desktopin tim zgjat 5 sekonda, dhe në serverin e prodhimit zgjat 28 sekonda. Sepse SQL duhet të presë për përfundimin fizik të hyrjes në regjistrin e transaksioneve, dhe ne po bëjmë transaksione shumë të shkurtra këtu. Përafërsisht, ne futëm një kamion të madh e të fuqishëm në trafikun e qytetit dhe pamë se si u kap me shpejtësi nga njerëzit që shpërndanin pica në skuter - qarkullimi nuk është i rëndësishëm këtu, vetëm vonesa është e rëndësishme. Dhe asnjë ruajtje në rrjet, pa marrë parasysh sa zero ka në çmimin e tij, nuk mund të mposhtë SSD-në lokale për sa i përket vonesës.

(në komente doli që gënjeva - e kisha vonuar qëndrueshmërinë në të dyja vendet. Pa qëndrueshmëri të vonuar del:
Desktop - 39 sekonda, 15K tr/sek, 0.065 ms/io vajtje-ardhje
PROD - 360 sekonda, 1600 tr/sek, 0.6 ms
Duhet të kisha vënë re se ishte shumë shpejt)

Megjithatë, në këtë rast kemi të bëjmë me zero triviale të funksionit zeta të Riemann-it me një shembull të parëndësishëm. Në shembullin që më sollën zhvilluesit, ishte ndryshe. Unë u binda se ata kishin të drejtë dhe fillova të heq nga shembulli të gjitha specifikat e tyre që lidhen me logjikën e biznesit. Në një moment kuptova se mund ta hidhja plotësisht kodin e tyre dhe të shkruaja timin - gjë që tregon të njëjtin problem - në prodhim ai funksionon 3-4 herë më ngadalë:

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

Nëse gjithçka është në rregull, atëherë kontrollimi i parësisë së një numri do të zgjasë 6-7-8 sekonda. Kjo ndodhi në një numër serverësh. Por në disa, kontrolli zgjati 25-40 sekonda. Shtë interesante që nuk kishte serverë ku ekzekutimi do të zgjaste, të themi, 14 sekonda - kodi funksionoi ose shumë shpejt ose shumë ngadalë, domethënë, problemi ishte, le të themi, bardh e zi.

Cfare kam bere? Metrikat e përdorura të VMware. Gjithçka ishte mirë atje - kishte një bollëk burimesh, kohë gati = 0, kishte mjaft nga gjithçka, gjatë provës në serverët e shpejtë dhe të ngadaltë CPU = 100 në një vCPU. Kam bërë një test për të llogaritur numrin Pi - testi tregoi të njëjtat rezultate në çdo server. Era e magjisë së zezë bëhej gjithnjë e më e fortë.

Pasi arrita në fermën DEV, fillova të luaja me serverët. Doli që vMotion nga hosti në host mund të "kurojë" një server, por gjithashtu mund të kthejë një server "të shpejtë" në një "të ngadaltë". Duket sikur kjo është - disa hostë kanë një problem... por... jo. Një makinë virtuale ishte e ngadaltë në host, le të themi A, por funksionoi shpejt në host B. Dhe një tjetër makinë virtuale, përkundrazi, funksionoi shpejt në A dhe ngadalësoi shpejtësinë në B! Të dy makinat "të shpejta" dhe "të ngadalta" shpesh rrotulloheshin në host!

Që nga ai moment, kishte një erë të qartë squfuri në ajër. Në fund të fundit, problemi nuk mund t'i atribuohej makinës virtuale (për shembull, arna të Windows) - në fund të fundit, ai u shndërrua në "i shpejtë" me vMotion. Por problemi gjithashtu nuk mund t'i atribuohej hostit - në fund të fundit, ai mund të kishte makina "të shpejta" dhe "të ngadalta". Gjithashtu, kjo nuk lidhej me ngarkesën - arrita të marr një makinë "të ngadaltë" në host, ku nuk kishte asgjë fare përveç saj.

Nga dëshpërimi, nisa Process Explorer nga Sysinternals dhe shikova pirgun e SQL. Në makinat e ngadalta, linja më tërhoqi menjëherë vëmendjen:

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

Kjo ishte tashmë diçka. Programi ishte shkruar:

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

Ky program demonstroi një ngadalësim edhe më të theksuar - në makinat "të shpejta" tregon 16-18 milion cikle në sekondë, ndërsa në makinat e ngadalta tregon një milion e gjysmë, apo edhe 700 mijë. Kjo është, ndryshimi është 10-20 herë (!!!). Kjo ishte tashmë një fitore e vogël: në çdo rast, nuk kishte asnjë kërcënim për të ngecur midis mbështetjes së Microsoft dhe VMware në mënyrë që ata të kthenin shigjetat kundër njëri-tjetrit.

Pastaj përparimi u ndal - pushime, çështje të rëndësishme, histeri virale dhe një rritje e mprehtë e ngarkesës së punës. Unë shpesh ua përmenda problemin magjik kolegëve të mi, por ndonjëherë dukej se ata as nuk më besonin gjithmonë - deklarata se VMware ngadalëson kodin me 10-20 herë ishte shumë monstruoze.

U përpoqa të gërmoja vetë atë që po më ngadalësonte. Ndonjëherë më dukej se kisha gjetur një zgjidhje - ndezja dhe fikja e prizave Hot, ndryshimi i sasisë së kujtesës ose numri i procesorëve shpesh e kthenin makinën në një "të shpejtë". Por jo përgjithmonë. Por ajo që doli të jetë e vërtetë është se mjafton të dalësh dhe të trokasësh në timon - domethënë të ndryshosh ndonjë parametri i makinës virtuale

Më në fund, kolegët e mi amerikanë papritmas gjetën shkakun kryesor.

Po, laptopi im i vjetër është disa herë më i fuqishëm se serveri juaj i prodhimit.

Pritësit ndryshonin në frekuencë!

  • Si rregull, kjo nuk është një punë e madhe. Por: kur kaloni nga një host 'vendas' në një host me një frekuencë 'të ndryshme', VMware duhet të rregullojë rezultatin GetTimePrecise.
  • Si rregull, ky nuk është problem, përveç nëse ka një aplikacion që kërkon kohën e saktë miliona herë në sekondë, si serveri SQL.
  • Por kjo nuk është e frikshme, pasi serveri SQL nuk e bën gjithmonë këtë (shiko përfundimin)

Por ka raste kur kjo grabujë godet fort. E megjithatë, po, duke trokitur në timon (duke ndryshuar diçka në cilësimet e VM) e detyrova VMware të 'rillogarisë' konfigurimin dhe frekuenca e hostit aktual u bë frekuenca 'vendase' e makinës.

vendim

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

Kur çaktivizon virtualizimin e TSC, leximi i TSC nga brenda makinës virtuale kthen vlerën TSC të makinës fizike dhe shkrimi i TSC nga brenda makinës virtuale nuk ka asnjë efekt. Migrimi i makinës virtuale në një host tjetër, rikthimi i saj nga gjendja e pezulluar ose rikthimi në një pamje të çastit bën që TSC të kërcejë në mënyrë të ndërprerë. Disa sisteme operative të ftuar dështojnë të nisin ose shfaqin probleme të tjera të matjes së kohës kur virtualizimi TSC është i çaktivizuar. Në të kaluarën, kjo veçori ndonjëherë është rekomanduar për të përmirësuar performancën e aplikacioneve që lexojnë shpesh TSC, por performanca e TSC virtuale është përmirësuar ndjeshëm në produktet aktuale. Funksioni është rekomanduar gjithashtu për përdorim kur kryeni matje që kërkojnë një burim të saktë të kohës reale në makinën virtuale.

Me pak fjalë, duhet të shtoni parametrin

monitor_control.virtual_rdtsc = FALSE

Përfundim

Ju ndoshta keni një pyetje: pse SQL thërret kaq shpesh GetTimePrecise?

Unë nuk kam kodin burimor të serverit SQL, por logjika e thotë këtë. SQL është pothuajse një sistem operativ me konkurencë bashkëpunuese, ku çdo thread duhet të "dorëzohet" herë pas here. Ku është vendi më i mirë për ta bërë këtë? Aty ku ka një pritje natyrale - bllokohet ose IO. Mirë, por çka nëse ne jemi duke rrotulluar sythe llogaritëse? Pastaj vendi i dukshëm dhe pothuajse i vetëm është në përkthyes (ky nuk është në të vërtetë një përkthyes), pas ekzekutimit të deklaratës tjetër.

Në përgjithësi, serveri SQL nuk përdoret për thonjtë e pastër kompjuterik dhe ky nuk është problem. Por unazat që funksionojnë me të gjitha llojet e tabelave të përkohshme (të cilat ruhen menjëherë në memorien specifike) e kthejnë kodin në një sekuencë deklaratash të ekzekutuara shumë shpejt.

Meqë ra fjala, nëse e mbështjellni funksionin në NATIVELY COMPILED, atëherë ai ndalon së kërkuari kohë dhe shpejtësia e tij rritet me 10 herë. Po në lidhje me shumë detyrat bashkëpunuese? Por për kodin e përpiluar në mënyrë origjinale, ne duhej të bënim SHUMË DETYRIME PARAPRAKEVE në SQL.

Burimi: www.habr.com

Shto një koment