Да, мојот стар лаптоп е неколку пати помоќен од вашиот производствен сервер.

Ова се токму поплаките што ги слушнав од нашите програмери. Најинтересно е што тоа се покажа како точно, што доведе до долга истрага. Ќе зборуваме за SQL сервери кои работат на VMware.

Да, мојот стар лаптоп е неколку пати помоќен од вашиот производствен сервер.

Всушност, лесно е да се осигура дека производниот сервер е безнадежно зад лаптопот. Извршете (не на tempdb и не на база на податоци со овозможена одложена издржливост) кодот:

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

На мојот десктоп тоа трае 5 секунди, а на серверот за производство трае 28 секунди. Бидејќи SQL мора да го чека физичкиот крај на записот во дневникот на трансакциите, а ние правиме многу кратки трансакции овде. Грубо кажано, влеговме голем, моќен камион во градскиот сообраќај и гледавме како брзо го претекнуваат луѓе кои доставуваат пица на скутери - тука не е важна пропусната моќ, важна е само доцнењето. И ниту едно мрежно складирање, без разлика колку нули има во неговата цена, не може да го победи локалниот SSD во однос на латентноста.

(во коментарите испадна дека лажам - имав одложена издржливост на двете места. Без одложена издржливост излегува:
Десктоп - 39 секунди, 15K tr/sec, 0.065ms /io повратен пат
PROD - 360 секунди, 1600 tr/sec, 0.6ms
Требаше да забележам дека беше пребрзо)

Меѓутоа, во овој случај се работи со тривијални нули на Римановата зета функција со тривијален пример. Во примерот што ми го донесоа програмерите беше поинаку. Бев убеден дека тие се во право и почнав да ги отстранувам од примерот сите нивни специфики поврзани со деловната логика. Во одреден момент сфатив дека можам целосно да го фрлам нивниот код и да напишам свој - што го покажува истиот проблем - во производството работи 3-4 пати побавно:

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

Ако сè е во ред, тогаш проверката на примарноста на бројот ќе трае 6-7-8 секунди. Ова се случи на голем број сервери. Но, на некои, проверката траеше 25-40 секунди. Интересно, немаше сервери каде што извршувањето ќе трае, да речеме, 14 секунди - кодот работеше или многу брзо или многу бавно, односно проблемот беше, да речеме, црно-бело.

Што направив? Користена метрика на VMware. Сè беше во ред таму - имаше изобилство ресурси, Време на подготвеност = 0, имаше доволно од сè, за време на тестот и на брзите и на бавните сервери CPU = 100 на еден vCPU. Направив тест за да го пресметам бројот Pi - тестот ги покажа истите резултати на кој било сервер. Мирисот на црна магија стануваше се посилен и посилен.

Откако стигнав до фармата DEV, почнав да играм со серверите. Се испостави дека vMotion од домаќин до хост може да „лечи“ сервер, но исто така може да претвори „брз“ сервер во „бавен“. Изгледа ова е тоа - некои домаќини имаат проблем... но... не. Некоја виртуелна машина беше бавна на домаќинот, да речеме А, но работеше брзо на домаќинот Б. А друга виртуелна машина, напротив, работеше брзо на А и забави на Б! И „брзите“ и „бавните“ машини често се вртеа врз домаќинот!

Од тој момент, во воздухот се чувствуваше изразен мирис на сулфур. На крајот на краиштата, проблемот не можеше да се припише на виртуелната машина (закрпи на Windows, на пример) - на крајот на краиштата, тој се претвори во „брз“ со vMotion. Но, проблемот, исто така, не може да се припише на домаќинот - на крајот на краиштата, може да има и „брзи“ и „бавни“ машини. Исто така, ова не беше поврзано со товарот - успеав да добијам „бавна“ машина на домаќинот, каде што немаше ништо освен тоа.

Од очај, го лансирав Process Explorer од Sysinternals и погледнав во стекот SQL. На бавните машини линијата веднаш ми привлече внимание:

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
... прескокна
sqldk.dll!SystemThread::MakeMiniSOSThread+0xa54
KERNEL32.DLL!BaseThreadInitThunk+0x14
ntdll.dll!RtlUserThreadStart+0x21

Ова веќе беше нешто. Програмата беше напишана:

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

Оваа програма покажа уште поизразено забавување - на „брзите“ машини покажува 16-18 милиони циклуси во секунда, додека на бавните машини покажува милион и пол, па дури и 700 илјади. Тоа е, разликата е 10-20 пати (!!!). Ова веќе беше мала победа: во секој случај, немаше закана да се заглавиме помеѓу поддршката на Microsoft и VMware, така што тие ќе вртат стрели еден кон друг.

Потоа запре напредокот - одмори, важни работи, вирусна хистерија и нагло зголемување на обемот на работа. Честопати им го спомнував магичниот проблем на моите колеги, но на моменти се чинеше дека тие дури и не ми веруваат секогаш - изјавата дека VMware го успорува кодот за 10-20 пати беше премногу монструозна.

Се обидов да си го откопам она што ме кочи. На моменти ми се чинеше дека најдов решение - вклучувањето и исклучувањето на Hot приклучоците, менувањето на количината на меморија или бројот на процесори често ја претвораа машината во „брза“. Но, не засекогаш. Но, она што се покажа како точно е дека доволно е да се излезе и да се затропа на тркалото - односно да се смени било параметар на виртуелна машина

Конечно, моите американски колеги одеднаш ја најдоа основната причина.

Да, мојот стар лаптоп е неколку пати помоќен од вашиот производствен сервер.

Домаќините се разликуваа по фреквенција!

  • Како по правило, ова не е голема работа. Но: кога се движите од „матичен“ хост во домаќин со „различна“ фреквенција, VMware мора да го прилагоди резултатот GetTimePrecise.
  • Како по правило, ова не е проблем, освен ако не постои апликација која го бара точното време милиони пати во секунда, како SQL серверот.
  • Но, ова не е страшно, бидејќи SQL-серверот не секогаш го прави ова (види Заклучок)

Но, има случаи кога ова гребло силно удира. А сепак, да, со допирање на тркалото (со менување на нешто во поставките за VM) го принудив VMware да ја „рекалкулира“ конфигурацијата, а фреквенцијата на тековниот хост стана „матична“ фреквенција на машината.

Решение

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

Кога ја оневозможувате виртуелизацијата на TSC, читањето на TSC од внатрешноста на виртуелната машина ја враќа вредноста на TSC на физичката машина, а пишувањето на TSC од внатрешноста на виртуелната машина нема ефект. Мигрирањето на виртуелната машина на друг хост, неговото продолжување од суспендирана состојба или враќање на слика предизвикува TSC да скока неконтинуирано. Некои гостински оперативни системи не успеваат да се подигнат или покажуваат други проблеми со мерењето на времето кога виртуелизацијата на TSC е оневозможена. Во минатото, оваа функција понекогаш се препорачуваше за подобрување на перформансите на апликациите кои често го читаат TSC, но перформансите на виртуелниот TSC се значително подобрени во сегашните производи. Функцијата е препорачана и за употреба при вршење мерења за кои е потребен прецизен извор на реално време во виртуелната машина.

Накратко, треба да го додадете параметарот

monitor_control.virtual_rdtsc = НЕТОЧНО

Заклучок

Веројатно имате прашање: зошто SQL толку често повикува GetTimePrecise?

Го немам изворниот код на SQL серверот, но логиката го вели ова. SQL е речиси оперативен систем со кооперативна истовременост, каде што секоја нишка мора да „попушти“ од време на време. Каде е најдоброто место за ова? Каде што има природно чекање - заклучување или IO. Добро, но што ако вртиме пресметковни јамки? Тогаш очигледното и речиси единственото место е во толкувачот (ова всушност не е толкувач), по извршувањето на следната изјава.

Општо земено, SQL-серверот не се користи за чисто пресметување приковување и тоа не е проблем. Но, циклусите кои работат со секакви привремени табели (кои веднаш се кешираат) го претвораат кодот во низа од многу брзо извршени изјави.

Патем, ако ја завиткате функцијата во NATIVELY COMPILED, тогаш таа престанува да бара време, а брзината и се зголемува за 10 пати. Што е со кооперативното мултитаскинг? Но, за природно компајлиран код, моравме да направиме ПРЕДНОСНО МУЛТИТАСКИРАЊЕ во SQL.

Извор: www.habr.com

Додадете коментар