Replikace
Replikace je způsob, jak mezi dvěma databázemi (databází je v tomto případě myšlena konkrétní přihrádka, nikoli celý server) synchronizovat data. Určí se dvě databáze – zdrojová a cílová – a všechna data, co v cílové chybí, jsou do ní ze zdrojové překopírována. Je při tom využíváno ID dokumentů a jejich revizí. Zdrojová databáze může být lokální nebo vzdálená a stejně tak může být i cílová lokální nebo vzdálenou. Podmínkou je, že obě databáze musí existovat (i když nemusí obsahovat ani jeden dokument).
Řekněme, že chceme vytvořit repliku databáze s alby (a předpokládejme, že tam nějaká alba jsou). Replika bude lokální, takže pro ni vytvoříme databázi:
$ curl -X PUT http://localhost:5984/alba-replika/
{"ok":true}
A poté spustíme samotnou replikaci (při opravdovém spuštění uvidíte místo tří teček údaje, týkající se replikace – kdy začala, skončila apod.):
$ curl -X POST -d '{"source":"alba","target":"alba-replika"}' http://localhost:5984/_replicate
{"ok":true,...}
Replikace je jeden z důvodů, proč CouchDB nemůže sloužit jako verzovací systém – při replikacích se kopírují jen ta nejčerstvější data.
Vzdálená databáze se od lokální liší tím, že určíme, na jakém serveru se nachází – http://jiny-server:5984/jina-databaze/
.
Stlačování (compaction)
MVCC (Multiversion Concurrency Control) má výhodu v tom, že nejsou potřeba žádné zámky, takže klienti nemusí na nic čekat. Ale jelikož se pokaždé vytváří úplně nová kopie dokumentu (a dokument může být docela velký), CouchDB si ukousne hezký kousek místa na disku. Vzhledem k tomu, že místo je levné, tak to většinou není až takový problém. Ale zase není neomezené. A proto je tu stlačování, při kterém se zahodí všechny nepotřebné (staré) revize a tím se uvolní místo.
Pokud byste měli velkou databázi alb, a ta se hodně měnila, starých revizí se zbavíte pomocí:
$ curl -X POST http://localhost:5984/alba/_compact
{"ok":true}
Pohledy (views)
Pohledy jsou asi tou nejzáludnější věcí, na kterou u CouchDB narazíte, a tohle bude jen takové lehké nakousnutí. Pohledy jsou způsobem, jak dát nestrukturovaným datům uloženým v dokumentech strukturu.
Abychom měli nějaká data, se kterými se bude dát pracovat, vytvořme si takový menší adresář osob:
$ curl -X PUT http://localhost:5984/adresar/
{"ok":true}
$ curl -X PUT -d '{"jmeno":"Pavel"}' http://localhost:5984/adresar/4835de71c4900920222793a70f5ffd9f
{"ok":true,"id":"4835de71c4900920222793a70f5ffd9f","rev":"1-77c126bcd8ca8702f3f7d9aa6e9f0fae"}
$ curl -X PUT -d '{"jmeno":"Pepa"}' http://localhost:5984/adresar/f9767dc49c498129ab71b33dccee06d0
{"ok":true,"id":"f9767dc49c498129ab71b33dccee06d0","rev":"1-edd7e3f5f0e91940c7f633052170ef21"}
$ curl -X PUT -d '{"jmeno":"Vašek"}' http://localhost:5984/adresar/38d6642e9f4f80790e41351e80067285
{"ok":true,"id":"38d6642e9f4f80790e41351e80067285","rev":"1-595c448e9755c9b1183839d6a9c73d08"}
$ curl -X PUT -d '{"jmeno":"Honza"}' http://localhost:5984/adresar/7695e6a0dc219ec16c311e4eaa19483d
{"ok":true,"id":"7695e6a0dc219ec16c311e4eaa19483d","rev":"1-e98255a638f0f7874fe24e37c11e54a4"}
$ curl -X PUT -d '{"jmeno":"Marek"}' http://localhost:5984/adresar/5747a709954fe6ae95bac69ed7090af1
{"ok":true,"id":"5747a709954fe6ae95bac69ed7090af1","rev":"1-9efd5be58372c54b03eb5da604a77018"}
Teď se už dostaneme k Futonu, tedy ke grafickému rozhraní CouchDB. Otevřte si adresu http://localhost:5984/_utils/. Měli byste vidět něco jako:
Nás zajímá databáze adresar
, takže na ni klikneme:
Jsou tu jednotlivé dokumenty. Pokud byste na některý z nich klikli, dostanete se na stránku, kde můžete dokument upravovat, přidávat a mazat přílohy atp. Ale nás zajímají pohledy, takže změníme zobrazení na dočasné pohledy:
Jak Futon správně varuje, dočasné pohledy by se neměly používat v produkčním nasazení, jelikož se při každém zavolání pohledu musí celý přepočítat. Na chvilku si s tím však vystačíme.
Jeden pohled bychom tam měli mít připravený (jako na obrázku), takže ho spustíme pomocí tlačítka Run. Pokud znáte styl zpracování dat pomocí MapReduce, asi je vám jasné, proč to dopadlo, jak to dopadlo. Funkce map je volána pro každý dokument v databázi a emit()
vypustí pod daným klíčem daná data. V tomto případě je vždy klíčem null
a data jsou celý dokument. Ale pomocí různých podmínek můžete vypustit všelijaká data pod rozličnými klíči. Také se nemusíte omezovat jen na jedno volání emit()
uvnitř map funkce – můžete emit()
zavolat milionkrát a nemusíte ho také zavolat vůbec.
Kdybychom chtěli získat všechny dokumenty z databáze, funkce map
by mohla vypadat následovně:
function(doc) {
emit(doc._id, {"rev" : doc._rev});
}
Jako klíč je ID dokumentu a data jsou jeho revize. Popravdě řečeno takový pohled pro vás CouchDB připravuje automaticky a máte ho k dispozici na /<databáze>/_all_docs
. Pro databázi adresar
by to tedy byla adresa http://localhost:5984/…ar/_all_docs. Pokud byste rovnou chtěli získat i data dokumentů, přidejte query parametr ?include_docs=true
– http://localhost:5984/…ar/_all_docs?….
Ale chtělo by to taky nějaký užitečný pohled. Jako map funkci zadejte tedy:
function(doc) {
emit(doc.jmeno, null);
}
Co z toho bude? Spustíte-li pohled pomocí Run, uvidíte, že jsme na místě klíčů dostali jména v adresáři a na místě hodnot null
– přesně to, co vypustí funkce emit()
. Bystřejší už vidí, že řádky jsou seřazeny abecedně podle klíče.
Pohled si uložíme. Klikněte tedy na Save As…. Pokud se chcete držet článku, jako Design document zadejte adresar
a jako View name podle-jmena
. Máte stálý (permanentní) pohled a na ten se jednoduše můžete dotazovat pomocí HTTP metodou GET
– http://localhost:5984/…/podle-jmena:
{"total_rows":5,"offset":0,"rows":[
{"id":"7695e6a0dc219ec16c311e4eaa19483d","key":"Honza","value":null},
{"id":"5747a709954fe6ae95bac69ed7090af1","key":"Marek","value":null},
{"id":"4835de71c4900920222793a70f5ffd9f","key":"Pavel","value":null},
{"id":"f9767dc49c498129ab71b33dccee06d0","key":"Pepa","value":null},
{"id":"38d6642e9f4f80790e41351e80067285","key":"Vau0161ek","value":null}
]}
Vidíte výstup podobný tomu z _all_docs
. Je zde počítadlo total_rows
oznamující, kolik řádků očekávat; offset
říkající, kolik jich bylo vynecháno; a pole rows
, kde jsou již samotná data.
Pokud do adresy přidáte query parametr ?skip=4
, čtyři řádky budou přeskočeny a dostanete jenom:
{"total_rows":5,"offset":4,"rows":[
{"id":"38d6642e9f4f80790e41351e80067285","key":"Vau0161ek","value":null}
]}
Pomocí parametru descending
můžete určit, mají-li se řádky řadit vzestupně ( descending=false
, výchozí), či sestupně ( descending=true
). Parametry se samozřejmě dají různě kombinovat – ?skip=4&descending=true
:
{"total_rows":5,"offset":4,"rows":[
{"id":"7695e6a0dc219ec16c311e4eaa19483d","key":"Honza","value":null}
]}
Parametr limit
zase určuje, kolik maximálně řádků se smí vrátit (ve výchozím stave bez limitu). Poté můžete ještě určit, jaký konkrétní klíč chcete pomocí key
. A startkey
a endkey
dávají možnost zadat rozsah klíčů. Chcete všechny kontakty od P? Máte je mít:
{"total_rows":5,"offset":2,"rows":[
{"id":"4835de71c4900920222793a70f5ffd9f","key":"Pavel","value":null},
{"id":"f9767dc49c498129ab71b33dccee06d0","key":"Pepa","value":null}
]}
Existuje ještě více parametrů, podle kterých můžete z pohledů vybírat řádky, které chcete. A v budoucnu možná ještě další přibudou. Všechny byste je měli najít na wiki.
Mnoho lidí se bojí, že jak CouchDB nemá schéma, jak to není všechno hezky v tabulce, tak nad daty ztratí veškerou kontrolu a všechno budou muset počítat v aplikaci (což se povětšinou nevyplatí). Pohledy CouchDB jsou důkazem, že to pravda není. Tohle bylo navíc jen takové zahřívací kolo – pravá legrace přichází, když se do všeho začnou míchat reduce funkce.
Stálé pohledy jsou předpočítané, resp. se počítají při prvním zavolání, takže žádné propastné prodlevy při dotazech na pohledy nehrozí – opět se (stejně jako u MVCC) obětovává místo ve prospěch rychlosti.
Na závěr
Toto bylo takové malé představení dokumentově orientované databáze CouchDB. Osobně mě někdy relační databáze docela vytáčejí, že něco „nejde“ udělat tak, jak bych si zrovna představoval (ano, jen do mě s tím, že to je jen kvůli tomu, že to neumím), tak se porozhlížím po jiných řešeních ukládání dat. CouchDB mě zaujala.
I když se přímo v dokumentaci píše, že CouchDB není relační databáze, ani náhrada za relační databáze, v článku jsem se srovnání právě s relačními databázemi, vzhledem k rozšířenosti a známosti jejich modelu mezi programátory, nemohl vyhnout. Myslím, že pro některé aplikace je CouchDB důstojnou alternativou relačních databází. Pro některé možná i vhodnějším řešením.
Doufám, že jsem CouchDB neudělal medvědí službu a tento článek vás zaujal alespoň natolik, že se třebas podíváte na její domovské stránky. Hlavní zdrojem inspirace pro příklady použité v článku je připravovaná kniha CouchDB: The Definitive Guide.
Přehled komentářů