21 komentáře “Úvaha nad horizontálním škálováním databází a těžkostmi s tím spojenými

  1. Zpětný odkaz: » Šestý rok Myšlenek dne otce Fura Myšlenky dne otce Fura

  2. Nestačila by daleko primitivnější strategie čtení z master/slave ?
    – vše se čte ze slave
    – při prvním zápise se přepne na master a dále se čte z master
    – zpět na slave přepnout pokud byl uživatel neaktivní dobu T (neposílal requesty)
    Kde T je empiricky zjištěná max. doba „seconds_behind_master“ + nějaká rezerva.

    Při vhodném(velkém) poměru reads/writes by to mohlo fungovat.
    Pokud jsou uživatelé kteří nezapisují, mohla by strategie fungovat i bez posledního bodu.

  3. Novoji, ja bych se velmi zamyslel nad tim, jestli vam to za ty komplikace stoji. To ze muzete cist z replika serveru neznamena, ze to budete delat. Potrebujete opravdu skalovat ready, mate efektivne zmerene o kolik vam to pomuze? Z tveho popisu se zda, ze chcete pouzivat replika ready pouze na data uzivatele tj. nikdy se nedelaji dotazy (joiny) pres tabulky, kde mohou zapisovat i jini uzivatele resp. vim ze pokud uzivatel neco zapise a nacte, neovlivni vraceny vysledek zapis jinych uzivatelu. V takovem pripade je spise otazka, proc ty data necacheovat primo na aplikacni urovni, odpadla by tim cela ta chytristika na zjistini jestli se data zreplikovala. Navic by odpadnul roundtrip do DB.

    Mimochodem pouziti session/cookies predpoklada, ze uzivatel nebude pouzivat vasi aplikace ve vicero browserech/tabech najednou.

    • Trošku jsem doufal, že se, Dagi, taky zapojíš do diskuse 🙂

      Přiznávám, že tohle je ve fázi co by kdyby – tj. jednou ráno jsem se vzbudil s tímhle nápadem v hlavě. Co se týká jistoty jestli a jak nám to pomůže žádná čísla nemám – otázka je, jestli se dají odhadnout jinak než to zkusit nasadit. Za jistých předpokladů bych třeba dokázal zjistit statistické rozložení write vs. read, ale nakonec bude přeci jen záviset na tom, jak jsou ready s writama provázané, takže takováhle statistika mi přijde jako nevypovídající.

      Jediná vypovídající statistika by podle mého názoru šla udělat jedině tehdy, kdybychom reálně do aplikace zavedly ty změny, které by určovaly, jestli by transakce mohla jít do repliky nebo do masteru a nasadili ji na nějaký systém, kde se doposud pracuje jen s masterem, v módu statistického sledování. Z toho pak teprve zjistit, kolik zátěže by šlo delegovat na repliky a jak bychom masteru ulevili (jak říkám, zatím ty naše databáze stíhají v pohodě všechno co mají, ale spíš se tak mentálně připravuju na budoucnost 🙂 ).

      V souvislosti s tou interakcí mezi uživateli jsi mě přivedl na myšlenku, že bychom naráželi na druhotný problém:

      Replika má T-1
      Já zapíšu v T+0 – user_write_timestamp je T+0
      Kolega zapíše v T+1 – já jeho zápis vidím, protože díky tomu, že replika má T-1 dotazuju MASTER
      Replika dožene master T+0
      Já si obnovím stránku a jdu už do repliky a vidím svůj zápis (T+0), ale zmizí mi zápis kolegy, protože ten je T+1 a na jeho timestampu se nečeká

      Cache se podle mého dají těžko považovat za řešení … pokud máš na data více pohledů – např. zkobinované s dalšími daty (JOINy) nebudou tyhle pohledy mezi sebou korelovat, pokud nebudou cache vygenerovány ve shodný čas. Tohle jsme právě probírali docela intenzivně a připadá nám tohle asi jako nejpracnější varianta – výhodou je ten odpadající roundtrip do DB.

      To, že v jiné session stejný uživatel viděl sem tam maličko starší data, myslím, není problém. Většina lidí pracuje v jedné session – třeba si otevírá odkazy do nových tabů, ale session sdílí. Tohle už se obhájit myslím dá.

      Ale dík moc za nakopnutí ohledně těch víc uživatelů.

      • Moje rada: nepoustet se do toho pokud to neni uzke hrdlo aplikace 🙂

        Je skoda, ze pomer mezi read/write operacemi neznas. Ja bych rozhodne zacal tim, ze bych si zkusil napsat nejake sondy, abych tohle rozlozeni zjistil. To vam umozni vyvazat nektere zbytecne write operace viz to servisni volani, kterym aktualizujete stav uzivatele. To by mohlo pomoci v procisteni kodu aplikace v kazdem pripade, bez ohledu na to jakou strategii zvolite.

        Jeste dve poznamky k tem replika reads:

        1.) Obecne musi pro transakci platit, ze jakykoliv zapis do masteru musi implikovat read z masteru a naopak, aby to bylo konzistenti, ale to je myslim zrejme a pocitas s tim.

        2.) Se casto pouzivaji na operace, kde eventual konzistence nevadi napr. reporting. Nesnazil bych se to tedy zapinat pro celou aplikaci ale pro mista kde to vyrazne pomuze. To jest nedelal bych to transparentne.

      • Ad 1) s tím se skutečně v návrhu počítá
        Ad 2) zdá se, že tohle je asi konsenz většiny komentujících

        Co se týká poměru read/write operací – tušení je, že poměr readů bude vysoký, ale dokud chybí jasná čísla nedá se na tom stavět.

        Díky

  4. Já bych BFU moc neřešil, když už jste takový puntičkáři, tak by možná stačilo prostě odhadnout v jakých situacích by takové případy mohly nastat a v těchto případech nastavit dobu, po kterou by se četlo z masteru i při dalších requestech. Další možnost je např. už na stránce uložení prostě počkat až se to dostane na slaves a mezitím uživateli točit kolečkem.

    • Vím, že naše Operations MySQL Cluster po diskusi zamítli s tím, že víc věcí komplikuje než řeší (detaily neznám). Pročetl jsem dokumentaci Percona XtraDB Cluster a zatím se tváří jako stříbrná kulka, což je samo o sobě podezřelé 🙂

      Našel jsem nějaké zmínky o nečekaných dead-lock: http://www.mysqlperformanceblog.com/2012/08/17/percona-xtradb-cluster-multi-node-writing-and-unexpected-deadlocks/

      Hledal jsem i nějaké porovnání výkonnosti XtraDB Clusteru (semi-synchronní replikace) a čistě asynchronní replikace v setupu master-slave a našel jsem následující graf ukazující signifikantní propad výkonu:

      http://www.mysqlperformanceblog.com/wp-content/uploads/2011/10/semisync.png

      Nicméně je to určitě věc, která stojí za zvážení.

      V tomto článku mne však zajímalo, zda je navržený princip uplatnitelný pro master-slave setup či ne.

      Díky za komentář.

      • MySQL cluster je strasne zavadzajuce meno — je to in-memory databaza ktora je urcena na uplne nieco ine.

        Vsetko od Percona je bleeding-edge: zoberu MySQL aplikuju vsemozne patche z Googlu / Facebooku, otestuju a pustia von. Vacsinou to funguje ale ked potrebujes nieco specificke tak nachdzas len same bugy. XtraDB Cluster nie je ziadna vynimka…

        XtraDB Cluster ma zopar problemov:
        – pridavanie nodov a crash recovery je trochu tazkopadne
        – z casu na cas zacne odmietat nove spojenia (ale velmi ziedka)
        – je to MySQL…

      • Super, díky moc … toto jsou hodně cenné názory. Dneska jsem probíral XtraDB cluster s našimi Operations a ti jsou v souvislosti s daty VELMI konzervativní. Což se ve světle těchto informací ukazuje jako správná cesta.

  5. Pokud jsou transakce skutečně serializovány a replikovány chronologicky, tak by to fungovat mělo, ostatně riziko je poměrně malé – v nejhorším případě user neuvidí svá data hned, ale až když po chvilce nadávání dá refresh.
    Já jsem vycházel z chování své primitivní table-based netransakční replikace, kterou jsem kdysi dávno implementoval.

  6. Tohle mi celé přijde jako hloupost. Proč vymýšlet vlastí hloupé řešení problému, který je milionkrát zdokumentován a milionkrát funkčně vyřešen?

    Je vidět, že jste si s analýzou problematiky nedal žádnou práci. Jinak byste naříklad nepřišel s tou timestamp, když máme seconds_behind_master!

    • Díky za názor. Budu velmi rád, když mě nasměrujete k jednomu z milónů funkčních řešení. S analýzou problematiky jsem si mohl určitě dát větší práci – vždycky si můžete s něčím dát ještě větší práci – nicméně zrovna v souvislosti se seconds_behind_master je situace poněkud složitější. Detaily v článku: http://mysqldatabaseadministration.blogspot.cz/2006/06/investigating-reasons-why-slaves-get.html

      Konkrétně cituji z dokumentace MySQL:

      This field is an indication of how “late” the slave is:

      When the slave SQL thread is actively processing updates, this field is the number of seconds that have elapsed since the timestamp of the most recent event on the master executed by that thread.

      When the SQL thread has caught up to the slave I/O thread and is idle waiting for more events from the I/O thread, this field is zero.

      In essence, this field measures the time difference in seconds between the slave SQL thread and the slave I/O thread.

      If the network connection between master and slave is fast, the slave I/O thread is very close to the master, so this field is a good approximation of how late the slave SQL thread is compared to the master. If the network is slow, this is not a good approximation; the slave SQL thread may quite often be caught up with the slow-reading slave I/O thread, so Seconds_Behind_Master often shows a value of 0, even if the I/O thread is late compared to the master. In other words, this column is useful only for fast networks.

      Nenašel jsem bohužel, že by existovala proměnná, která by obsahovala poslední replikovanou timestamp a byla vždy spolehlivě naplněná správným údajem. Navíc, ve chvíli, kdy neběží replikační vlákno na slave, zdá se, že proměnná seconds_behind_master obsahuje NULL hodnotu, což nemá nijak vypovídající hodnotu.

      Další věc, která do jisté míry protiřečí tomu, co jste zde napsal je to, že škálování pomocí čtení z replik je jeden ze „standardních“ způsobů horizontálního škálování: http://dev.mysql.com/doc/refman/5.0/en/replication-solutions-scaleout.html

  7. Replikace neprobíhají atomicky
    Může se stát, že user vložil nová data v čase T+0, tabulka v master s timestampem bude zreplikována jako první (v čase T+1), mezitím se budou zdlouhavě replikovat další tabulky (dokončení replikačního cyklu v čase T+3), ale uživatel chce vidět jím vložená data už v čase T+2, kdy ještě na replikách nejsou, přestože porovnáním T+0 a T+1 se budeme mylně domnívat, že ano.

    • Hmm, to nějak neodpovídá mému poznání světa (vycházím z MySQL) – můžete to podložit nějakými odkazy do dokumentace? Napíšu vám, z čeho vycházím já:

      – jsou dva druhy replikace row-based a statement-based: http://www.ovaistariq.net/528/statement-based-vs-row-based-replication/
      – zápisy do binlogu jdou v pořadí, v jakém jsou commitovány: http://dev.mysql.com/doc/refman/5.6/en/replication-options-binary-log.html#sysvar_binlog_order_commits
      – slave provádí statementy / aktualizace row podle sekvenčně za sebou, jak je dostává z relay-logu = kopie bin-logu: http://www.xaprb.com/blog/2007/01/20/how-to-make-mysql-replication-reliable/

      Uvádím citaci z posledně jmenovaného zdroje:

      However, these queries are serialized to the binlog in order of transaction commit, so they are run serially on the slave. The slave doesn’t read ahead in the binlog and execute multiple queries simultaneously. To keep the updates consistent, the queries must be executed in the order they’re in the binlog.

      Nikde jsem nenašel informaci o tom, že by row-based replikace neměla dodržovat pořadí commitů. Nikde jsem nenašel informaci o tom, že by replikace probíhala samostatně na úrovni tabulek – tj. nedodržovala pořadí commitů.

      Za předpokladu, že aktualizaci „master timestampu“ provedu po dokončení uživatelské transakce a budu pracovat s touto hodnotou, měl bych mít možnost se spolehnout, že jakmile se objeví v replice, předchozí commitnutá transakce s uživatelskými daty už tam bude.

      Pokud máte někde odkaz na něco, co vysvětluje opak, budu vám za to velmi vděčen.

      • Záleží na tom, zda „pořadí commitů“ je stejné pořadí, v jakém je vidí všechna spojení k databázi (obecně to neplatí, je možné, že MySQL je v tomhle specifická a tohle chování zaručuje). I kdyby to tak bylo, je potřeba jako časovou značku použít čas commitu transakce, což nevím, jestli jde nějak zjistit.

    • Nepovazuju se za velkeho db guru, ale pokud by nebyla zajistena posloupnost akci, tak by db prece nemohla garantovat referencni integritu, ne?

  8. Podle mne vycházíte z předpokladu, že se transakce provádějí jedna po druhé v nějakém pořadí, a že se ve stejném pořadí provedou i na replice. Což platí pro lokální databázi jen v případě izolace transakcí serializable, v případě replikované databáze bych si tím nebyl jist ani při této úrovni izolace transakcí.

    Pokud by se nepoužívala časová značka, ale sekvence, stačila by možná úroveň izolace repeatable read. Ale pořád se tam budou prát všechny transakce o jednu sekvenci, takže to bude mít dopad na výkon. A hlavně je otázka, co vlastně zaručuje ta replikace — jestli vůbec něco zaručuje ve vztahu k transakcím a jejich izolaci.

    • Ano, z tohohle předpokladu vycházím. Stačí mi, aby bylo zaručeno pořadí transakcí vzhledem k aktuálnímu uživateli = requestu = 1 vláknu. Pokud vím, tak replikace se děje tak, že master zapisuje sekvenčně do binárního logu, ze kterého čtou i repliky. Tj. jak se transakce do logu zařadí, tak by jí měl i slave zpracovat – tj. tady je ta relativní sekvenčnost vzhledem k jednomu uživateli zachovaná.

      Pravdpodobně narážíte na problém, kdy bych při nějakém výpočtu četl data z replik a použil je pro výpočet nějaké nové hodnoty, kterou zapíšu do masteru. Tím bych zanedbal možné aktualizace jiných uživatelů v master databázi. To je samozřejmě pravda, ale v případě, že celou podobnou operaci (včetně čtení) uzavřu do jedné transakce (což bych měl tak jako tak), která díky svému zápisovému charakteru (produkuje nějaký zápis na závěr) musí jít do master databáze, budou se proti master databázi provádět i ta čtení, takže si myslím, že princip zůstává funkční i pro tento případ.

      Možná máte na mysli ještě něco jiného, co jsem nepochopil? Nějaký use-case? … díky

      • Šlo mi o to, že ani tohle nezaručuje, že uživatelův zápis už v replice je. Protože v replice může časovou značku zvýšit zápis jiného uživatele, který se do repliky dostane dřív, než zápis „můj“.

        Nebo-li můžete mít dvě transakce, transakce A bude mít časovou značku 1, transakce B časovou značku 2. Do transakčního logu se ale mohou zapsat v pořadí B, A. Pak může nastat okamžik, kdy v replice už bude transakce B (takže podle časových značek „novější“ transakce), ale A tam nebude. Nebo-li uživatel stejně neuvidí svá data, protože v replice ještě nejsou, přestože tam jsou data z „novějších“ transakcí.

        Pokud nechcete všechny transakce tvrdě seřadit za sebe, byla by ještě možnost transakce číslovat a do pomocné tabulky si ukládat přehled transakcí, které proběhly. Takže repliky byste se pak neptal na časovou značku, ale na to, zda má v tabulce transakcí záznam o transakci X. Samozřejmě je to náročnější na výkon, protože v té tabulce se bude muset hledat. Na druhou stranu tam potřebujete jen záznamy o aktuálních session, takže počet záznamů té tabulky by byl zhruba stejný, jako počet souběžně otevřených session. Ostatní záznamy by bylo možné průběžně mazat. Ale vůbec nemám představu, jaký dopad na výkon by něco takového mělo.