Bəli, mənim köhnə noutbukum istehsal serverinizdən bir neçə dəfə güclüdür

Tərtibatçılarımızdan eşitdiyim şikayətlər məhz bunlardır. Ən maraqlısı odur ki, bunun həqiqət olduğu üzə çıxıb və uzun araşdırmalara səbəb olub. VMware üzərində işləyən SQL serverləri haqqında danışacağıq.

Bəli, mənim köhnə noutbukum istehsal serverinizdən bir neçə dəfə güclüdür

Əslində, istehsal serverinin ümidsiz şəkildə noutbukun arxasında olmasını təmin etmək asandır. Kodu icra edin (tempdb-də deyil və Gecikmiş Davamlılıq aktivləşdirilmiş verilənlər bazasında deyil):

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

Masaüstümdə 5 saniyə, istehsal serverində isə 28 saniyə çəkir. Çünki SQL əməliyyat jurnalı girişinin fiziki sonunu gözləməlidir və biz burada çox qısa əməliyyatlar edirik. Təxminən desək, biz böyük, güclü yük maşını ilə şəhər trafikinə sürdük və skuterlərdə pizza çatdıran adamların onu cəsarətlə ötməsinə baxdıq - burada ötürmə qabiliyyəti vacib deyil, yalnız gecikmə vacibdir. Heç bir şəbəkə yaddaşı, qiymətində nə qədər sıfır olsa da, gecikmə müddətinə görə yerli SSD-dən üstün ola bilməz.

(şərhlərdə yalan danışdığım üzə çıxdı - mən hər iki yerdə dayanıqlığı gecikdirmişəm. Gecikmiş qalıcılıq olmadan belə çıxır:
İş masası - 39 saniyə, 15K tr/san, 0.065ms/io gediş-gəliş
PROD - 360 saniyə, 1600 tr/san, 0.6 ms
Çox sürətli olduğunu fərq etməliydim)

Bununla belə, bu halda biz Riemann zeta funksiyasının mənasız sıfırları ilə əhəmiyyətsiz bir nümunə ilə məşğul oluruq. Tərtibatçıların mənə gətirdiyi nümunədə fərqli idi. Mən onların haqlı olduğuna əmin oldum və biznes məntiqi ilə bağlı bütün xüsusiyyətlərini nümunədən çıxarmağa başladım. Bir anda başa düşdüm ki, onların kodunu tamamilə atıb öz kodumu yaza bilərəm - bu eyni problemi nümayiş etdirir - istehsalda 3-4 dəfə yavaş işləyir:

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

Hər şey qaydasındadırsa, o zaman rəqəmin primallığının yoxlanılması 6-7-8 saniyə çəkəcək. Bu, bir sıra serverlərdə baş verib. Ancaq bəzilərində yoxlama 25-40 saniyə çəkdi. Maraqlıdır ki, icrasının məsələn, 14 saniyə çəkəcəyi serverlər yox idi - kod ya çox tez, ya da çox yavaş işləyirdi, yəni problem, deyək ki, ağ-qara idi.

Mən nə etmişəm? İstifadə olunmuş VMware ölçüləri. Orada hər şey yaxşı idi - çoxlu resurs var idi, Hazırlıq vaxtı = 0, hər şey kifayət qədər idi, həm sürətli, həm də yavaş serverlərdə sınaq zamanı bir vCPU-da CPU = 100. Pi sayını hesablamaq üçün test etdim - test istənilən serverdə eyni nəticələri göstərdi. Qara sehrin qoxusu daha da gücləndi.

DEV təsərrüfatına çatdıqdan sonra serverlərlə oynamağa başladım. Məlum oldu ki, hostdan hosta vMotion bir serveri “müalicə edə” bilər, eyni zamanda “sürətli” serveri “yavaş” serverə çevirə bilər. Deyəsən, bu belədir - bəzi aparıcıların problemi var... amma... yox. Bəzi virtual maşın hostda yavaş idi, deyək ki, A, lakin B hostunda tez işləyirdi. Başqa bir virtual maşın isə əksinə, A üzərində tez işlədi və B-də yavaşladı! Həm "sürətli", həm də "yavaş" maşınlar tez-tez ev sahibinin üzərində fırlanırdı!

O andan etibarən havada aydın kükürd qoxusu var idi. Axı, problemi virtual maşına aid etmək olmaz (məsələn, Windows yamaqları) - axırda o, vMotion ilə "sürətə" çevrildi. Ancaq problem həm də ev sahibinə aid edilə bilməzdi - axırda onun həm "sürətli", həm də "yavaş" maşınları ola bilər. Ayrıca, bu yüklə əlaqəli deyildi - mən ev sahibinə "yavaş" bir maşın ala bildim, burada heç bir şey yox idi.

Çarəsizlikdən Sysinternals-dan Process Explorer proqramını işə saldım və SQL yığınına baxdım. Yavaş maşınlarda xətt dərhal diqqətimi çəkdi:

ntoskrnl.exe!KeSynchronizeExecution+0x5bf6
ntoskrnl.exe!Birdən çox obyekt+0x109d üçün gözləyin
ntoskrnl.exe!MultipleObjects+0xb3f üçün gözləyin
ntoskrnl.exe!KeWaitForSingleObject+0x377
ntoskrnl.exe!KeQuerySystemTimePrecise+0x881 < — !!!
ntoskrnl.exe!ObDereferenceObjectDeferDelete+0x28a
ntoskrnl.exe!KeSynchronizeExecution+0x2de2
sqllang.dll!CDiagThreadSafe::PxlvlReplace+0x1a20
... atladı
sqldk.dll!SystemThread::MakeMiniSOSTthread+0xa54
KERNEL32.DLL!BaseThreadInitThunk+0x14
ntdll.dll!RtlUserThreadStart+0x21

Bu artıq bir şey idi. Proqram yazılıb:

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

Bu proqram daha da nəzərə çarpan bir yavaşlama nümayiş etdirdi - "sürətli" maşınlarda saniyədə 16-18 milyon dövrə göstərir, yavaş maşınlarda isə bir milyon yarım, hətta 700 min göstərir. Yəni fərq 10-20 dəfədir (!!!). Bu, artıq kiçik bir qələbə idi: hər halda, Microsoft və VMware dəstəyi arasında ilişib qalmaq təhlükəsi yox idi ki, onlar oxları bir-birinə çevirsinlər.

Sonra tərəqqi dayandı - tətillər, vacib məsələlər, viral isteriya və iş yükünün kəskin artması. Mən tez-tez həmkarlarıma sehrli problemdən danışırdım, lakin bəzən mənə elə gəlirdi ki, onlar hətta həmişə mənə inanmırlar - VMware-nin kodu 10-20 dəfə yavaşlatması barədə bəyanat çox dəhşətli idi.

Məni yavaşlatan şeyi özümü qazmağa çalışdım. Bəzən mənə elə gəlirdi ki, mən bir həll tapmışam - İsti prizləri yandırıb-söndürmək, yaddaşın miqdarını və ya prosessorların sayını dəyişdirmək tez-tez maşını "sürətli"yə çevirir. Amma əbədi deyil. Amma doğru olan odur ki, çıxıb sükanı döymək kifayətdir - yəni dəyişmək hər hansı bir virtual maşın parametri

Nəhayət, amerikalı həmkarlarım birdən-birə kök səbəbini tapdılar.

Bəli, mənim köhnə noutbukum istehsal serverinizdən bir neçə dəfə güclüdür

Ev sahibləri tezliyə görə fərqləndilər!

  • Bir qayda olaraq, bu böyük bir şey deyil. Lakin: “doğma” hostdan “fərqli” tezlikli hosta keçərkən, VMware GetTimePrecise nəticəsini tənzimləməlidir.
  • Bir qayda olaraq, SQL server kimi saniyədə milyonlarla dəfə dəqiq vaxtı tələb edən proqram yoxdursa, bu problem deyil.
  • Ancaq bu qorxulu deyil, çünki SQL server həmişə bunu etmir (Nəticə bax)

Amma elə hallar olur ki, bu dırmıq bərk vurur. Və yenə də, bəli, təkərə toxunaraq (VM parametrlərində nəyisə dəyişdirməklə) mən VMware-ni konfiqurasiyanı “yenidən hesablamağa” məcbur etdim və cari hostun tezliyi maşının “doğma” tezliyinə çevrildi.

qərar

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

TSC-nin virtuallaşdırılmasını söndürdüyünüz zaman, virtual maşın daxilində TSC-nin oxunması fiziki maşının TSC dəyərini qaytarır və TSC-nin virtual maşın daxilində yazılması heç bir təsir göstərmir. Virtual maşının başqa hosta köçürülməsi, onu dayandırılmış vəziyyətdən bərpa etmək və ya snapshota qayıtmaq TSC-nin fasiləsiz atlamasına səbəb olur. Bəzi qonaq əməliyyat sistemləri TSC virtualizasiyası söndürüldükdə yüklənə bilmir və ya digər vaxt hesablama problemlərini nümayiş etdirir. Keçmişdə bu xüsusiyyət bəzən TSC-ni tez-tez oxuyan proqramların işini yaxşılaşdırmaq üçün tövsiyə olunurdu, lakin virtual TSC-nin performansı cari məhsullarda əhəmiyyətli dərəcədə yaxşılaşdırılıb. Xüsusiyyətdən virtual maşında real vaxtın dəqiq mənbəyini tələb edən ölçmələri yerinə yetirərkən istifadə üçün də tövsiyə edilmişdir.

Bir sözlə, parametr əlavə etmək lazımdır

monitor_control.virtual_rdtsc = FALSE

Nəticə

Yəqin ki, bir sualınız var: niyə SQL GetTimePrecise-i tez-tez çağırır?

SQL server mənbə kodum yoxdur, amma məntiq bunu deyir. SQL, demək olar ki, kooperativ paralellikli əməliyyat sistemidir, burada hər bir ip vaxtaşırı “təslim olmalıdır”. Bunu etmək üçün ən yaxşı yer haradadır? Təbii gözləmənin olduğu yerdə - kilid və ya IO. Yaxşı, amma hesablama döngələrini fırlasaq nə etməli? Sonra aydın və demək olar ki, yeganə yer tərcüməçidədir (bu, həqiqətən tərcüməçi deyil), növbəti ifadəni yerinə yetirdikdən sonra.

Ümumiyyətlə, SQL server təmiz hesablama dırnaqları üçün istifadə edilmir və bu problem deyil. Lakin bütün növ müvəqqəti cədvəllərlə işləyən döngələr (onlar dərhal keşlənir) kodu çox tez yerinə yetirilən ifadələr ardıcıllığına çevirir.

Yeri gəlmişkən, əgər siz NATIVELY COMPILED funksiyasını büksəniz, o zaman vaxt istəməyi dayandırır və sürəti 10 dəfə artır.Bəs kooperativ multitasking? Ancaq yerli olaraq tərtib edilmiş kod üçün biz SQL-də PREMPTIV MULTITASKING etməli olduq.

Mənbə: www.habr.com

Добавить комментарий