Jah, minu vana sülearvuti on mitmeid kordi võimsam kui teie tootmisteenus

Just such complaints I heard from our developers. The most interesting thing is that it turned out to be true, leading to a lengthy investigation. We will talk about SQL servers running on VMware.

Jah, minu vana sülearvuti on mitmeid kordi võimsam kui teie tootmisteenus

In fact, it is easy to make the production server hopelessly lag behind the laptop. Execute (not on tempdb and not on the database with Delayed Durability) the code:

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

On my desktop, it runs in 5 seconds, while on the production server — 28 seconds. This is because SQL has to wait for the physical completion of writing to the transaction log, and we are making very short transactions here. Roughly speaking, we have driven a big powerful truck into city traffic, and we see how pizza delivery guys on scooters easily overtake it — here throughput doesn't matter, only latency does. And no network storage, no matter how many zeros are in its price, can win on latency against a local SSD.

(it turned out in the comments that I lied — I have delayed durability captured in both places. Without delayed durability, it results in:
Desktop — 39 seconds, 15K tr/sec, 0.065ms /io roundtrip
PROD — 360 seconds, 1600 tr/sec, 0.6ms
I should have noticed that it was way too fast)

However, in this case, we are dealing with trivial zeros of the Riemann zeta function with a trivial example. In the example brought to me by the developers, it was different. I made sure that they were right and began to clear the example of all their specifics related to business logic. At some point, I realized that I could completely discard their code and write my own — which demonstrates the same problem — it runs 3-4 times slower in production:

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

If everything is fine with you, then checking the primality of a number will take 6-7-8 seconds. It was like that in a number of cases serverid. But in some cases, the check took 25-40 seconds. Interestingly, there were no servers where the execution took, say, 14 seconds — the code either ran very fast or very slowly, so the problem was, let's say, black and white.

Mida ma tegin? Vaatasin VMware statistikat. Kõik oli hästi – ressursse oli rohkem kui piisavalt, Ready time = 0, kõike jagus, testimise ajal oli nii kiiretel kui ka aeglastel serveritel CPU=100 ühel vCPU-l. Võtsin testi π arvestamiseks – test näitas samu tulemusi igasugustes serverites. Kujunes välja, et asi muutub mustaks maagiseks.

DEV farmast välja pääsedes hakkasin serveritega katsetama. Selgus, et vMotion hostilt hostile võib "ravida" serverit, kuid samas võib ka "kiire" serveri muuta "aeglaseks". Tundub, et siin on probleem — mõned hostid on probleemsed... aga... ei. Mõni virtuaalmasin takerdus hostil, ütleme, A, kuid töötas kiiresti hostil B. Teine virtuaalmasin seevastu töötas kiiresti A-l ja takerdas B-l! Hostil tihti töötasid nii "kiired" kui ka "aeglased" masinad!

Sellest hetkest õhus hakati selgesti tunnetama väävlilõhna. Probleem ei saanud olla seotud ühegi virtuaalmasinaga (näiteks Windowsi plaastritega) — ta muutus "kiireks" vMotioni käigus. Kuid probleem ei saanud olla ka hostiga seotud — see võiks hõlmata nagu "kiireid", nii ka "aeglasi" masinaid. Samuti ei olnud see seotud koormusega — mul õnnestus saada "aeglane" masin hostil, kus ei olnud mitte midagi muud.

Meeleheite sunnil käivitasin Sysinternals'i Process Explorer'i ja vaatasin SQL steki. Aeglastel masinatel tõmbas mulle 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  0)
                {
                    Console.WriteLine("{0}", counter);
                }
            }
        }
    }

See programm demonstreeris veelgi suuremat aeglustumist — "kiiretel" masinatel näitab see 16-18 miljonit tsüklit sekundis, samas kui aeglastel — poolteist miljonit või isegi 700 tuhat. Erinevus on seega 10-20 korda (!!!). See oli juba väike võit: igal juhul ei olnud ohtu jääda Microsofti ja VMware toe vahele, et nad üksteise süüd üle kandsid.

Edasi liikumine peatus — puhkus, olulised asjad, viirusine hüsteeria ja koormuse järsk suurenemine. Ma mainisin sageli maagilist probleemi kolleegidele, kuid mõnikord tundus, et nad ei usu mind — liiga koletuks oli välja öelda, et VMware aeglustab koodi 10-20 korda.

Ma üritasin ise välja selgitada, mis siis aeglustab. Mõnikord tundus, et leidsin lahenduse — Hot plugide sisse ja välja lülitamine, mälu mahu muutmine või protsessorite arvu muutmine muudavad masina sageli "kiireks". Kuid mitte igavesti. Küll aga selgus tõe kohta — et piisab, kui välja minna ja ratast koputada — st muuta (mõelgem kõrgesse ametisse olevatele inimestele), siis on see halb virtuaalmasina parameetrit

Lõpuks leidsid mu Ameerika kolleegid äkki põhjuse.

Jah, minu vana sülearvuti on mitmeid kordi võimsam kui teie tootmisteenus

Hostid erinevad sageduse poolest!

  • Reeglina pole see hirmus. Kuid: kui kolida ‘kodusest’ hostist ‘teise’ sagedusega hosti, peab VMware kohandama GetTimePrecise tulemusi.
  • Reeglina pole see hirmus, kui ei osutu olevat rakendust, mis küsib täpset aega miljoneid kordi sekundis, nagu SQL server.
  • Kuid ka see ei ole hirmus, sest SQL server ei tee seda kaugel alati (vt Kokkuvõte)

Kuid on juhtumeid, kus need takistused löövad valusalt. Ja tõepoolest, ratast koputades (muutes VM-i seadeid) panin ma VMware 'uuesti arvutama' konfigureerimise, ja praeguse hosti sagedus muutus 'koduseks' sageduseks.

Lahendus

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

Kui keelad TSC virtualiseerimise, siis TSC lugemine virtuaalmasinast tagastab füüsilise masina TSC väärtuse, ja TSC kirjutamine virtuaalmasinast ei avalda mõju. Virtuaalmasina teisaldamine teisele hostile, seisundist taastamine või snapshotile tagasipöördumine põhjustab TSC järsku hüpet. Mõned külalisoperatsioonisüsteemid ei käivitu või näitavad muid ajatsemise probleeme, kui TSC virtualiseerimine on keelatud. Minevikus on seda funktsiooni mõnikord soovitatud rakenduste jõudluse parandamiseks, mis loevad TSC-d sageli., kuid virtuaalse TSC jõudlust on praegustes toodetes märksa parandatud. Funktsiooni on samuti soovitatud kasutada, kui tehakse mõõtmisi, mis vajavad virtuaalmasinas täpset reaalaja allikat.

Lühiülevaates tuleks lisada parameeter

monitor_control.virtual_rdtsc = FALSE

Kokkuvõte

Te tõenäoliselt küsid, miks SQL-i tuleb kutsuda GetTimePrecise nii sageli?

Mul ei ole SQL serveri lähtekoodide, kuid loogika ütleb järgmist. SQL on peaaegu operatsioonisüsteem kooperatiivse konkurentsiga, kus iga teema peab aeg-ajalt "loovutama". Ja kus oleks parem seda teha? Seal, kus on loomulik ooteaeg — lukustus või I/O. Hästi, aga mis siis, kui me keerame arvutustsükleid? Siis on ilmselge ja peaaegu ainus koht — tõlgendis (see ei ole päris tõlgendi), pärast järgmise käsu täitmist.

Reeglina ei kasutata SQL serverit puhta arvutustöö jaoks ja see ei ole probleem. Kuid tsüklid, millega töötatakse igasuguste ajutiste tabelitega (mis kohe püsivalt vahemälus), muudavad koodi väga kiiresti täidetavate käsu järjestuseks.

Muide, kui funktsioon mähkida NATIVELY COMPILED, siis lõpetab see aja pärimise ja selle kiirus suureneb kümme korda. Aga kuidas on kooperatiivse mitmeülesandelisusega? Noh, just seetõttu tuli SQL-i luua NATIVE COMPILED koodi jaoks PREEMPTIVE MULTITASKING.

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster