Da, moj stari prenosnik je nekajkrat močnejši od vašega produkcijskega strežnika.

To so trditve, ki sem jih slišal od naših razvijalcev. Najbolj zanimivo je, da se je to izkazalo za res, kar je povzročilo dolgotrajno preiskavo. Govorili bomo o strežnikih SQL, ki delujejo na VMware.

Da, moj stari prenosnik je nekajkrat močnejši od vašega produkcijskega strežnika.

Pravzaprav je preprosto postaviti produkcijski strežnik brezupno za prenosnik. Zaženite (ne v tempdb in ne v bazi podatkov z omogočeno zakasnjeno vzdržljivostjo) kodo:

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 mojem namizju traja 5 sekund, na produkcijskem strežniku pa 28 sekund. Ker mora SQL počakati na fizični konec pisanja v dnevnik transakcij, mi pa tukaj izvajamo zelo kratke transakcije. Grobo rečeno, velik zmogljiv tovornjak smo zapeljali v mestni promet in opazujemo, kako ga raznašalci pice na skuterjih famozno prehitevajo - tu pretočnost ni pomembna, pomembna je le latenca. In niti en omrežni pomnilnik, ne glede na to, koliko ničel ima njegova cena, ne bo mogel prehiteti lokalnega SSD-ja glede latence.

(v komentarjih se je izkazalo, da sem lagal - na obeh mestih sem imel zakasnjeno obstojnost. Brez zakasnjene obstojnosti se izkaže:
Namizni računalnik - 39 sekund, 15K tr/s, 0.065 ms/io povratno potovanje
PROD - 360 sekund, 1600 tr/sek, 0.6 ms
Moral bi opaziti, da je prehitro)

Vendar imamo v tem primeru opravka s trivialnimi ničlami ​​Riemannove funkcije zeta s trivialnim primerom. V primeru, ki so mi ga prinesli razvijalci, je bilo drugače. Prepričan sem bil, da imajo prav, in začel iz primera čistiti vse njihove posebnosti, povezane s poslovno logiko. Na neki točki sem ugotovil, da lahko popolnoma zavržem njihovo kodo in napišem svojo - kar kaže na isto težavo - v proizvodnji deluje 3-4 krat počasneje:

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

Če je z vami vse v redu, bo preverjanje preprostosti številke trajalo 6-7-8 sekund. To se je zgodilo na številnih strežnikih. Toda pri nekaterih je preverjanje trajalo 25-40 sekund. Zanimivo je, da ni bilo strežnikov, kjer bi izvajanje trajalo recimo 14 sekund - koda je delovala ali zelo hitro ali zelo počasi, se pravi, problem je bil recimo črno-bel.

Kaj sem naredil? Zašel sem v meritve VMware. Tam je bilo vse v redu - virov je bilo dovolj, Ready time = 0, vsega je bilo dovolj, med testom tako na hitrih kot na počasnih strežnikih CPU = 100 na enem vCPU. Opravil sem test za izračun števila Pi - test je pokazal enake rezultate na vseh strežnikih. Vonj črne magije je postajal vedno močnejši.

Ko sem prišel na farmo DEV, sem se začel igrati s strežniki. Izkazalo se je, da lahko vMotion od gostitelja do gostitelja »ozdravi« strežnik, lahko pa tudi spremeni »hiter« strežnik v »počasnega«. Zdi se, da je to to - nekateri gostitelji imajo težave ... ampak ... ne. Neki virtualni stroj se je upočasnil na gostitelju, recimo A, vendar je hitro deloval na gostitelju B. Drugi virtualni stroj pa je, nasprotno, hitro delal na A in upočasnil na B! Na hosti so se pogosto vrteli tako »hitri« kot »počasni« avtomobili!

Od tistega trenutka naprej je bil v zraku izrazit vonj po žveplu. Navsezadnje težave ni bilo mogoče pripisati nobenemu virtualnemu stroju (na primer popravki za Windows) - navsezadnje se je z vMotion spremenil v "hitrega". Toda težave tudi ni bilo mogoče pripisati gostitelju - navsezadnje bi lahko imel tako "hitre" kot "počasne" stroje. Prav tako ni bilo povezano z obremenitvijo - uspelo mi je dobiti "počasen" stroj na gostitelju, kjer poleg njega ni bilo ničesar.

Iz obupa sem zagnal Sysinternals's Process Explorer in pogledal sklad SQL. Na počasnih strojih mi je vrstica takoj padla v oči:

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

Bilo je že nekaj. Program je bil 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);
                }
            }
        }
    }

Ta program je pokazal še bolj izrazito upočasnitev - na "hitrih" strojih prikazuje 16-18 milijonov ciklov na sekundo, na počasnih pa milijon in pol ali celo 700 tisoč. To pomeni, da je razlika 10-20-krat (!!!). Že to je bila majhna zmaga: v vsakem primeru ni bilo nobene grožnje, da bi se zataknili med podporo Microsofta in VMware, da bi drug drugemu zamenjali puščice.

Potem se je napredek ustavil - počitnice, pomembne stvari, virusna histerija in močno povečanje delovne obremenitve. Kolegom sem pogosto omenil magično težavo, a včasih se je zdelo, da mi sploh ne verjamejo - izjava, da je VMware kodo upočasnil za 10-20-krat, je bila preveč pošastna.

Sam sem poskušal ugotoviti, kaj ga upočasnjuje. Včasih se mi je zdelo, da sem našel rešitev - vklop in izklop Hot plugov, spreminjanje količine pomnilnika ali števila procesorjev je stroj pogosto spremenil v "hitrega". Ampak ne za vedno. A izkazalo se je, da je dovolj, da greste ven in potrkate na volan – torej spremenite koli parameter virtualnega stroja

Končno so moji ameriški kolegi nenadoma našli temeljni vzrok.

Da, moj stari prenosnik je nekajkrat močnejši od vašega produkcijskega strežnika.

Gostitelji so se razlikovali po pogostosti!

  • Praviloma to ni strašljivo. Toda: pri prehodu z "domačega" gostitelja na gostitelja z "drugačno" frekvenco mora VMware prilagoditi rezultat GetTimePrecise.
  • Praviloma to ni problem, razen če obstaja aplikacija, ki zahteva točen čas milijonkrat na sekundo, kot je SQL strežnik.
  • Toda tudi to ni strašljivo, saj strežnik SQL tega ne počne vedno (glejte Zaključek)

So pa primeri, ko te grablje boli. In ja, s trkanjem na kolescu (s spreminjanjem nečesa v nastavitvah VM) sem prisilil VMware, da je 'preračunal' konfiguracijo in frekvenca trenutnega gostitelja je postala 'domača' frekvenca stroja.

odločitev

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

Ko onemogočite virtualizacijo TSC, branje TSC znotraj virtualnega stroja vrne vrednost TSC fizičnega stroja, pisanje TSC znotraj virtualnega stroja pa nima učinka. Selitev navideznega stroja na drugega gostitelja, njegovo nadaljevanje iz stanja začasne zaustavitve ali vrnitev na posnetek povzroči nezvezen skok TSC. Nekateri gostujoči operacijski sistemi se ne morejo zagnati ali kažejo druge težave z merjenjem časa, ko je virtualizacija TSC onemogočena. V preteklosti je bila ta funkcija včasih priporočena za izboljšanje delovanja aplikacij, ki pogosto berejo TSC, vendar je bila zmogljivost virtualnega TSC v trenutnih izdelkih bistveno izboljšana. Funkcijo priporočamo tudi za izvajanje meritev, ki zahtevajo natančen vir realnega časa v virtualnem stroju.

Skratka, dodati morate parameter

monitor_control.virtual_rdtsc = FALSE

Zaključek

Verjetno imate vprašanje: zakaj bi SQL tako pogosto klical GetTimePrecise?

Nimam virov SQL strežnika, vendar logika pravi tako. SQL je skoraj operacijski sistem s kooperativno sočasnostjo, kjer mora vsaka nit občasno "popustiti". Kje je to najboljše? Kjer je naravno pričakovanje - ključavnica ali IO. V redu, kaj pa če vrtimo računske cikle? Potem je očitno in skoraj edino mesto v tolmaču (to ni čisto tolmač), po izvedbi naslednjega operatorja.

SQL strežnik se praviloma ne uporablja za čisto računalništvo in to ni problem. Toda cikli z delom z vsemi vrstami začasnih tabel (ki so takoj predpomnjene) spremenijo kodo v zaporedje zelo hitro izvedenih stavkov.

Mimogrede, če funkcijo zavijete v NATIVELY COMPILED, potem neha zahtevati časa in njena hitrost se poveča za 10-krat. Kaj pa kooperativna večopravilnost? Toda za izvorno prevedeno kodo smo morali narediti PREPREHODNO VEČOPRAVILNOST v SQL.

Vir: www.habr.com

Dodaj komentar