ú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?


  1. 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
  2. Otestovat více řešení!
  3. Nasadit
    je-li to opětovný cyklus -  může dojít k nasazení jiné db 
  4. 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

    Mongo - rady+php

    • dát si pozor na datové typy (Int vs. String, NumberInt vs. NumberLong)
    • datumy (Date/ISODate/Unixtime), dělat index

    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)
    UPDATE users SET age = age + 3 WHERE status = "A"
    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 

    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
    vs.
    require 'json/ext'
    require 'couchrest'
    @db = "http://..."
    
    def foo
    	CouchRest.put(@db, doc)
    def
    rozdíl 50-100%

    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
    ...

    Sledovat Karmiho (@)

    zdroje a Odkazy co mohou pomoci




    Dotazy ?




    Ondřej Kryl
    @toust

    Nosql

    By Ondřej TouSt Kryl