Jah, minu vana sülearvuti on mitu korda võimsam kui teie tootmisserver.

Need on väited, mida kuulsin meie arendajatelt. Kõige huvitavam on see, et see osutus tõeks ja põhjustas pika uurimise. Räägime SQL-serveritest, mis töötavad VMware'is.

Jah, minu vana sülearvuti on mitu korda võimsam kui teie tootmisserver.

Tegelikult on tootmisserveri lootusetult sülearvuti taha saamine lihtne. Käivitage (mitte tempdb-s ega andmebaasis, kus on lubatud viivitatud vastupidavus) kood:

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

Minu töölaual kulub 5 sekundit ja tootmisserveris 28 sekundit. Sest SQL peab ootama tehingulogi kirjutamise füüsilist lõppu ja me teeme siin väga lühikesi tehinguid. Jämedalt öeldes sõitsime suure võimsa veokiga linnaliiklusesse ja jälgime, kuidas tõukeratastel pitsatoojad sellest kuulsalt mööda sõidavad - siin pole läbilaskevõime oluline, oluline on ainult latentsus. Ja mitte ükski võrgumälu, hoolimata sellest, kui palju nulle selle hinnas on, ei suuda latentsusaja poolest ületada kohalikku SSD-d.

(kommentaarides selgus, et valetasin - mul oli mõlemas kohas vastupidavus edasi lükatud. Ilma viivitatud vastupidavuseta selgub:
Lauaarvuti – 39 sekundit, 15 0.065 tr/s, XNUMX ms/io edasi-tagasi
PROD – 360 sekundit, 1600 tr/s, 0.6 ms
Oleksin pidanud märkama, et see on liiga kiire)

Sel juhul on aga tegemist triviaalse näitega Riemanni zeta funktsiooni triviaalsete nullidega. Näites, mille arendajad mulle tõid, oli see teisiti. Veendusin, et neil on õigus, ja hakkasin näitest välja puhastama kõiki nende äriloogikaga seotud eripärasid. Mingil hetkel taipasin, et võin nende koodi täielikult ära visata ja kirjutada enda oma - mis näitab sama probleemi - tootmises töötab see 3-4 korda aeglasemalt:

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

Kui teiega on kõik korras, kulub numbri lihtsuse kontrollimiseks 6-7-8 sekundit. Seda on juhtunud mitmetes serverites. Kuid mõnel võttis kontroll 25–40 sekundit. Huvitaval kombel polnud servereid, kus täitmine võtaks näiteks 14 sekundit – kood töötas kas väga kiiresti või väga aeglaselt, ehk siis probleem oli, ütleme, must-valge.

Mis ma teinud olen? Sattusin VMware mõõdikutesse. Seal oli kõik korras - ressursse oli küllaga, Ready time = 0, kõike oli piisavalt, testi ajal nii kiiretel kui aeglastel serveritel CPU = 100 ühel vCPU-l. Tegin testi Pi arvu arvutamiseks – test näitas samu tulemusi kõigis serverites. Musta maagia lõhn muutus aina tugevamaks.

Pärast DEV-farmist välja jõudmist hakkasin serveritega mängima. Selgus, et vMotion hostist hostini võib serverit "ravida", kuid see võib muuta ka "kiire" serveri "aeglaseks". Tundub, et see on nii - mõnel hostil on probleem ... aga ... ei. Mõni virtuaalmasin aeglustas hostis, näiteks A-s, kuid töötas kiiresti hostis B. Ja teine ​​​​virtuaalne masin, vastupidi, töötas kiiresti A-s ja aeglustus B-s! Nii "kiired" kui "aeglased" autod keerlesid sageli peremehe peal!

Sellest hetkest peale oli õhus selgelt tunda väävlilõhna. Lõppude lõpuks ei saanud probleemi seostada ühegi virtuaalse masinaga (näiteks Windowsi paigad) - lõppude lõpuks muutus see vMotioniga "kiireks". Kuid probleemi ei saanud ka hostile omistada - lõppude lõpuks võis sellel olla nii "kiire" kui ka "aeglane" masinad. See polnud ka koormaga seotud - mul õnnestus hostile saada “aeglane” masin, kus peale selle polnud üldse midagi.

Meeleheitest käivitasin Sysinternalsi Process Exploreri ja vaatasin SQL-i pinu. Aeglastel masinatel jäi kohe silma rida:

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
… vahele jäetud
sqldk.dll!SystemThread::MakeMiniSOSThread+0xa54
KERNEL32.DLL!BaseThreadInitThunk+0x14
ntdll.dll!RtlUserThreadStart+0x21

See oli juba midagi. Programm oli kirjutatud:

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

See programm näitas veelgi tugevamat aeglustumist - "kiiretel" masinatel näitab see 16–18 miljonit tsüklit sekundis, aeglastel aga poolteist miljonit või isegi 700 tuhat. See tähendab, et vahe on 10-20 korda (!!!). See oli juba väike võit: igatahes ei olnud ohtu jääda Microsofti ja VMware toe vahele nii, et nad nooli üksteisele ümber vahetaksid.

Siis seiskus progress – puhkus, olulised asjad, viiruslik hüsteeria ja töömahu järsk tõus. Mainisin seda maagilist probleemi sageli kolleegidele, kuid kohati tundus, et nad isegi ei usu mind alati – väide, et VMware aeglustas koodi 10-20 korda, oli liiga koletu.

Proovisin ise välja kaevata, mis seda aeglustab. Kohati tundus mulle, et olen leidnud lahenduse – Hot plugi sisse-välja lülitamine, mälumahu või protsessorite arvu muutmine muutis masina sageli “kiireks”. Aga mitte igavesti. Kuid tõeks osutus see, et piisab, kui minna välja ja rattale koputada - see tähendab vahetada iga virtuaalmasina parameeter

Lõpuks leidsid mu Ameerika kolleegid ootamatult algpõhjuse.

Jah, minu vana sülearvuti on mitu korda võimsam kui teie tootmisserver.

Võõrustajad erinesid sageduselt!

  • Reeglina pole see hirmutav. Kuid: kui liigute natiivselt hostilt erineva sagedusega hostile, peab VMware kohandama GetTimePrecise'i tulemust.
  • Reeglina pole see probleem, välja arvatud juhul, kui on mõni rakendus, mis küsib täpset aega miljoneid kordi sekundis, näiteks SQL-server.
  • Kuid see pole ka hirmutav, kuna SQL-server seda alati ei tee (vt Järeldus)

Kuid on juhtumeid, kui see reha teeb haiget. Ja jah, rattale koputades (muutes midagi VM-i sätetes), sundisin VMware'i konfiguratsiooni 'ümber arvutama' ja praeguse hosti sagedusest sai masina 'native' sagedus.

otsus

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

Kui keelate TSC virtualiseerimise, tagastab TSC virtuaalmasinast lugemine füüsilise masina TSC väärtuse ja virtuaalmasina seest TSC kirjutamine ei mõjuta. Virtuaalse masina üleviimine teise hosti, selle peatatud olekust jätkamine või hetktõmmise naasmine põhjustab TSC katkendliku hüppamise. Kui TSC virtualiseerimine on keelatud, ei õnnestu mõnel külalisoperatsioonisüsteemil alglaadimine või ilmnevad muud ajavõtuprobleemid. Varem on seda funktsiooni mõnikord soovitatud sageli TSC-d lugevate rakenduste jõudluse parandamiseks, kuid virtuaalse TSC jõudlust on praegustes toodetes oluliselt paranenud. Funktsiooni on soovitatud kasutada ka mõõtmiste tegemisel, mis nõuavad virtuaalmasinas täpset reaalaja allikat.

Lühidalt, peate parameetri lisama

monitor_control.virtual_rdtsc = VÄÄR

Järeldus

Tõenäoliselt on teil küsimus: miks peaks SQL nii sageli GetTimePrecise'i kutsuma?

Mul pole SQL-i serveri allikaid, kuid loogika ütleb seda. SQL on peaaegu kooperatiivse samaaegsusega operatsioonisüsteem, kus iga lõim peab aeg-ajalt "järgi andma". Kus on parim koht seda teha? Kus on loomulik ootus – lukk või IO. Olgu, aga mis siis, kui me keerutame arvutustsükleid? Siis on ilmselge ja peaaegu ainus koht tõlgis (see pole päris tõlk), pärast järgmise operaatori täitmist.

SQL-serverit reeglina puhtaks andmetöötluseks ei kasutata ja see pole probleem. Kuid tsüklid koos igasuguste ajutiste tabelitega (mis on kohe vahemällu salvestatud) muudavad koodi väga kiiresti täidetavate avalduste jadaks.

Muide, kui funktsioon on mähitud sisse NATIVELY COMILED, siis see lõpetab aja küsimise ja selle kiirus suureneb 10 korda. Aga kuidas on lood koostööga multitegumtööga? Ent natiivselt kompileeritud koodi jaoks pidin SQL-is tegema ennetava MULTITASKINGI.

Allikas: www.habr.com

Lisa kommentaar