Internet Info, s.r.o. Lupa Měšec Podnikatel Root Zdroják DigiZone Slunečnice Vitalia TopDrive KupDnes Navrcholu NovýTarif Dobrý web Weblogy Woko Jagg Computer.cz SK: MojeLinky

Hlavní navigace

MySQL v roli neschémové databáze

Neschémové databáze (pro které se vžilo označení „NoSQL“) jsou stále populárnější. Vývojáři začínají často narážet na omezení relačních databází, jejichž návrh je sice časem ověřený, ale přece jen poněkud staršího data. Pojďme se podívat na zajímavý příklad netradičního využití MySQL ve službě FriendFeed.

Tweetni to Twitter Jaggni to! Jagg Del.icio.us Delicious

Článek je volným překladem článku Breta Taylora, vydaného pod CC-BY licencí 27.2.2009 na autorově blogu.

Na úvod

Ve FriendFeedu používáme pro ukládání veškerých dat MySQL. Naše databáze rostla zároveň s počtem uživatelů. V současnosti je v databázi přes 250 milionů záznamů („současnost“ je únor 2009, pozn. překl.) a spousta dalších dat, od komentářů a příznaků „líbí se mi“ až po seznamy přátel.

Během růstu databáze jsme se museli vypořádat s problémy škálovatelnosti, které se objevily spolu s růstem. Udělali jsme to, co se dělává: Použili jsme čtení ze slave databází a memcached pro zvýšení výkonu při čtení, a rozdělili jsme databázi (v originále „database sharding“ – technika, při níž jsou záznamy umisťovány do různých fyzických úložišť podle nějakého předem stanoveného kritéria – pozn. překl.) pro zvýšení výkonu při zápisu. Ale škálování existujících funkcí tak, aby stačily zvýšenému přenosu dat, se ukázalo být tím menším problémem v porovnání s přidáváním nových funkcí.

Konkrétněji: změnit strukturu tabulky nebo přidat indexy do databáze s více než 10–20 miliony řádků zaměstná databázi naplno po dobu několika hodin v kuse. Odstranění starých indexů zabere neméně času, a pokud je neodstraníte, sníží to výkon celého stroje, protože databáze bude dál číst a zapisovat do nepoužitých bloků při každém INSERTu, a bude si tak odsouvat z paměti to potřebné. Existují určité postupy operací, které můžete dělat, abyste se těmto problémům vyhnuli (jako nastavit nový index nejprve na slave stroji a pak prohodit slave a master), ale tyto procedury jsou velmi náročné a náchylné k chybám. Což nás v důsledku nutilo vyhýbat se přidávání takových funkcí, které by vyžadovaly změnu struktury nebo indexů. A protože se od doby, co jsme databázi rozdělili, staly „relační“ funkce jako třeba JOIN naprosto nepoužitelné, rozhodli jsme se porozhlédnout mimo svět relačních databází.

Existuje spousta databázových projektů, navržených tak, aby řešily problém ukládání dat s pružnou strukturou a vytváření indexů za běhu (například CouchDB) (psali jsme o ní na Zdrojáku – pozn. překl.). Bohužel žádný z nich není dostatečně používaný na velkých serverech, abychom se mohli někde inspirovat. V testech, které jsme četli a které jsme si udělali, se žádný z těchto projektů neukázal dostatečně stabilním či otestovaným do té míry, jakou potřebujeme. MySQL funguje. Nepoškozuje data. Replikace funguje. Známe její omezení. Máme rádi MySQL jako úložiště dat, ne kvůli tomu, že je to relační databáze.

Po jistých úvahách jsme se rozhodli implementovat „neschémové“ úložiště nad MySQL namísto vytvoření kompletního nového úložiště. V tomto článku se pokusím popsat nejvýraznější rysy tohoto systému. Zajímá nás, jak se s podobnými problémy vypořádávají jiné velké servery, a doufáme, že některé z našich úvah mohou být užitečné ostatním vývojářům.

Pokud vás naopak zajímá to, jak využít správně všech výhod, které MySQL nabízí, a pokud se chcete naučit správně používat MySQL jako relační databázi, přijďte na školení Akademie Root.cz s tématem Návrh a používání MySQL databáze, kde se dozvíte vše potřebné od návrhu až po samotné využití MySQL ve vašich projektech. Školení je vhodné jak pro důkladné seznámení se základy databází, tak pro využití MySQL u náročnějších projektů.

Přehled

Naše úložiště ukládá neschémové seznamy atributů (jako JSON objekty nebo Pythonské slovníky). Jediná vyžadovaná součást uloženého záznamu je identifikátor, který tvoří šestnáctibajtový UUID. Zbytek záznamu je z hlediska úložiště nezajímavý. Můžeme tedy snadno změnit „schéma“ prostým uložením nových atributů. (V originále „properties“, překlad „vlastnost“ by nebyl vhodný, proto budu používat „atributy“ – pozn. překl.)

Uložená data indexujeme pomocí indexových záznamů, uložených v oddělených MySQL tabulkách. Pokud chceme indexovat tři atributy v každém záznamu, budeme mít tři MySQL tabulky, pro každý indexovaný atribut jednu. Když budeme chtít přestat užívat index, prostě přestaneme do této tabulky zapisovat. Tabulku pak můžeme smazat. Když potřebujeme nový index, uděláme pro něj novou MySQL tabulku a spustíme asynchronní proces, který index na pozadí naplní, aniž by to mělo vliv na „živou“ službu.

Nakonec jsme skončili s větším počtem tabulek, než jsme měli předtím, ale přidávání a odstraňování indexů je teď snadné. Máme velmi dobře optimalizovaný proces, který vytváří nové indexy (zvaný „Čistič“). Ten dokáže rychle naplnit indexové tabulky, aniž by přitom „zboural“ celý web. Můžeme přidávat nové atributy a indexovat je v řádu dnů, nikoli v řádu týdnů, a nemusíme kvůli tomu prohazovat MySQL master a slave nebo dělat jiné děsivé akce.

Detaily

Naše entity jsou v MySQL uloženy v tabulce, která vypadá nějak takto:

CREATE TABLE entities (
    added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    id BINARY(16) NOT NULL,
    updated TIMESTAMP NOT NULL,
    body MEDIUMBLOB,
    UNIQUE KEY (id),
    KEY (updated)
) ENGINE=InnoDB;

Sloupec „ added_id“ je tu proto, že InnoDB ukládá řádky fyzicky v pořadí podle primárního klíče. Primární klíč s AUTO_INCREMENT  zajistí, že nově přidané záznamy jsou na disk zapisovány sekvenčně za starší záznamy, což napomáhá zápisu i čtení (nové záznamy bývají čteny mnohem častěji než starší záznamy, čemuž napomáhá i to, že stránky FriendFeed jsou řazeny v obráceném chronologickém pořadí). Těla záznamů jsou ukládána jako pythonské slovníky, serializované (pickled) a komprimované pomocí zlib.

Indexy jsou ukládány v oddělených tabulkách (v originále sharded – pozn. překl.). Když vytváříme nový index, vytvoříme novou tabulku, která obsahuje atributy, jež chceme indexovat. Kupříkladu typický záznam (entita) ve FriendFeedu může vypadat takto: 

{
    "id": "71f0c4d2291844cca2df6f486e96e37c",
    "user_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "feed_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "title": "We just launched a new backend system for FriendFeed!",
    "link": "http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
    "published": 1235697046,
    "updated": 1235697046,
}

Chceme indexovat atribut „ user_id“ v těchto záznamech, abychom mohli zobrazit stránku všech záznamů daného uživatele. Naše indexová tabulka bude tedy vypadat nějak takto:

CREATE TABLE index_user_id (
    user_id BINARY(16) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (user_id, entity_id)
) ENGINE=InnoDB;

Naše datové úložiště udržuje indexy automaticky, ve své režii. Pokud chceme se záznamy pracovat, můžeme použít nějaký takový kód (v Pythonu):

user_id_index = friendfeed.datastore.Index(
    table="index_user_id", properties=["user_id"], shard_on="user_id")
datastore = friendfeed.datastore.DataStore(
    mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
    indexes=[user_id_index])

new_entity = {
    "id": binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"),
    "user_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
    "feed_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
    "title": u"We just launched a new backend system for FriendFeed!",
    "link": u"http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
    "published": 1235697046,
    "updated": 1235697046,
}

datastore.put(new_entity)

entity = datastore.get(binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"))
entity = user_id_index.get_all(datastore, user_id=binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"))

Třída Index hledá atribut user_id ve všech záznamech a automaticky spravuje index v tabulce index_user_id. Protože je naše databáze dělená, udává argument shard_on klíč, podle kterého bude rozhodnuto o konkrétním umístění (zde podle čísla  user_id).

Můžete položit dotaz na index pomocí instance této třídy (viz user_id_index.get_all výše). Kód úložiště udělá potřebný „join“ mezi tabulkou „ index_user_id“ a tabulkou entit v Pythonu, a to tak, že nejprve požádá o seznam ID záznamů ze všech tabulek index_user_id ze všech dílčích databází, a pak vyzvedne tyto záznamy z tabulky entities.

Pokud chceme přidat další index, například indexovat odkazy (atribut link), vytvoříme opět novou tabulku:

CREATE TABLE index_link (
    link VARCHAR(735) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (link, entity_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Změníme inicializační kód datového úložiště tak, aby zohlednil nový index:

user_id_index = friendfeed.datastore.Index(
    table="index_user_id", properties=["user_id"], shard_on="user_id")
link_index = friendfeed.datastore.Index(
    table="index_link", properties=["link"], shard_on="link")

datastore = friendfeed.datastore.DataStore(

    mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
    indexes=[user_id_index, link_index])

Nakonec asynchronně naplníme index (tedy za normálního provozu) pomocí:

./rundatastorecleaner.py --index=index_link

Konzistence a atomicita

Konzistence je v situaci, kdy je databáze je rozdělena na více strojů a indexy pro entity mohou být uloženy na různých oddílech, často na jiných než entita samotná, docela problém. Co když zkolabuje proces předtím, než jsou zapsány všechny záznamy do všech indexových tabulek?

Vytvořit transakční protokol se stalo výzvou pro ty nejambicióznější vývojáře FriendFeedu, ale chtěli jsme zároveň udržet systém tak jednoduchý jak jen to je možné. Rozhodli jsme se porušit některá pravidla konzistence, jako například:

  • Indexy nemusí vždy odrážet aktuální stav záznamů
  • Směrodatný je obsah dat v tabulce entities

V důsledku toho zapisujeme nové záznamy do databáze v těchto krocích:

  1. Zapíšeme záznam do tabulky „ entities“ a využíváme přitom vlastností ACID, které nabízí InnoDB
  2. Zapíšeme indexy do všech indexových tabulek ve všech dílech databáze.

Když hledáme informace v indexu, tak víme, že nemusí být přesné (například mohou obsahovat staré hodnoty atributů, pokud při update neproběhl korektně krok 2). Abychom se ujistili, že nevracíme neplatná data, používáme indexy jen jako vodítko k tomu, abychom zjistili, jaké záznamy načíst, ale patřičné filtry aplikujeme znovu na načtené záznamy; nemůžeme totiž důvěřovat indexům jako autoritativnímu zdroji informací. Tedy:

  1. Přečteme entity_id ze všech tabulek podle dotazu
  2. Načteme záznamy z tabulky entities podle získaných ID
  3. Vyfiltrujeme (v Pythonu) všechny entity, u nichž hodnoty atributů neodpovídají podmínkám v dotazu.

Abychom se ujistili, že v indexech něco nebude chybět napořád a že nesrovnalosti budou opraveny, prochází proces „Čistič“, který jsem zmiňoval v předchozím textu, neustále tabulkou entit, zapisuje chybějící indexy a čistí staré a neplatné záznamy. Při práci postupuje od posledních změněných záznamů, takže nesrovnalosti v indexech jsou opraveny v praxi velmi rychle (během pár sekund).

Výkon

Optimalizovali jsme naše indexy a výsledkem jsme příjemně překvapeni. Výkon stoupl a latence je stabilní. Se systémem je navíc velmi snadné pracovat. (originálním článku jsou grafy, ukazující zlepšení stability výkonu, v kontextu tohoto článku na Zdrojáku nebyly nezbytné – pozn. překl.)

O autorovi: Autor textu, Bret Taylor, je spoluzakladatel služby FriendFeed a její bývalý CEO, nyní (po odkoupení FriendFeedu Facebookem) pracuje ve Facebooku jako produktový ředitel.

Poznámka překladatele

Na výše uvedený článek jsem narazil náhodou, když jsem hledal informace o rozumném, snadno použitelném a netradičním ORM systému pro PHP, který by fungoval jako persistentní úložiště objektů. Zvažoval jsem migraci k nějaké „čisté NoSQL“ databázi, ale problém je s tím, že NoSQL databáze nepatří do standardní nabídky webhostingů. Výše uvedený článek mi připadal jako velmi inspirativní, právě pro vývojáře, kteří by rádi zkusili přechod k NoSQL a využili přitom široce dostupné, známé a relativně spolehlivé MySQL databáze. Navíc přináší pohled autora opravdu velkého webu, což je zkušenost pro většinu vývojářů v ČR neznámá. Proto jsem se rozhodl článek přeložit, i když je už téměř rok starý, a nabídnout čtenářům Zdrojáku.

Martin Malý

Martin Maly

Začal programovat v roce 1984 s progra­movatelnou kalkulačkou. Pokračoval k BASICu, assembleru Z80, Forthu, Pascalu, Céčku, dalším assemblerům, před časem v PHP a teď je rád, že neprogramuje…

Školení Google+ pro firmy

DW - Školení PPC
  • Jak využít Google+ pro firemní komunikaci a marketing.
  • Čím se liší Google+ od Twitteru a Facebooku z pohledu firemního využití.
  • Jak využít Google+ v souladu s pravidly užívání.
  • Založení Google+ Page (Stránky) krok po kroku, včetně praktických tipů.

Detailní informace o školení Google+ »

Přehled názorů

Další zdroje?
Patrik Votoček (Vrtak-CZ) 6. 1. 2010 00:31
Nový
└ 
Re: Další zdroje?
Martin Malý 6. 1. 2010 02:06
Nový
Díky
Tomáš Kafka 6. 1. 2010 03:23
Nový
├ 
Re: Díky
Tomáš Kafka 6. 1. 2010 03:30
Nový
├ 
Re: Verzování
Jakub Vrána 6. 1. 2010 03:49
Nový
│
└ 
Re: Verzování
Tomáš Kafka 6. 1. 2010 04:11
Nový
│
 
├ 
Re: Verzování
Jakub Vrána 6. 1. 2010 04:26
Nový
│
 
├ 
Re: Verzování
Peter Helcmanovsky 6. 1. 2010 09:21
Nový
│
 
└ 
Re: Verzování
Inkvizitor 7. 1. 2010 20:09
Nový
└ 
Re: Díky
Michal Krause 6. 1. 2010 10:39
Nový
Neblokující ALTER
Jakub Vrána 6. 1. 2010 03:42
Nový
├ 
Re: Neblokující ALTER
aprilchild 6. 1. 2010 07:17
Nový
│
└ 
Re: Neblokující ALTER
Jakub Vrána 28. 9. 2010 01:04
Nový
└ 
Re: Neblokující ALTER
Martin Malý 6. 1. 2010 11:24
Nový
 
├ 
Re: Neblokující ALTER
Jan Kodera 6. 1. 2010 12:34
Nový
 
└ 
Re: Neblokující ALTER
Lukas 6. 1. 2010 14:32
Nový
 
 
└ 
Re: Neblokující ALTER
Martin Malý 6. 1. 2010 15:06
Nový
Klasicky zdroj pro NoSQL
aprilchild 6. 1. 2010 07:08
Nový
takže v zásadě nic nového
Martin Soušek 6. 1. 2010 09:18
Nový
├ 
Re: takže v zásadě nic nového
Palo 6. 1. 2010 09:31
Nový
└ 
Re: takže v zásadě nic nového
Martin Malý 6. 1. 2010 11:01
Nový
NoSQL?
X 6. 1. 2010 10:42
Nový
└ 
Re: NoSQL?
PM 6. 1. 2010 10:47
Nový
 
└ 
Re: NoSQL?
Martin Malý 6. 1. 2010 11:08
Nový
 
 
└ 
Re: NoSQL?
Petr Bíža 6. 1. 2010 11:39
Nový
 
 
 
└ 
Re: NoSQL?
Martin Malý 6. 1. 2010 11:40
Nový
 
 
 
 
└ 
Re: NoSQL?
Petr Bíža 6. 1. 2010 11:45
Nový
 
 
 
 
 
└ 
Re: NoSQL?
Martin Malý 6. 1. 2010 11:52
Nový
 
 
 
 
 
 
└ 
Re: NoSQL?
Petr Bíža 6. 1. 2010 12:11
Nový
 
 
 
 
 
 
 
└ 
Re: NoSQL?
Martin Malý 6. 1. 2010 15:11
Nový
Hracka
Ivan 6. 1. 2010 11:07
Nový
└ 
Re: Hracka
Martin Malý 6. 1. 2010 11:35
Nový
Fajn clanek
Tomas Vymazal 6. 1. 2010 12:14
Nový
Bulvární titulek
Jiří Knesl 6. 1. 2010 12:33
Nový
├ 
Re: Bulvární titulek
krteQ 6. 1. 2010 12:47
Nový
│
└ 
Re: Bulvární titulek
backup 6. 1. 2010 13:08
Nový
│
 
├ 
Re: Bulvární titulek
Jiří Knesl 6. 1. 2010 13:20
Nový
│
 
│
└ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 13:26
Nový
│
 
│
 
└ 
Re: Bulvární titulek
Jiří Knesl 6. 1. 2010 13:31
Nový
│
 
│
 
 
├ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 13:49
Nový
│
 
│
 
 
│
└ 
Re: Bulvární titulek
Palo 6. 1. 2010 14:11
Nový
│
 
│
 
 
│
 
├ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 14:12
Nový
│
 
│
 
 
│
 
│
├ 
Re: Bulvární titulek
Palo 6. 1. 2010 14:27
Nový
│
 
│
 
 
│
 
│
│
└ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 14:31
Nový
│
 
│
 
 
│
 
│
│
 
└ 
Re: Bulvární titulek
Palo 6. 1. 2010 15:06
Nový
│
 
│
 
 
│
 
│
└ 
Re: Bulvární titulek
Petr 7. 1. 2010 02:56
Nový
│
 
│
 
 
│
 
└ 
Co se učí a co dělá.
Franta Kučera 6. 1. 2010 23:20
Nový
│
 
│
 
 
│
 
 
├ 
Re: Co se učí a co dělá.
Martin Malý 6. 1. 2010 23:28
Nový
│
 
│
 
 
│
 
 
└ 
Re: Co se učí a co dělá.
backup 6. 1. 2010 23:50
Nový
│
 
│
 
 
└ 
Re: Bulvární titulek
Jirka Kosek 6. 1. 2010 14:59
Nový
│
 
│
 
 
 
├ 
Re: Bulvární titulek
Palo 6. 1. 2010 15:14
Nový
│
 
│
 
 
 
│
├ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 15:25
Nový
│
 
│
 
 
 
│
│
└ 
Re: Bulvární titulek
Palo 6. 1. 2010 15:37
Nový
│
 
│
 
 
 
│
│
 
└ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 16:20
Nový
│
 
│
 
 
 
│
├ 
Re: Bulvární titulek
Jirka Kosek 6. 1. 2010 23:24
Nový
│
 
│
 
 
 
│
│
├ 
Re: Bulvární titulek
Jakub Vrána 7. 1. 2010 00:15
Nový
│
 
│
 
 
 
│
│
│
└ 
Re: Bulvární titulek
Jirka Kosek 7. 1. 2010 00:25
Nový
│
 
│
 
 
 
│
│
│
 
└ 
Adresy ve fakturách
Franta Kučera 7. 1. 2010 11:33
Nový
│
 
│
 
 
 
│
│
├ 
Faktury
Franta Kučera 7. 1. 2010 11:13
Nový
│
 
│
 
 
 
│
│
├ 
Re: Bulvární titulek
Karel Minařík 7. 1. 2010 11:13
Nový
│
 
│
 
 
 
│
│
│
├ 
Re: Bulvární titulek
Pavel Stěhule 7. 1. 2010 11:47
Nový
│
 
│
 
 
 
│
│
│
│
└ 
Re: Bulvární titulek
Pavel Stěhule 7. 1. 2010 11:51
Nový
│
 
│
 
 
 
│
│
│
│
 
└ 
Re: Bulvární titulek
Almad 7. 1. 2010 12:49
Nový
│
 
│
 
 
 
│
│
│
├ 
Pokrok
Franta Kučera 7. 1. 2010 11:54
Nový
│
 
│
 
 
 
│
│
│
│
└ 
Re: Pokrok
Palo 7. 1. 2010 14:27
Nový
│
 
│
 
 
 
│
│
│
│
 
└ 
Re: Pokrok
Franta Kučera 7. 1. 2010 14:58
Nový
│
 
│
 
 
 
│
│
│
│
 
 
└ 
Re: Pokrok
Palo 7. 1. 2010 15:48
Nový
│
 
│
 
 
 
│
│
│
└ 
Re: Bulvární titulek
Lukáš Konarovský 7. 1. 2010 20:58
Nový
│
 
│
 
 
 
│
│
│
 
└ 
Fakturoid
Franta Kučera 7. 1. 2010 21:31
Nový
│
 
│
 
 
 
│
│
└ 
Re: Bulvární titulek
Palo 7. 1. 2010 13:18
Nový
│
 
│
 
 
 
│
└ 
Re: Bulvární titulek
Franta Kučera 6. 1. 2010 23:36
Nový
│
 
│
 
 
 
├ 
Re: Bulvární titulek
Franta Kučera 6. 1. 2010 23:32
Nový
│
 
│
 
 
 
├ 
Re: Bulvární titulek
HolyHop 7. 1. 2010 12:48
Nový
│
 
│
 
 
 
└ 
Re: Bulvární titulek
Palo 7. 1. 2010 13:46
Nový
│
 
│
 
 
 
 
├ 
Re: Bulvární titulek
Michal Krause 7. 1. 2010 13:51
Nový
│
 
│
 
 
 
 
│
├ 
Re: Bulvární titulek
Palo 7. 1. 2010 14:46
Nový
│
 
│
 
 
 
 
│
└ 
Re: Bulvární titulek
koles 7. 1. 2010 22:07
Nový
│
 
│
 
 
 
 
└ 
Re: Bulvární titulek
Martin Malý 7. 1. 2010 13:53
Nový
│
 
└ 
Re: Bulvární titulek
krteQ 6. 1. 2010 13:21
Nový
├ 
Re: Bulvární titulek
Jan Kodera 6. 1. 2010 12:53
Nový
│
└ 
Re: Bulvární titulek
backup 6. 1. 2010 13:13
Nový
│
 
└ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 13:14
Nový
│
 
 
└ 
Re: Bulvární titulek
Matyáš Novák 6. 1. 2010 14:55
Nový
│
 
 
 
├ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 14:59
Nový
│
 
 
 
│
└ 
Re: Bulvární titulek
Matyáš Novák 6. 1. 2010 16:32
Nový
│
 
 
 
│
 
├ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 16:40
Nový
│
 
 
 
│
 
└ 
Re: Bulvární titulek
Palo 6. 1. 2010 16:50
Nový
│
 
 
 
└ 
Re: Bulvární titulek
Mard 6. 1. 2010 15:18
Nový
│
 
 
 
 
└ 
Re: Bulvární titulek
A 7. 1. 2010 11:42
Nový
├ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 13:22
Nový
├ 
Re: Bulvární titulek
Karel Minařík 6. 1. 2010 14:51
Nový
│
└ 
Re: Bulvární titulek
Jiří Knesl 6. 1. 2010 16:56
Nový
│
 
├ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 17:02
Nový
│
 
├ 
Re: Bulvární titulek
Pavel Stěhule 6. 1. 2010 17:12
Nový
│
 
├ 
Re: Bulvární titulek
Palo 6. 1. 2010 17:19
Nový
│
 
│
└ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 17:20
Nový
│
 
│
 
└ 
Re: Bulvární titulek
Jiří Knesl 6. 1. 2010 20:53
Nový
│
 
│
 
 
├ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 21:22
Nový
│
 
│
 
 
├ 
Re: Bulvární titulek
Palo 6. 1. 2010 21:32
Nový
│
 
│
 
 
├ 
Re: Bulvární titulek
David Grudl 6. 1. 2010 23:13
Nový
│
 
│
 
 
│
└ 
Re: Bulvární titulek
Martin Malý 6. 1. 2010 23:14
Nový
│
 
│
 
 
│
 
├ 
Re: Bulvární titulek
Jaroslav Moravec 7. 1. 2010 01:10
Nový
│
 
│
 
 
│
 
└ 
Re: Bulvární titulek
Palo 7. 1. 2010 09:48
Nový
│
 
│
 
 
├ 
Re: Bulvární titulek
Martin Soušek 7. 1. 2010 09:26
Nový
│
 
│
 
 
│
├ 
Re: Bulvární titulek
David Grudl 7. 1. 2010 10:54
Nový
│
 
│
 
 
│
└ 
Re: Bulvární titulek
Jiří Knesl 7. 1. 2010 12:03
Nový
│
 
│
 
 
├ 
Re: Bulvární titulek
Jan Samek 7. 1. 2010 11:00
Nový
│
 
│
 
 
│
└ 
Re: Bulvární titulek
Jiří Knesl 7. 1. 2010 12:49
Nový
│
 
│
 
 
│
 
└ 
Re: Bulvární titulek
Jan Samek 7. 1. 2010 22:12
Nový
│
 
│
 
 
├ 
Re: Bulvární titulek
Langi 7. 1. 2010 11:11
Nový
│
 
│
 
 
└ 
Re: Bulvární titulek
Vojta 7. 1. 2010 14:44
Nový
│
 
└ 
Re: Bulvární titulek
Karel Minařík 6. 1. 2010 17:22
Nový
│
 
 
├ 
Re: Bulvární titulek
Jiří Knesl 6. 1. 2010 19:56
Nový
│
 
 
│
├ 
Re: Bulvární titulek
Aleš Roubíček 7. 1. 2010 07:30
Nový
│
 
 
│
└ 
Re: Bulvární titulek
Karel Minařík 7. 1. 2010 10:59
Nový
│
 
 
├ 
Re: Bulvární titulek
Aleš Roubíček 7. 1. 2010 07:27
Nový
│
 
 
│
└ 
Re: Bulvární titulek
Karel Minařík 7. 1. 2010 10:27
Nový
│
 
 
│
 
└ 
Re: Bulvární titulek
Aleš Roubíček 7. 1. 2010 11:38
Nový
│
 
 
│
 
 
└ 
Re: Bulvární titulek
Karel Minařík 7. 1. 2010 16:25
Nový
│
 
 
└ 
Re: Bulvární titulek
Pavel Stěhule 7. 1. 2010 09:00
Nový
│
 
 
 
└ 
Re: Bulvární titulek
Martin Malý 7. 1. 2010 13:00
Nový
│
 
 
 
 
├ 
Re: Bulvární titulek
Pavel Stěhule 7. 1. 2010 13:18
Nový
│
 
 
 
 
│
├ 
Webové aplikace
Franta Kučera 7. 1. 2010 13:34
Nový
│
 
 
 
 
│
│
└ 
Re: Webové aplikace
Martin Malý 7. 1. 2010 13:43
Nový
│
 
 
 
 
│
│
 
└ 
Re: Webové aplikace
Franta Kučera 7. 1. 2010 14:21
Nový
│
 
 
 
 
│
└ 
Re: Bulvární titulek
Martin Malý 7. 1. 2010 13:38
Nový
│
 
 
 
 
│
 
└ 
Re: Bulvární titulek
Pavel Stěhule 7. 1. 2010 13:56
Nový
│
 
 
 
 
│
 
 
├ 
Re: Bulvární titulek
Martin Malý 7. 1. 2010 14:03
Nový
│
 
 
 
 
│
 
 
└ 
Re: Bulvární titulek
Martin Malý 7. 1. 2010 14:03
Nový
│
 
 
 
 
└ 
Re: Bulvární titulek
Palo 7. 1. 2010 13:26
Nový
│
 
 
 
 
 
└ 
Re: Bulvární titulek
Martin Malý 7. 1. 2010 13:31
Nový
│
 
 
 
 
 
 
└ 
Re: Bulvární titulek
Palo 7. 1. 2010 15:14
Nový
│
 
 
 
 
 
 
 
└ 
Re: Bulvární titulek
backup 7. 1. 2010 15:32
Nový
│
 
 
 
 
 
 
 
 
└ 
Re: Bulvární titulek
Palo 7. 1. 2010 16:22
Nový
└ 
Re: Bulvární titulek
jos 6. 1. 2010 17:37
Nový
vytvareni/odstranova ni indexu
krteQ 6. 1. 2010 12:42
Nový
└ 
Re: vytvareni/odstranova ni indexu
Martin Malý 6. 1. 2010 13:29
Nový
NoSql ? To už tu jednou bylo.
Lukas 6. 1. 2010 18:26
Nový
└ 
Re: NoSql ? To už tu jednou bylo.
Palo 6. 1. 2010 19:18
Nový
 
└ 
Re: NoSql ? To už tu jednou bylo.
Lukas 6. 1. 2010 22:55
Nový
Know-jow/Technologie /Cas/Zdroje - to je oč tu běží
Michal Blaha 7. 1. 2010 00:15
Nový
My programátoři si rádi život komplikujeme
David Grudl 7. 1. 2010 00:24
Nový
└ 
Re: My programátoři si rádi život komplikujeme
Palo 7. 1. 2010 13:09
Nový
Kladivem na sroubecek
Bob 7. 1. 2010 09:57
Nový
├ 
Re: Kladivem na sroubecek
C 7. 1. 2010 11:54
Nový
└ 
Re: Kladivem na sroubecek
Almad 7. 1. 2010 12:34
Nový
 
└ 
Re: Kladivem na sroubecek
Martin Malý 7. 1. 2010 12:46
Nový
 
 
├ 
Re: Kladivem na sroubecek
Almad 7. 1. 2010 12:59
Nový
 
 
└ 
Re: Kladivem na sroubecek
Franta Kučera 7. 1. 2010 13:10
Nový
 
 
 
└ 
Re: Kladivem na sroubecek
Almad 7. 1. 2010 16:21
Nový
Pozor na to!
Vojta 7. 1. 2010 14:29
Nový
└ 
Re: Pozor na to!
Franta Kučera 7. 1. 2010 15:12
Nový
Hezký článek
Sebastian 8. 1. 2010 17:45
Nový
relační model vs. noSQL
Martin Kavalec 10. 1. 2010 14:17
Nový
Článek k tématu
Jakub Vrána 26. 1. 2010 15:26
Nový
       

Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.

Zasílat nově přidané příspěvky e-mailem