Fəsil 06 · 8 dəq oxu

Partitioning

Partitioning

Fəsil 5-də eyni data-nın bir neçə surətini saxladıq. Amma data həcmi və ya sorğu yükü bir maşına sığmayanda surət çıxarmaq kömək etmir: hər eyni böyüklükdədir və eyni yazma yükünü daşıyır. Bu halda data-nı hissələrə bölmək lazımdır. Hər hissə partition adlanır, MongoDB və Elasticsearch-də , HBase-də region, Cassandra-da vnode kimi də tanınır. Hər qeyd tam olaraq bir partition-a aiddir və hər partition özlüyündə kiçik database-dir.

məqsədi -dir: böyük dataset çoxlu diskə, sorğu yükü isə çoxlu processor-a yayılır. Yaxşı bölünmüş sistemdə on node on dəfə çox data saxlayır və təxminən on dəfə çox verir. Pis bölünmüş sistemdə isə bütün yük bir node-a düşür və qalan doqquzu boş dayanır. Bu fəsil bölgünü düzgün etməyin, index-ləri bölməyin, node əlavə olunanda yenidən balanslamağın və sorğunu düzgün partition-a yönləltməyin yollarını izah edir.

Partitioning və replication birlikdə

adətən ilə birlikdə işləyir: hər partition-ın surəti bir neçə node-da saxlanılır. Leader-based modeldə hər partition-ın öz leader-i var və bir node bəzi partition-lar üçün leader, digərləri üçün follower ola bilər. Belə düzülüşdə bir node çökəndə onun leader olduğu partition-ların baş verir, qalan partition-lar isə təsirsiz qalır. Fəsil 5-də haqqında deyilənlər hər partition-a ayrıca tətbiq olunur və bu fəsildə bölgünün özünə baxırıq.

Key-value data-nı bölmək

Sadə haldan başlayaq: data key ilə əldə edilir. Məqsəd yükü node-lar arasında bərabər yaymaqdır. Bərabər olmayan bölgü skewed adlanır, bütün yükün düşdüyü partition isə -dur. Ən sadə yanaşma qeydləri təsadüfi node-a yazmaqdır, amma onda oxuyanda hansı node-a getməyi bilməzsiniz və hamısını soruşmalı olarsınız.

Key range üzrə partitioning

Hər partition-a key-lərin davamlı diapazonu verilir: kağız ensiklopediyanın cildləri kimi, birinci cild A–B, ikinci C–D. Sərhədlər bərabər məsafədə olmaq məcburiyyətində deyil, çünki data bərabər paylanmayıb: T–Z bir cildə sığır, A isə ikisini tutur. Sərhədlər əl ilə və ya avtomatik seçilir.

Hər partition daxilində key-lər sıralı saxlanılır, Fəsil 3-dəki SSTable-lar kimi. Bu, range scan-ləri ucuz edir: "mart ayının bütün ölçmələri" tək partition-da ardıcıl oxumadır.

Zəif tərəfi . Sensor data-sında key timestamp olsa, bütün bugünkü yazmalar tək partition-a gedir, digərləri boş qalır. Həll key-i dəyişməkdir: sensor adını timestamp-in qabağına qoymaq. Onda yazma sensor-lara görə yayılır, amma "bütün sensor-ların bu saatkı data-sı" sorğusu hər partition-a ayrı getməli olur.

Hash üzrə partitioning

Skew riskini azaltmaq üçün key-in özü deyil, partition seçir. Yaxşı funksiyası oxşar key-ləri bir-birindən uzağa atır: "2026-11-02T10:00" və "2026-11-02T10:01" fərqli partition-lara düşür. Kriptoqrafik güc lazım deyil, MD5 və ya Fowler–Noll–Vo kifayətdir. Diqqət edilməli məqam: bəzi dillərin built-in funksiyası proses hər başlayanda fərqli nəticə verir və üçün yararsızdır.

Hər partition-a dəyərlərinin diapazonu verilir. Bu, yükü bərabər yayır, amma range scan-ləri itirir: bir vaxtlar qonşu olan key-lər indi hər yerə səpələnib. MongoDB-də açıqsa, range query bütün partition-lara gedir.

Cassandra bu iki yanaşmanı birləşdirir: compound primary key-in birinci hissəsi və partition-ı seçir, qalan sütunlar isə partition daxilində sıralama üçündür. Məsələn (user_id, update_timestamp) key-ində hər istifadəçinin yeniləmələri tək partition-dadır və vaxta görə range scan mümkündür, fərqli istifadəçilər isə fərqli partition-lara yayılıb. Bu, one-to-many əlaqələr üçün güclü pattern-dir.

Terminologiya haqqında qeyd: bəzən adlanır, amma bu termin əslində yükü təsadüfi seçilmiş sərhədlərlə yaymaq üçün istifadə olunan konkret üsula aiddir və database-lərdə nadir hallarda tətbiq edilir. Fərqi qarışdırmamaq üçün " " demək daha dəqiqdir.

Hash-in həll etmədiyi skew

Eyni key-ə həddindən çox müraciət olanda kömək etmir: milyonlarla follower-i olan məşhurun hər paylaşımı eyni key-ə düşür. Hazırda database-lər bunu avtomatik həll etmir, application özü etməlidir. Məlum üsul "qaynar" key-in sonuna təsadüfi iki rəqəmli suffix əlavə etməkdir: yazmalar yüz fərqli key-ə yayılır. Qiyməti oxumadadır, hər oxuma yüz key-i toplamalıdır, ona görə yalnız az sayda qaynar key üçün dəyər və hansı key-lərin qaynar olduğunu izləmək lazımdır.

Secondary index-ləri bölmək

İndiyə qədər qeydə yalnız primary key ilə çatırdıq. Secondary index-lər, məsələn "qırmızı rəngli bütün maşınlar", partition-a aydın şəkildə uyğun gəlmir, çünki qeydi unikal təyin etmir. İki yanaşma var.

Document-partitioned: lokal index

Hər partition öz qeydləri üçün ayrıca index saxlayır və digər partition-ların data-sı ilə maraqlanmır. Yazma sadədir: qeyd hansı partition-a gedirsə, index də orada yenilənir. Oxuma isə bahadır: qırmızı maşınlar bütün partition-larda ola bilər, ona görə sorğu hər partition-a göndərilib nəticələr birləşdirilir. Buna scatter/gather deyilir və Fəsil 1-dəki amplification-ın birbaşa nümunəsidir: ən yavaş partition cavabı müəyyən edir. Buna baxmayaraq MongoDB, Cassandra, Elasticsearch və SolrCloud bu yanaşmanı seçir.

Term-partitioned: qlobal index

Alternativ bütün data-nı əhatə edən tək index qurub onu da bölməkdir, amma primary key-dən fərqli şəkildə: məsələn color:a–color:r bir partition-da, qalanı digərində. Index-in bölündüyü açar term adlanır, ona görə bu yanaşma term-partitioned-dir. Term üzrə range diapazon sorğularına, isə bərabər yükə kömək edir.

Oxuma səmərəlidir: client yalnız lazımi term-in olduğu partition-a gedir. Yazma isə mürəkkəbləşir: bir qeydin sütunları fərqli index partition-larına düşə bilər və hər yazma bir neçə node-a toxunur. Bunu synchronous etmək Fəsil 7-dəki distributed transaction tələb edir, ona görə praktikada qlobal index-lər asinxron yenilənir: yazdıqdan dərhal sonra index-də görünməyə bilər. DynamoDB-nin global secondary index-ləri belə işləyir.

XüsusiyyətDocument-partitionedTerm-partitioned
Yazmatək partition, synchronousbir neçə partition, adətən asinxron
Oxumascatter/gather, hədəfli, tək partition
Index-in təzəliyidərhalgecikə bilər
Uyğun halyazma-ağır, nəticələri az sorğularoxuma-ağır, seçici sorğular

Rebalancing

Vaxt keçdikcə sorğu yükü artır, dataset böyüyür, maşınlar çökür. Hamısı data-nı bir node-dan digərinə köçürməyi tələb edir: rebalancing. Yaxşı rebalancing üç tələbə cavab verir: sonra yük bərabər paylanıb, köçürmə zamanı sistem oxumanı və yazmanı qəbul edir, şəbəkə və disk yükü azaltmaq üçün yalnız lazım olan qədər data köçür.

Niyə hash mod N olmaz

İlk instinkt key-in node sayına bölüb qalığını götürməkdir. Problem N dəyişəndə ortaya çıxır: on node-dan on birə keçəndə key-lərin böyük əksəriyyəti yeni node-a düşür, çünki hash mod 10 və hash mod 11 demək olar ki, hər key üçün fərqlidir. Bütün data cluster-də yerini dəyişir, bu da ən bahalı rebalancing-dir.

Sabit sayda partition

Sadə həll node sayından qat-qat çox partition yaratmaqdır: on node-da min partition, hər node-da yüz. Yeni node gələndə hər mövcud node-dan bir neçə partition "oğurlayır" ki, bölgü yenidən bərabərləşsin. Partition-ların sayı və key-lərin partition-a təyini dəyişmir, yalnız partition-ların node-a təyini dəyişir. Bu, Riak, Elasticsearch, Couchbase və Voldemort-un yanaşmasıdır.

Partition sayı əvvəldən seçilir və sonradan dəyişmir. Çox böyük seçsəniz, hər partition-ın idarə overhead-i yığılır; çox kiçik seçsəniz, node sayı artanda hər node-a az partition düşür. Dataset-in həcmi dəyişkən olanda düzgün sayı tapmaq çətindir. Daha güclü maşınlara daha çox partition verməklə qeyri-bərabər hardware-i də nəzərə almaq olar.

Dinamik partitioning

Key range sabit sərhədlər dataset dəyişəndə skew yaradır. Dinamik yanaşmada partition müəyyən ölçünü keçəndə ikiyə bölünür, çox kiçiləndə isə qonşusu ilə birləşir: yuxarı səviyyəsinə bənzəyir. Partition sayı data həcmi ilə böyüyür. HBase və MongoDB belə işləyir. Zəif tərəfi başlanğıcdır: boş database tək partition-dan ibarətdir və ilk bölünməyə qədər bütün yük bir node-dadır. Bəzi sistemlər bunu pre-splitting ilə həll edir: key paylanması məlum olanda başlanğıc sərhədləri əl ilə verilir.

Node sayına mütənasib

Üçüncü yanaşma hər node-a sabit sayda partition verməkdir, məsələn Cassandra-da 256. Dataset böyüdükcə partition-lar böyüyür, node əlavə olunanda isə yeni node mövcud partition-ların bəzilərini təsadüfi nöqtələrdə kəsib yarısını götürür. Təsadüfilik qeyri-bərabər bölgü yarada bilər, amma çoxlu partition üzərindən ortalama bu təsiri azaldır.

Avtomatik, yoxsa əl ilə

Tam avtomatik rebalancing rahatdır, amma riskli: Fəsil 5-dəki kimi, bu da gözlənilməz nəticə verə bilər. Yüklənmiş node cavab verməyəndə sistem onu çökük sayıb partition-larını başqalarına köçürməyə başlayır, bu da həmin node-u və şəbəkəni daha da yükləyir və cascading failure yaranır. Ona görə bir çox sistem avtomatik plan təklif edir, amma icra üçün operator-un təsdiqini gözləyir. Rebalancing bahalı əməliyyatdır və insanın nəzarətində olması çox vaxt daha təhlükəsizdir.

Sorğunu düzgün partition-a yönləltmək

Partition-lar node-lar arasında köçürüləndə client hansı node-a müraciət edəcəyini necə bilir? Bu, service discovery-nin xüsusi halıdır. Üç yanaşma var:

  1. Client istənilən node-a müraciət edir; node sorğunu özündə olmayan partition üçün sahibinə ötürüb cavabı qaytarır.
  2. Bütün sorğular partition-aware routing tier-dən keçir; o, rolunu oynayır və özü data saxlamır.
  3. Client partition-ların node-lara təyinini bilir və birbaşa düzgün node-a gedir.

Hər halda əsas sual eynidir: təyinat dəyişəndə yönləndirən tərəf bundan necə xəbər tutur? Bir çox sistem ZooKeeper kimi ayrıca coordination service-dən istifadə edir: hər node özünü orada qeyd edir, təyinatın nüfuzlu surəti orada saxlanılır və dəyişiklikdə routing tier bildiriş alır. Fəsil 9 bu cür service-lərin daxilində consensus-un necə işlədiyini açır. Cassandra və Riak isə ayrıca service olmadan gossip protocol ilə işləyir: node-lar bir-birinə öz bildiklərini ötürür və hər node təyinatı bilir, bu da birinci yanaşmaya uyğundur.

İndiyə qədər tək key ilə işləyən sorğulara baxdıq. Analitik massively parallel processing database-ləri isə bir sorğunu bir neçə partition-da paralel icra olunan mərhələlərə bölür: join, filter və aggregation hər partition-da hesablanıb birləşdirilir. Fəsil 10-dakı eyni ideyanın ümumiləşdirilmiş formasıdır.

Fəslin nəticəsi

böyük data-nı çoxlu maşına yaymağın yeganə yoludur və bu yayılmanın hər detalında var: key range sorğunu asanlaşdırır, yükü bərabərləşdirir; lokal index yazmanı, qlobal index oxumanı ucuzlaşdırır; sabit partition sayı sadədir, dynamic uyğunlaşandır. Hər partition kiçik, müstəqil database olduğuna görə bir neçə partition-a toxunan yazma yeni bir problemi qaldırır: onların hamısı ya uğurlu olmalı, ya heç biri. Bu, növbəti fəslin, yəni transaction-ların mövzusudur.

Tapşırıqlar

  1. IoT platformasında hər sensor saniyədə bir ölçmə göndərir və tipik sorğu "verilən sensor-un son 24 saatı"dır, nadir sorğu isə "bütün sensor-ların son 5 dəqiqəsi". Key range, və compound key yanaşmalarının hər biri üçün yazma skew-unu və hər iki sorğunun toxunduğu partition sayını müqayisə edin, sonra seçiminizi əsaslandırın.
  2. Dörd node, hər birində 64 sabit partition olan cluster-ə beşinci node əlavə olunur. Hər node-dan neçə partition köçürülməlidir ki, bölgü bərabər qalsın? Eyni vəziyyəti hash mod N ilə müqayisə edib köçürülən data payını qiymətləndirin.
  3. Document-partitioned secondary index-li sistemdə "qiyməti 100-dən az məhsullar" sorğusu yavaşdır. Scatter/gather-in bu niyə yaratdığını Fəsil 1-dəki amplification ilə izah edin və term-partitioned index-ə keçidin hansı yeni problemi gətirəcəyini göstərin.