úvod do
Nosql
Ondřej Kryl
Historie
1960-70:
- IBM: (MultiValue) [B+tree]
- AT&T: (DBM) [file-based hash]
1980:
- Ing Direct: (GT.M) [Prvni Key-value]
- BerkeleyDB (BSD), LotusNotes [document db]
1990: utlum + SQL DB
2000: rozmach
-
Neo4j (2000), Memcache (2003), Google Bigtable (2004), CouchDB (2005), Amazon Dynamo (2007), MongoDB (2007), Cassandra (2008), Redis/Riak/Hbase (2009)
výhody
-
Velké objemy dat
(strukturovaných, částečně strukturovaných, nestrukturovaných)
-
Agilní sprinty, rychlé iterace
-
Objektově orientované programování
snadněji ovladatelné a flexibilní
-
Efektivní, škálovatelná architektura místo drahé monolitické
- Ekonomičnost a open-source
Nevýhody
-
Májí velmi úzké zaměření
-
nelze je cpát všude!
-
NoSQL DB ještě nedozrály
-
vývojáři, administrátoři, znalosti/dovednosti
-
Standardizace
- Open-source a podpora
- žádná nosql db není stejná
-
jiné zaměření, struktura dat
-
Výkon a škálování vs. konzistence
Typy
typy
Key-Value
Redis, Oracle NoSQL DB, Kyoto Cabinet
(Hierarchical/Ordered/Cache/Consistent) Key-Value
Memcache, Amazon Dynamo, Riak, Voldemort, GT.M
BigTable
Apache HBase, Apache Cassandra
Document base
MongoDb, CouchDb, CouchBase, Lotus notes
Full Text Search
Apache Lucene, Apache Solr, ElasticSearch
Graph Databases
neo4j, FlockDB
kterou db? Kde máme jistotu?
Key-value:
Enumerable - cache, rychlost čtění hodnoty (př.: userID_messageID)
Memcache , Redis
BigTable:
Index Table - userID, Composite Key Index - State:City:UserID | State:*
Hbase, Cassandra, Hadoop, Amazon SimpleDb
Fulltext engine:
ElasticSearch
Index table
(bigtables)
Composite key index
(bigtables)
WHERE
state=
"CA:*"
WHERE
city=
"CA:San Francisco*"
Jak vybrat mezi ostatními db?
- Ptát se! Zda-li někdo řešil konkrétní případ nad NoSQL
Ano? jásejte, víte za kým jít / s kým diskutovat - Otestovat více řešení!
- Nasadit
je-li to opětovný cyklus - může dojít k nasazení jiné db - Nastane problém
a) vyřeším skrze - dokumentaci, google, stackoverflow, metoda pokus-omyl
b) nevyřeším - zpět na krok 1.
Na co myslet / dbát?
Být připraven (v aplikaci) na rychlé změny.
Dbát na architekturu aplikace.
Nejlépe: domain driven design!
a udělejte z toho přednost!
Entita
class Entity_Profile extends FM_Model_Entity
{
protected $_data = array(
'profileId' => null,
'display' => '',
'about_me' => '',
'state' => '',
'education' => '',
...
);
...
}
Mapper
class Mapper_Profile extends FM_Model_Mapper {
protected $map = array(
'profileId' => '_id',
);
public function insert(Entity_Profile $entity){
if(!$this->getDbTable()->isExist($entity->getPrimaryName(), $entity ))
$this->getDbTable()->insert($entity->toArray());
else return false;
}
...
}
Repository-Table
class DbTable_Profile extends DbTable_Mongo {
CONST NAME = 'profile';
private $_collection = NULL;
public function __construct(){
parent::__construct();
$this->setCollection( self::NAME );
}
public function insert(array $data = array()){
return $this->getCollection()->insert($data);
}
public function update($filtre=array('_id' => NULL), array $data = array()){
return $this->getCollection()->update($filtre, array('$set' => $data));
}
...
}
repository source - Mongo
class DbTable_Mongo {
const NAME = 'dbxyz';
protected $_db;
public function __construct(){
try {
$connection = new Mongo();
$this->_db = $connection->{self::NAME};
} catch (MongoConnectionException $e) {
$this->getLog()->log($e, Zend_Log::ERR);
die(...); // ? // throw new NosqlException(...)
}
}
...
Controller / service
//find - output Entity_Profile
$mapper = new Mapper_Profile();
$this->view->row = $mapper->find(array("profileId" => $id));
if(!isset($this->view->row)) {
// flashMsg
// redirect
}
//insert
$mapper = new Mapper_Profile();
$entity = new Entity_Profile($form->getValues());
if($mapper->insert($entity)) {
// flashMsg
// redirect
}
Mongodb
(document-base)
Kdy?
- dynamické dotazy
-
dáváte-li přednost indexům před map/reduce funkcemi
- pokud se mění často data
Proč?
master/slave replikace
velká komunita
dobrá dokumentace
nativní knihovny
má "blíže" k relační db
velká komunita
dobrá dokumentace
nativní knihovny
má "blíže" k relační db
Mongo - rady+php
- dát si pozor na datové typy (Int vs. String, NumberInt vs. NumberLong)
- datumy (Date/ISODate/Unixtime), dělat index
-
phpMyAdmin =>
Rockmongo
-
PHP extension
(PECL, balíčky v repositářích)
-
Shanty_Mongo
- pro Zend Framework
- Symfony2? Doctrine MongoDB ODM
Mongo - find
SELECT * FROM users WHERE
( age > 25 AND age <= 50 ) AND user_id LIKE "bc%"
ORDER BY user_id ASC LIMIT 5, 10
db.users.find(
{ $and: [
{ age: { $gt: 25, $lte: 50 } } ,
{ user_id: /^bc/ }
]
}
).sort( { user_id: -1 } ).limit(5).skip(10)
db.users.update(
{ status: "A" } ,
{ $inc: { age: 3 } },
{ multi: true }
)
couchdb
(document-base)
Kdy?
-
Kde je důležité verzování
-
Hromadění / shromažďování dat
- méně se měnící data, na kterých spouštíme předdefinované dotazy - Využití: CRM, CMS, Multi-site
Proč?
master-master replikace
map/reduce funkce (javascript)
fulltext skrze ElasticSearch river
Pozor !
CouchDb
vs.
BigCouch
Clustering, kompatibilní s CouchDb, dojde ke sloučení s CouchDb
vs.
CouchBase
Komerční (podpora), Auto-sharding cluster , Memcached-compatible API ,
HTTP API , CouchApps
HTTP API , CouchApps
Rady
-
Použivat co nejjednodužší přístup (i knihovny)
-
single doc / bulk insert
PHP
Ruby
- Nikdy nepoužívat Couchrest_model !!!
- Opatrnost při použití Couchrest
- Používat https pouze když je to nezbytné
- Používat json/ext
Ruby - couchrest
require 'couchrest'
def foo
db = CouchRest.database!("http://..")
db.save_doc(doc)
def
require 'json/ext'
require 'couchrest'
@db = "http://..."
def foo
CouchRest.put(@db, doc)
def
Couchdb - ukázka 1
Map/Reduce:
INPUT:
IP | Traffic |
---|---|
192.168.1.11 | 3022 |
192.168.1.11 | 53022 |
192.168.1.13 | 955332 |
192.168.1.11 | 1024 |
192.168.1.13 | 952 |
Couchdb - ukázka 1
Map/Reduce:
MAPPER => REDUCER:
IP | Traffic |
---|---|
192.168.1.11 | 3022 |
53022 | |
1024 | |
192.168.1.13 | 955332 |
952 |
Couchdb - ukázka 1
Map/Reduce:
AFTER REDUCE:
IP | Traffic |
---|---|
192.168.1.11 | 57068 |
192.168.1.13 | 956284 |
Couchdb - ukázka 2
SELECT recipeid FROM recipe JOIN ingredients on ingredients.recipeid = recipe.recipeid
WHERE (ingredient = 'carrot' OR ingredient = 'rice') AND totaltime = 20
Map/Reduce:
function(doc, meta)
{
if (doc.ingredients)
{
for (i=0; i < doc.ingredients.length; i++)
{
for (j=0; j < doc.ingredients.length; j++)
{
emit([doc.ingredients[i].ingredient, doc.ingredients[j].ingredient, recipe.totaltime], null);
}
}
}
}
KEY | VALUE |
---|---|
["carrot", "rice", "1"] | NULL |
["carrot", "rice", "5"] | NULL |
["carrot", "pasta", "2"] | NULL |
["carrot", "rice", "25"] | NULL |
Query:
?startkey=["carrot","rice",0]&key=["carrot","rice",20]
ElasticSearch
(fulltext engine)
lze použít i jako document-base storage
dokumentace a komunita roste
memcached
geolokace
fuzzy logika
asynchroní replikace
...
zdroje a Odkazy co mohou pomoci
-
http://kkovacs.eu/cassandra-vs-mongodb-vs-couchdb-vs-redis
-
http://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/
-
http://seancribbs.com/tech/2009/09/28/modeling-a-tree-in-a-document-database/
- http://probablyprogramming.com/2008/07/04/storing-hierarchical-data-in-couchdb
- http://www.couchbase.com/documentation
-
http://bigcouch.cloudant.com
-
http://docs.mongodb.org/manual/
-
http://www.slideshare.net/gabriele.lana/couchdb-vs-mongodb-2982288
Dotazy ?
Ondřej Kryl
@toust
Nosql
By Ondřej TouSt Kryl
Nosql
- 1,976