Selliseid vÀiteid kuulsin meie arendajatelt. KÔige huvitavam on see, et see osutus tÔeks, alustades pikaajalist uurimist. Jutt kÀib SQL serveritest, mis jooksutavad meie VMware'is.

SeetĂ”ttu on kerge saavutada, et tootmisserver jÀÀks sĂŒlearvutile lootusetult alla. KĂ€ivitage (mitte tempdb-s ja mitte andmebaasis, kus on aktiveeritud Delayed Durability) jĂ€rgnev kood:
set nocount on
create table _t (v varchar(100))
declare @n int=300000
while @n>0 begin
insert into _t select 'Milline aeglane!'
delete from _t
set @n=@n-1
end
GO
drop table _t
Minu töölaual vĂ”tab see aega 5 sekundit, tootmisserveris â 28 sekundit. Sest SQL peab ootama tehingulogis kirjutamise fĂŒĂŒsilise lĂ”petamise pĂ€rast, ja me teeme siin vĂ€ga lĂŒhikesi tehinguid. Ătleme nii, et me surusime suure vĂ”imsa kaubiku linna liiklusesse, ja vaatame, kuidas pizza kullerid oma mootorratastel meid kergelt mööda sĂ”idavad â siin ei loe lĂ€bivus, vaid ainult latentsus. Ăkski vĂ”rgu mĂ€luseade, kui palju nulli ka ei oleks selle hinnas, ei suuda saavutada madalamat latentsust kohaliku SSD ees.
(kommentaarides selgus, et ma valetasime â mul on mĂ”lemas kohas viibinud delayed durability. Ilma delayed durability'ta on tulemused:
Töölaua puhul â 39 sekundit, 15K tr/sec, 0,065 ms /io ĂŒmmargune reisi aeg.
PROD â 360 sekundit, 1600 tr/sec, 0,6 ms
Pidin tÀhele panna, et see on liiga kiire)
Siinkohal on meil tegemist Riemanni funktsiooni triviaalsete nullidega triviaalse nĂ€itega. NĂ€ites, mille mulle arendajad tĂ”id, oli midagi muud. Ma veendusin, et nad on Ă”iged, ja hakkasin nĂ€itest eemaldama nende Ă€ri loogikaga seotud spetsiifikat. Mingil hetkel sain aru, et vĂ”in tĂ€ielikult visata nende koodi Ă€ra ja kirjutada oma â mis demonstreerib sama probleemi â productionis 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 -- kontrolli paarituid numbreid kuni ruudust
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
GOKui teil on kĂ”ik hĂ€sti, siis arvutatakse arvu primitiivsuse kontroll 6â7â8 sekundi jooksul. Nii oli mitmel puhul. serverite. Kuid mĂ”nikord vĂ”ttis kontroll 25â40 sekundit. Mis on huvitav, ei olnud servereid, kus tĂ€itmine oleks kestnud nĂ€iteks 14 sekundit â kood töötas kas vĂ€ga kiiresti vĂ”i vĂ€ga aeglaselt, ehkki probleem oli, ĂŒtleme nii, must-valge.
Mida ma tegin? Vaatasin VMware'i mÔÔdikuid. KĂ”ik oli korras â ressurssi oli ĂŒle, Ready time = 0, kĂ”ike jagus, testimise ajal olid nii kiiretel kui ka aeglastel serveritel CPU=100 ĂŒhel vCPU-l. Tehtud test Pi arvestamiseks â test nĂ€itas samasid tulemusi igas serveris. Must maa muutus jĂ€rjest selgemaks.
DEV farmist vĂ€lja pÀÀsedes hakkasin serveritega katsetama. Selgus, et vMotion hostilt hostile vĂ”ib serverit 'ravida', aga vĂ”ib ka vastupidi, 'kiire' server muuta 'aeglaseks'. Tundub, et probleem on olemas â mĂ”nel hostil on probleem... aga... ei. Ăks virtuaalmasin pidurdas hostis, ĂŒtleme, A, aga töötas kiiresti hostis B. Teine virtuaalmasin vastupidi, töötas kiiresti A-s ja pidurdas B-s! Hostis liiklesid tihti nii 'kiired' kui ka 'aeglased' masinad!
Sellest hetkest alates oli Ă”hus selgelt vÀÀvli lĂ”hna. Probleemi ei saanud seostada virtuaalmasinaga (nĂ€iteks Windowsi uuendustega) â see muutus ju vMotionil "kiireks". Kuid probleem ei saanud olla seotud ka hostiga â sellel vĂ”is olla nii "kiireid" kui "aeglaseid" masinaid. Samuti ei olnud see koormusega seotud â mul Ă”nnestus saada "aeglane" masin hostil, kus peale selle ei olnud ĂŒldse midagi.
HÀda tÔttu kÀivitasin Sysinternalsi Process Exploreri ja vaatasin SQL-i stacki. Aeglaste masinate puhul jÀi 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
⊠vahelejÀtetud
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 demonstreerib veelgi tugevamat aeglust â "kiiretel" masinatel nĂ€itab see 16-18 miljonit tsĂŒklit sekundis, samas kui aeglastel â ĂŒhe ja poole miljoni vĂ”i isegi 700 000. See tĂ€hendab, et vahe on 10-20 korda (!!!). See oli juba vĂ€ike vĂ”it: igal juhul ei olnud ohtu takerduda Microsofti ja VMware toe vahele, et nad suunaksid ĂŒksteisele sĂŒĂŒdistusi.
Edasi liikumine peatus â puhkus, tĂ€htsad asjad, viirushaigus ja koormuse jĂ€rsk tĂ”us. Olen tihti maininud kolleegidele maagilist probleemi, kuid vahel tundus, et nad ei usu mind alati â liiga uskumatud olid vĂ€ited, et VMware aeglustab koodi 10-20 korda.
PĂŒĂŒdsin ise vĂ€lja uurida, mis takistab. Vahel tundus, et leidsin lahenduse â kuumplugide sisse ja vĂ€lja lĂŒlitamine, muutmine mĂ€lu mahtu vĂ”i protsessorite arvu tihti muutis masina "kiireks". Kuid mitte igavesti. Siiski selgus, et piisab, kui lihtsalt vĂ€lja minna ja ratast koputada â st muuta igaĂŒhe virtuaalmasina parameetrit
LÔpuks leidsid mu Ameerika kolleegid root cause'i.

Hostid erinevad sageduse poolest!
- Reeglina pole see hirmus. Kuid: ĂŒleminekul âomaâ hostilt âteiseâ sagedusega hostile peab VMware korrigeerima GetTimePrecise'i tulemusi.
- Reeglina pole see hirmus, kui ei satu rakendust, mis kĂŒsib tĂ€pset aega miljon korda sekundis, nagu SQL server.
- Aga isegi see pole hirmus, kuna SQL server ei tee seda sugugi alati (vt KokkuvÔte)
Aga on juhtumeid, kui need lĂ”ksud tĂ”eliselt haiget teevad. Jah, kui koputasin ratastele (vahetades midagi VM seadetest), sundisin ma VMware 'ĂŒlekalkuleerima' konfiguratsiooni ja aktiivse hosti sagedus muutus 'originaalseks' sageduseks masina jaoks.
Lahendus
Kui te keelate TSC virtualiseerimise, tagastab TSC lugemine virtuaalmasinast fĂŒĂŒsilise masina TSC vÀÀrtuse ning TSC kirjutamine virtuaalmasinast ei pĂ”hjusta mingit mĂ”ju. Virtuaalmasina teisaldamine teisele hostile, selle jĂ€rsu seiskamise taastamine vĂ”i hetkeseisule tagasipöördumine pĂ”hjustab TSC rikkumist. MĂ”ned kĂŒlalisoperatsioonisĂŒsteemid ei kĂ€ivitu vĂ”i nĂ€itavad muid ajahoidmise 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Ă€rkimisvÀÀrselt parandatud. Funktsiooni on soovitatud kasutada ka mÔÔtmiste tegemisel, mis vajavad virtuaalmasinas tĂ€pset reaalaega allikat.
LĂŒhidalt öeldes, tuleb lisada parameeter
monitor_control.virtual_rdtsc = FALSE
KokkuvÔte
Te kindlasti kĂŒsite: miks SQL kutsub GetTimePrecise nii sageli?
Mul ei ole SQL serveri lĂ€htekoodid, kuid loogika ĂŒtleb jĂ€rgmist. SQL on peaaegu operatsioonisĂŒsteem kooperatiivse konkurentsiga, kus iga lĂ”ime peab aeg-ajalt "loomulikult" ootama. Aga kus oleks parem seda teha? Seal, kus on loomulik ooteaeg â lukud vĂ”i IO. HĂ€sti, aga mis siis, kui me keerame arvutuslikke tsĂŒkleid? Siis on ilmne ja peaaegu ainus koht â tĂ”lgendajas (see ei ole pĂ€ris tĂ”lgendaja), pĂ€rast jĂ€rgmise kĂ€su tĂ€itmist.
Reeglina ei kasutata SQL serverit puhaste arvutuste tegemiseks, ning see ei ole probleem. Kuid tsĂŒklid ajutiste tabelitega (mis kohe vahemĂ€lustesse salvestatakse) muudavad koodi vĂ€ga kiiresti tĂ€idetavate kĂ€skude jĂ€rjestuseks.
Muide, kui funktsiooni ĂŒmbritseda NATIVELY COMPILED, siis see lĂ”petab ajakĂŒsimise, ja selle kiirus suureneb kĂŒmme korda. Aga kuidas on kooperatiivse multitaskinguga? Noh, just natively compiled koodi jaoks tuli SQL-s teha PREEMPTIVE MULTITASKING.
Allikas: habr.com
