1.1 Shardlama nədir?
Əgər Google-da təkidlə axtarsaz, görəcəksiniz ki, belə deyək, partitioning və shardlama arasındakı sərhəd kifayət qədər qeyri-müəyyəndir. Hər kəs istədiyini istədiyi kimi adlandırır. Bəzi insanlar horizontal partitioning və shardlamanı fərqləndirir. Digərləri deyirlər ki, shardlama horizontal partitioningin xüsusi bir növüdür.
Bu terminologiya ilə razılaşan və ISO tərəfindən təsdiqlənmiş vahid standart yoxdur. Şəxsi daxili inancım aşağıdakıdır: Partitioning ümumiyyətlə - bu "bazani parçalamaq" təsadüfi bir şəkildə edir.
- Vertical partitioning — sütunlarla. Məsələn, 2 milyard qeyd ilə 60 sütundan ibarət böyük bir cədvəl var. Belə böyük bir cədvəli saxlamaq əvəzinə, 2 milyard qeyd olan 60 böyük cədvəl saxlayırıq - bu sütunlarla baza deyil, vertikal partitioningdir (terminologiya nümunəsi olaraq).
- Horizontal partitioning — sətirlə kəsmək, ola bilər ki, server daxilində.
Burada horizontal partitioning və shardlama arasındakı incə fərqdə bir az çaşqınlıq var. Məni parçalamayın, amma əminliklə deyə bilmərəm bu fərqə nə demək olar. Belə bir hiss var ki, shardlama və horizontal partitioning təxminən eyni şeydir.
Shardlama — ümumiyyətlə, böyük bir cədvəl bazalar və ya sənəd kolleksiyası, obyektlər termini ilə deyilsə, document store istifadə edirsinizsə, obyektlərə görə kəsilir. Yəni 2 milyard obyektin içindəki parçalar seçilir, ölçünün əhəmiyyəti yoxdur. Obyektin hər bir obyekt içində özünü parçalamırıq, ayrı-ayrı sütunlara bölmürük, ancaq paketlərlə fərqli yerlərə yerləşdiririk.
Sonra artıq terminologiyanın incə fərqləri yaranır. Məsələn, şərti olaraq Postgres proqramçıları deyə bilər ki, horizontal partitioning əsas cədvəlin bölündüyü bütün cədvəllərin eyni sxemada olduğu hallarda aparılır, müxtəlif maşınlarda olduğu vaxt isə shardlama olur.
Ümumiyyətlə, konkret bir verilənlər bazası və konkret bir məlumat idarəetmə sistemi terminologiyası ilə əlaqəli olmadan, belə bir hiss yaranır ki, shardlama sadəcə olaraq sətir/sənədcə kəsilməkdən ibarətdir və s. — vəssalam.
Əsas vurğulanacaq məqam budur ki, 2 milyard sənədi 20 cədvələ bölüb, idarə etmək daha asan olacaqdır, lakin məqsədimiz bu sənədləri çox nüvələrə, çox disklərə və ya çox müxtəlif fiziki və ya virtual serverlərə bölüşdürməkdir.

1.2 Bölünməz olanı bölürük
Məqsədimiz hər shardı — hər bir məlumat parçasını — çox sayda təkrarlamaqdır. Amma əslində, belə deyil.
INSERT INTO docs00
SELECT * FROM documents WHERE (id%16)=0
...
INSERT INTO docs15
SELECT * FROM documents WHERE (id%16)=15
Əslində, əgər siz belə bir məlumat parçalama edirsinizsə, və bir nəhəng SQL cədvəli MySQL-də cəsur laptopunuzda əmələ gətirib, 16 kiçik cədvəl ilə çıxmazdan əvvəl, bir laptopun, bir sxemanın, bir verilənlər bazasının hüdudlarını tərk etmirsiniz, o zaman şərtsiz shardlama edirsiniz.
Bu aşağıdakı nəticələrə gətirir:
- Bandwidth artır — keçırıcılıq qabiliyyəti.
- Latency dəyişmir, yəni hər bir işçi ya da consumer bu halda özünü alır. Müxtəlif sorğular təxminən eyni zamanda yerinə yetirilir.
- Həmçinin, hər ikisi, həm də yüksək mövcudluq (təkrarlama).
Bandwidth nə üçün lazımdır? Bəzən elə bir məlumat həcmi ola bilər ki, sığmır - haraya aydın deyil, amma sığmır - 1 {nüvə | disk | server | ...}. Sadəcə olaraq resurs çatışmır və hər şey. Bu böyük datanı işləmək üçün onu bölməliyik.
Latency nə üçün lazımdır? Tək bir nüvədə 2 milyard sətirli cədvəli skan etmək 20 dəfə yavaşdır, 20 nüvədə 20 cədvəli paralel skan edərkən. Məlumatlar bir resursda çox yavaş işlənir.
Yüksək mövcudluq nə üçün lazımdır? Ya da məlumatları kəsirik ki, həm birini, həm də digərini eyni zamanda edə bilək və eyni zamanda hər bir shardın bir neçə nüsxəsini təkrarlama yüksək mövcudluğu təmin edir.
1.3 Əllə necə etmək olar
Şərti shardlama test cədvəli test.documents-də 32 sənədlər vasitəsilə həyata keçirilə bilər və bu cədvəldən təxminən 2 sənəd üçün 16 test cədvəli yaradıla bilər: test.docs00, 01, 02, ..., 15.
INSERT INTO docs00
SELECT * FROM documents WHERE (id%16)=0
...
INSERT INTO docs15
SELECT * FROM documents WHERE (id%16)=15
Niyə təxminən? Çünki əvvəlcədən id-lərin necə bölündüyünü bilmirik, əgər 1-dən 32-yə qədərdirsə, bu təxminən 2 sənəd olacaq, əks halda — yox.
Bunu niyə edirik. 16 cədvəl yaratdıqdan sonra, 16 sınıfı "yemək" edə bilərik və lazım olan resurslarla paralel işləyə bilərik. Məsələn, diskin tutumu çatmırsa, bu cədvəlləri ayrı disklərə bölmək mantiqidir.
Hər şey təəssüf ki, pulsuz deyil. Şübhələnirəm ki, kanonik SQL standartı (çoxdan SQL standartını oxumamışam, bəlkə də çoxdan yenilənmədi), hər hansı SQL serverinə "Əziz SQL server, 32 shard et və onları 4 diskə böl" demək üçün rəsmi bir standartlaşdırılmış sintaksis yoxdur. Amma ayrı-ayrı reallaşdırmalarda bu prinsiplə eyni şeyləri etmək üçün xüsusi sintaksis var. PostgreSQL-də partitioning mexanizmləri var, MySQL MariaDB-də var, Oracle yəqin bunu çoxdan etdi.
Bununla yanaşı, əgər bunu əllə, verilənlər bazasının dəstəyi olmadan və standart çərçivəsində edirik, o zaman şərti olaraq verilənlərə girişin mürəkkəbliyinə görə pul ödəyirik. Harada ki, sadə SELECT * FROM documents WHERE id=123, indi 16 x SELECT * FROM docsXX var. Və yaxşı, biz qeydi açardan əldə etməyə çalışırıqsa. Əhəmiyyətli dərəcədə maraqlıdır ki, erkən bir qeydlər aralığını almağa çalışırıqsa, bu halda (əgər standart çərçivədə, dələduz kimisə, qalsaq) bu 16 SELECT * FROM nəticələri tətbiqdə birləşdirilməlidir.
Performansın hansı dəyişikliyini gözləmək olar?
- İntuisiya ilə — xətti.
- Nəzəri olaraq — xətti olmayan, çünki Amdahl qanunu.
- Praktik olaraq — bəlkə, demək olar xətti, bəlkə, yox.
Həqiqətən də düzgün cavab — bilinməyən. Shardlama texnikasının cəld tətbiqi ilə tətbiqinizin işini ciddi şəkildə yavaşlata bilərik, üstəlik DBA qaynar dəmir çubuqla qaçır.
Buna necə nail ola biləcəyimizi görəcəyik. PostgreSQL-də sadəcə olaraq shards=16 parametrlərini qoyub, sonrası öz-özünə işləyəcək - bu maraqlı deyil. Gəlin düşünək ki, 16 dəfə shardlamanın nəticəsində 32 dəfə yavaşladığımızda belə necə edə bilərik - bu, bunu etməməyin maraqlıdır.
Yaxud sürətlənmək və ya yavaşlamaq cəhdlərimiz həmişə klassikaya - köhnə yaxşı Amdahl qanununa (Amdahl law), müraciət edəcək, bu da deyir ki, hər bir sorğunu ideal şəkildə paralel etmək olmur, həmişə müəyyən bir ardıcıl hissə olur.
1.4 Amdahl qanunu
Həmişə bir serialized hissə var.
Həmişə bir sorğunun icra olunması, paralel edilən bir hissə və həmişə paralel edilməyən bir hissə var. Hətta sizə elə gələ bilər ki, mükəmməl paralel edilən bir sorğudur, ən azından nəticə sətirinin toplanması, hansı ki, müştəriyə göndərməyə çalışırsız, hər bir sharddan alınan sətirlərdən, həmişə var və o həmişə ardıcıldır.
Həmişə bir ardıcıl hissə var. O, ümumi konteksdə görünməyəcək kiçik o qədər hissə ola bilər, böyük ola bilər və beləliklə, paralelizasyona təsir edir, amma o həmişə var.
Bundan əlavə, onun təsiri dəyişərək böyüyə bilər, məsələn, cədvəlimizi bölüşdürsək - gəlin mərci yüksəldək - 64 qeyddən 16 cədvələ 4 qeyddən, bu hissə dəyişəcək. Əlbəttə ki, bu cür böyük məlumat həcmləri ilə işləyirik, mobil telefonda və 86 prosessoru 2 MHz, artıq açıq tutulan fayllarımız da yoxdur. Görünür, belə girişlərlə birdəfəlik bir fayl açırıq.
- O idi Ümumi = Serial + Paralel. Məsələn, paralel — DB içi işin hamısı, serial —nəticənin müştəriyə göndərilməsi.
- Oldu Ümumi2 = Serial + Paralel/N + Xserial. Məsələn, ümumi ORDER BY olduqda, Xserial>0.
Bu sadəcə bir nümunə ilə göstərməyə çalışıram ki, müəyyən bir Xserial əmələ gəlir. Bundan başqa, həmişə bir serialized hissə var və məlumatlarla paralel işləməyə çalışırıq, bu məlumatları bölməni təmin etmək üçün əlavə bir hissə yaranır. Təxminən, ehtiyacımız olacaq:
- bu 16 cədvəli databasın içindəki daxili lügatda tapmaq;
- faylları açmaq;
- yaddaş təmin etmək;
- yaddaşı azad etmək;
- nəticələri birləşdirmək;
- nüvələr arasında senkronizasiya etmək.
Bəzi senkronizasiya dəyişiklikləri mütləq yaranır. Onlar heç bir əhəmiyyətli olmayan və ümumi vaxtdan bir milyardıncı hissəni götürən ola bilər, amma həmişə sıfırsızdır və həmişə var. Onların köməyi ilə shardlamadan sonra performansı kəskin şəkildə itirə bilərik.

Bu Amdahl qanunu haqqında bir standart şəkildir. Burada vacib olan budur ki, ideal olaraq düz və xətti artmalıdır, lakin asimptota ilə üzləşir. İnternetdən olan qrafik oxunmaya bilər olduğundan, daha aydın rəqəmlərlə cədvəllər hazırladım.
Tutaq ki, sorğunun icra olunmasında müəyyən bir serialized hissə var ki, o, cəmi 5% tutur: serial = 0.05 = 1 / 20.
İntuisiya ilə elə gəlir ki, 1/20 hissəni götürən serialized bir hissə ilə sorğunun işlənməsini 20 nüvəyə bölüşdürsək, o təxminən 20, ən pis halda isə 18 dəfə sürətli olacaq.
Əslində riyaziyyat — mərhəmətsiz bir şeydir:
wall = 0.05 + 0.95/num_cores, speedup = 1 / (0.05 + 0.95/num_cores)
Nəticə elə olur ki, əgər diqqətlə hesablasanız, serialized hissə 5% olduqda, sürət 10 dəfə olacaq (10,3) və bu, nəzəri olaraq ideal olanın 51%-dir.
8 nüvə | = 5.9 | = 74% |
10 nüvə | = 6.9 | = 69% |
20 nüvə | = 10.3 | = 51% |
40 nüvə | = 13.6 | = 34% |
128 nüvə | = 17.4 | = 14% |
20 nüvə (20 disk, əgər istəsəniz) istifadə edərək, əvvəllər bir nüvənin işlədiyi işdə, hətta nəzəri olaraq heç vaxt 20 dəfə sürətləndirməni almayacağıq, amma praktiki olaraq — daha az olacaq. Üstəlik, paralellərin sayı artdıqca, effektivliyin azalması çox böyüyür.
Yalnız 1% serialized iş qaldıqda, 99% paralel olduqda, artırma göstəriciləri bir qədər yaxşılaşır:
8 nüvə | = 7.5 | = 93% |
16 nüvə | = 13.9 | = 87% |
32 nüvə | = 24.4 | = 76% |
64 nüvə | = 39.3 | = 61% |
Çox böyük bir sorğu üçün, hansı ki, saatlarla işlənir və hazırlıq işi və nəticə quruluşu çox az vaxt alır (serial = 0.001), artıq yaxşı bir effektivlik görünür:
8 nüvə | = 7.94 | = 99% |
16 nüvə | = 15.76 | = 99% |
32 nüvə | = 31.04 | = 97% |
64 nüvə | = 60.20 | = 94% |
Nəzərə alın ki, 100% heç vaxt görünməyəcək. Çox yaxşı hallarda, məsələn 99,999% görünə bilər, amma tam olaraq 100% deyil.
GO TO FULL VERSION