Fəsil 01 · 9 dəq oxu

Reliable, scalable və maintainable sistemlər

Reliable, scalable və maintainable sistemlər

Bu gün yazılan application-ların çoxu compute-intensive deyil, data-intensive-dir. CPU onların darboğazı deyil; problem data-nın həcmi, mürəkkəbliyi və dəyişmə sürətidir. Tipik bir məhsul eyni anda bir neçə sual verir: istifadəçinin data-sını haradasa saxlamaq və sonra tapmaq, bahalı əməliyyatların nəticəsini yadda saxlayıb oxumanı sürətləndirmək, istifadəçiyə keyword ilə axtarış vermək, başqa process-ə mesaj göndərib asinxron emal etmək, hər gecə böyük həcmdə toplanmış data-nı emal edib hesabat çıxarmaq. Bu suallara cavab verən standart building block-lar var: database, , search index, message queue və , batch processor.

Amma heç bir məhsul "sadəcə database" deyil. Real tətbiq bu alətlərin bir neçəsini birləşdirir, aralarında data-nı sinxron saxlayır və nəticədə özünə məxsus, bespoke data sistemi qurur: application kodu invalidate edir, search index-i yeniləyir, queue-ya event yazır. Siz artıq data sisteminin dizayneri olursunuz və bu kitab məhz bu rolu öyrədir. Kitabın bütün fəsillərini üç sual birləşdirir: sistem nə vaxt reliable-dır, nə vaxt və nə vaxt maintainable-dır.

Reliability: fault ilə failure fərqi

Reliable sistem "nəsə səhv getsə də düzgün işləməyə davam edən" sistemdir. Burada dəqiq terminologiya vacibdir. Fault sistemin bir komponentinin spesifikasiyadan kənara çıxmasıdır: disk nasazlığı, process-in çökməsi, yanlış konfiqurasiya. Failure isə bütün sistemin istifadəçiyə lazım olan xidməti verə bilməməsidir. Fault-ları sıfıra endirmək mümkün deyil; məqsəd fault-un failure-a çevrilməsinin qarşısını almaqdır. Buna fault-tolerant və ya resilient sistem deyilir.

Bu baxış bir qəribə nəticə verir: fault-ları qəsdən yaratmaq faydalıdır. Təsadüfi process-ləri öldürən, şəbəkəni kəsən alətlər fault-tolerance mexanizmlərinin həqiqətən işlədiyini yoxlayır.

Hardware fault-ları

Böyük data center-də hard disk-lər daim sıradan çıxır. Disk-lərin MTTF-i, yəni orta nasazlıq müddəti, 10–50 il civarındadır. Bu az görünə bilər, amma 10 000 disk-lik cluster-də gündə təxminən bir disk ölür:

gündəlik nasazlıq ≈ disk sayı / (MTTF il · 365)
                  ≈ 10 000 / (30 · 365) ≈ 0.9 disk/gün

Klassik cavab : RAID, ikili power supply, generator. Bu, tək maşının ölməsini nadir hadisəyə çevirir. Amma data həcmi artdıqca maşın sayı artır və bütöv maşının itməsi adi hala gəlir. Buna görə müasir sistemlər hardware software-də fault-tolerance ilə tamamlayır: bir node itəndə sistem işləməyə davam edir. Əlavə fayda: node-ları bir-bir restart edib downtime-sız patch tətbiq etmək olur.

Software səhvləri

Hardware fault-ları adətən müstəqildir: bir disk-in ölməsi digərinin ölmə ehtimalını artırmır. Software səhvləri isə korrelyasiyalıdır. Eyni bug bütün node-larda eyni anda tetiklənir: müəyyən bir giriş alanda çökən process, bütün server-ləri eyni vaxtda əhatə edən leap second bug-ı, bütün resursu yeyən runaway process, cavab verməyən xarici service-ə bağlı olan bütün komponentlər, kiçik xətanın böyüyə çevrildiyi cascading failure.

Bu bug-lar uzun müddət gizli qalır, çünki nadir şərtləri gözləyir. Onlara qarşı sehrli həll yoxdur; işləyən şeylər xırdadır: sistemin fərziyyələrini açıq yazmaq, hərtərəfli test, process-ləri izolyasiya etmək, çökən process-in avtomatik restart-ı və production-da davranışı ölçüb gözləniləndən kənara çıxanda xəbərdar etmək.

İnsan səhvləri

Böyük internet service-lərində əsas səbəbi operator-un yanlış konfiqurasiyasıdır, hardware fault-ları isə siyahıda çox aşağıdadır. İnsanları etibarlı etmək mümkün deyil, amma sistemi insan səhvini çətinləşdirəcək şəkildə qurmaq olar:

  • Yaxşı dizayn olunmuş abstraksiya və API-lar "düzgün yolu" asan, səhv yolu çətin edir.
  • Production-dan ayrı, real data ilə işləyən mühitlər təhlükəsiz eksperiment imkanı verir.
  • Unit test-dən integration test-ə qədər hər səviyyədə avtomatik test, xüsusilə normal istifadədə nadir rast gəlinən corner case-lər üçün.
  • Tez və asan recovery: konfiqurasiyanı etmək, yeni kodu tədricən buraxmaq, köhnə data-nı yenidən hesablamaq.
  • Ətraflı monitoring və telemetry: performance metrikləri, error rate-lər. Telemetry olmadan problem baş verəndən sonra nə olduğunu bilmirsiniz.

Scalability: yükü təsvir etmək

Sistemin bu gün reliable olması sabah da belə olacağı demək deyil. Yük artanda sistem degradasiya olur. "X " kimi binar etiket deyil; suala cavabdır: "sistem müəyyən şəkildə böyüyəndə bununla necə baş edirik?"

Bu sualı vermək üçün əvvəlcə yükü təsvir etmək lazımdır. Yük load -lərlə təsvir olunur: saniyədə request sayı, database-də oxuma-yazma nisbəti, eyni anda aktiv istifadəçi sayı, hit rate. Hansı parametrin vacib olması sistemə bağlıdır.

Klassik nümunə Twitter-in home timeline-ıdır. İki əsas əməliyyat var: tweet göndərmək və öz timeline-ını oxumaq. Oxuma yazmadan yüz dəfələrlə çoxdur. İki dizayn mümkündür:

YanaşmaYazmaOxumaProblem
Fan-out on readTweet sadəcə global cədvələ yazılırİstifadəçinin follow etdiyi hər kəsin tweet-lərini tapıb birləşdirirOxuma saniyədə yüz minlərlə sorğu yaradır; timeline yavaş açılır
Fan-out on writeTweet hər follower-in timeline kopyalanırHazır bir oxumaMilyonlarla follower-i olan istifadəçi bir tweet-lə milyonlarla yazma tetikləyir

Fan-out on write-da əsas load istifadəçi başına follower paylanmasıdır, sadəcə ümumi tweet sayı deyil. Həll hibriddir: adi istifadəçilərin tweet-ləri yazma anında fan-out olunur, məşhurların tweet-ləri isə oxuma anında ayrıca gətirilib birləşdirilir. Bu dizaynın dərsi odur ki, "doğru" yanaşma yükün paylanmasından asılıdır və bir parametrin dəyişməsi dizaynı dəyişdirə bilər.

Scalability: performansı təsvir etmək

Yükü təsvir edəndən sonra iki sual verilir: load artanda resursu sabit saxlasaq performans necə dəyişir, və performansı sabit saxlamaq üçün resursu nə qədər artırmaq lazımdır?

Batch sistemlərində performans -dur: saniyədə emal olunan qeyd sayı. Online sistemlərdə isə response time: istifadəçinin request göndərməsindən cavab almasına qədər keçən vaxt. Response time ilə fərqli şeylərdir: request-in emal olunmağı gözlədiyi vaxtdır, response time isə buna şəbəkə gecikməsini və emalı da əlavə edir.

Response time tək rəqəm deyil, paylanmadır. Eyni request təkrar göndərilsə də, context switch, paket itkisi, garbage collection pause, disk-in page oxuması səbəbindən fərqli nəticə verir. Buna görə orta göstərici pis metrikdir: neçə istifadəçinin həqiqətən gecikmə yaşadığını demir. Doğru alət percentil-lərdir:

  • p50, median: request-lərin yarısı bundan tez cavab alır.
  • , , : ən yavaş 5%, 1% və 0.1%. Bunlara deyilir.

vacibdir, çünki ən yavaş request-ləri alan istifadəçilər çox vaxt ən çox data-sı olan, yəni ən dəyərli müştərilərdir. Amazon daxili service-lər üçün üzrə tələb qoyur: bir milyon request-dən min biri də olsa, sayılır.

Percentil-lər və -larda istifadə olunur: "p50 200 ms-dən az, 1 s-dən az, uptime 99.9%". Belə razılaşma müştərinin nə gözləyəcəyini dəqiqləşdirir.

Backend-də iki fenomen tail-i böyüdür. Head-of-line blocking: server-in paralel işlədə biləcəyi request sayı azdırsa, bir neçə yavaş request növbəni tutur və sonrakı sürətli request-lər də yavaş görünür. amplification: bir istifadəçi request-i on backend çağırışı tələb edirsə, birinin yavaş olması bütün request-i yavaşladır. Backend çağırışlarının 1%-i yavaşdırsa, on çağırışlı request-lərin təxminən 10%-i yavaş olur:

P(ən azı biri yavaş) = 1 − (0.99)^10 ≈ 0.096

Percentil-ləri ölçmək üçün bütün response time-ları saxlamaq lazım deyil; HdrHistogram və ya t-digest kimi alqoritmlər yaxşı yaxınlaşma verir. Amma dashboard-da percentil-ləri ortalamaq olmaz: orta dəyərinin mənası yoxdur, histogram-ları birləşdirmək lazımdır.

Scalability: yüklə baş etmək

Bir həcmdə yük üçün yaxşı olan architecture on dəfə böyük yük üçün yaxşı olmur. Sürətlə böyüyən sistemdə architecture-i hər böyümə pilləsində yenidən düşünmək lazımdır.

İki əsas istiqamət var. Scaling up, vertical scaling: daha güclü maşın. Scaling out, horizontal scaling: çoxlu kiçik maşın, buna shared-nothing architecture də deyilir. Tək güclü maşın sadədir, amma xətti olmayan şəkildə bahalaşır. Praktik sistemlər çox vaxt qarışıqdır: bir neçə güclü maşın çoxlu zəif virtual maşından ucuz ola bilər.

Elastic sistemlər yük artanda avtomatik resurs əlavə edir; əl ilə scale olunan sistemlər isə daha sadə və proqnozlaşdırıla biləndir. Yük qeyri-müəyyəndirsə elastiklik faydalıdır, amma öz sürprizlərini gətirir.

Stateless service-ləri çoxlu maşına yaymaq sadədir. Stateful sistemi, yəni database-i distributed etmək isə ciddi mürəkkəblik gətirir. Buna görə uzun müddət qayda belə idi: database-i tək maşında saxla, scaling tələbi məcbur edənə qədər. Bu qayda dəyişir; distributed data sistemləri üçün abstraksiyalar yaxşılaşır. Kitabın II hissəsi məhz bu mövzudur.

Vacib olan odur ki, universal architecture yoxdur. Saniyədə 100 000 request, hər biri 1 KB, ilə dəqiqədə 3 request, hər biri 2 GB, eyni , amma tamam fərqli sistem tələb edir. architecture konkret load fərziyyəsi üzərində qurulur; fərziyyə səhv çıxarsa, iş boşa gedir. Sürətlə inkişaf edən məhsulda gələcəyin hipotetik yükü üçün deyil, növbəti funksionallıq üçün tez iterasiya etmək çox vaxt daha vacibdir.

Maintainability: operability

Software-in əsas xərci ilkin development deyil, davamlı baxımdır: bug-ları düzəltmək, sistemi işlək saxlamaq, failure-ları araşdırmaq, yeni platformalara uyğunlaşdırmaq, texniki borcu ödəmək. Maintainable sistem bu işi asanlaşdırır. Üç prinsip var.

Operability operations komandasının sistemi rahat işlək saxlaya bilməsidir. Yaxşı operations pis software-in çatışmazlıqlarını örtə bilir, amma yaxşı software pis operations ilə etibarlı işləmir. Operability-ni artıran şeylər: runtime davranışı və daxili vəziyyət barədə görünürlük, avtomatlaşdırma və standart alətlərlə inteqrasiya, tək maşından asılılığın olmaması, yaxşı sənədləşdirmə və aydın operational model, "default-lar yaxşıdır, amma lazım olanda override etmək mümkündür" prinsipi, özünü sağaldan amma lazım gələndə admin-ə nəzarət verən davranış, proqnozlaşdırıla bilən davranış.

Maintainability: sadəlik

Kiçik layihələr sadə kod ilə başlayır, böyüdükcə mürəkkəbləşir və anlaşılması çətinləşir. Bu mürəkkəblik baxımı yavaşladır, büdcəni və cədvəli pozur və dəyişiklik zamanı bug riski artırır. Mürəkkəbliyin əlamətləri: şişmiş state space, modullar arasında sıx , qarışıq dependency-lər, ardıcıl olmayan adlandırma, bir problemi həll edən hack-lər, special-case-lər.

Burada accidental complexity anlayışı vacibdir: problemin öz mahiyyətindən gəlməyən, sadəcə implementasiyadan yaranan mürəkkəblik. Onu azaltmağın ən yaxşı aləti abstraction-dır. Yaxşı abstraksiya implementasiya detallarını təmiz fasad arxasında gizlədir və fərqli tətbiqlərdə təkrar istifadə olunur. SQL belə abstraksiyadır: disk-də data strukturlarını, konkurent sorğuları, crash-dən sonra bərpanı gizlədir. High-level proqramlaşdırma dilləri maşın kodunu gizlədir. Distributed sistemlər üçün bu səviyyədə yaxşı abstraksiyalar hələ az tapılır və kitab boyunca onları axtaracağıq.

Maintainability: evolvability

Sistemin tələbləri sabit qalmır: yeni fakt öyrənilir, biznes prioriteti dəyişir, istifadəçi yeni funksiya istəyir, platforma dəyişir, qanunvericilik yenilənir. Agile metodologiyası dəyişikliyə uyğunlaşma üçün kod səviyyəsində alətlər verir: TDD, refactoring. Böyük data sistemi səviyyəsində eyni ideyaya evolvability deyilir.

Data sistemini dəyişdirmək kodu dəyişdirməkdən çətindir, çünki data-nın özü də var: schema dəyişir, köhnə qeydlər yeni formatla oxunmalıdır, iki versiya eyni anda işləyir. Fəsil 4 bu problemi, yəni encoding və schema evolution-ı, ətraflı izah edir. Sadə və abstraksiyası yaxşı sistem dəyişdirməyə daha asandır; evolvability sadəliklə sıx bağlıdır.

Üç prinsip bir yerdə

Reliability, və maintainability bir-birindən ayrı deyil. Bu üç suala cavab verərkən konkret texnologiya yox, dizayn seçimləri həlledicidir:

PrinsipƏsas sualTipik alətlər
ReliabilityFault baş verəndə sistem düzgün işləməyə davam edirmi?, , izolyasiya, test, telemetry, tez
Yük artanda performansı saxlamaq üçün nə etməliyik?Load ölçmək, percentil-lər, scaling out,
MaintainabilityYeni mühəndis sistemi başa düşüb dəyişə bilirmi?Yaxşı abstraksiya, operability, sadə operational model, evolvable schema

Qalan fəsillər bu sualları konkret komponentlər üzərində açır: əvvəlcə tək maşında data model-lər və storage engine-lər, sonra distributed data və nəhayət derived data sistemləri.

Tapşırıqlar

  1. Öz işlədiyiniz və ya yaxşı tanıdığınız bir sistem üçün ən azı üç load yazın. Hər parametr on dəfə artsa, hansı komponent ilk olaraq darboğaz olar? Fan-out on read ilə fan-out on write nümunəsini bu sistemə tətbiq etmək mümkündürmü?
  2. Bir istifadəçi request-i paralel olaraq 20 backend çağırışı edir və hər çağırışın 300 ms-dir. Request-lərin təxminən neçə faizi 300 ms-dən yavaş olacaq? Bu rəqəmi aşağı salmaq üçün dizayn səviyyəsində hansı iki seçim var?
  3. Son bir ildə gördüyünüz production insidentlərini üç kateqoriyaya bölün: hardware, software, insan. Hər biri üçün fault-un failure-a çevrilməsinin qarşısını ala biləcək bir mexanizm təklif edin və onun qiymətini qiymətləndirin.