Cassandra Un moteur in-memory d’écriture Plan Caractéristiques principales Ring LSM-Tree Modèle de données Insertion massive des données Interrogation des données Nouveau paradigme: in-memory Lexique Un cluster Cassandra est appelé ring: il fonctionne en mode peer-topeer, chaque nœud du ring pouvant traiter toute demande d’un client ( absence de relation maître-esclave ) Un nœud du ring appelé par un client en tant que coordinateur est capable de lire ou d’écrire des données d’une table ( ou famille de colonnes ) réparties sur plusieurs noeuds ( architecture de type shared-nothing ) Chaque table a ses données répliquées n fois sur les nœuds du cluster. Cassandra optimise l’écriture des données via une table en mémoire appelée Memtable . Les écritures disques se font de manière asynchrone dans une Sstable ( Sorted String Table ) Caractéristiques Solution libre de la fondation Apache développée initialement par Facebook Distribution Datastax ( Community + Enterprise ) Ecrit en Java SGBD orienté colonne => clé-valeur ( valeur = ensemble de colonnes ) Système distribué en mode peer-to-peer Caractéristiques Cassandra 2.0 CQL, système d’interrogation de la base, surcouche sql => client cqlsh à privilégier au détriment de cassandra-cli orienté colonne Liste des drivers clients: Java, C#, Python Pas de locking en cas de mises à jour concurrentes => si plusieurs clients modifient les mêmes colonnes de manière concurrente, seule les modifications les plus récentes seront conservées. Une table Cassandra Caractéristiques Atomicité assurée au niveau de la ligne pour une transaction => insertion et modification de colonnes pour une ligne traitées comme une seule opération Isolation assurée au niveau d’une ligne Durabilité assurée via un journal de commit log Read & Write consistency Ring Cassandra Système peer-to-peer où chaque nœud est capable de traiter une demande d’un client ( pas de relation maître/esclave ). Les données des tables sont distribuées de manière hashée et compressée sur chaque nœud dans des partitions. Chaque partition est répliquée sur des nœuds différents. Ring: écriture Ring: lecture LSM-tree LSM-tree Structure optimisée pour l’écriture des données, plus performante qu’une table SQL munie d’index sur de grands volumes ( GB, TB ). Idée principale: écrire en mémoire dans une table de type clé-valeur, puis écrire sur disque de manière asynchrone et séquentielle Une écriture sur disque est immuable => algorithme de merge-sort pour fusionner les mêmes tables SST Ecriture dans une table Lecture dans une table Modèle de données Ensemble de tables indépendantes les unes des autres ( pas de jointure en nosql ) Un seul index, la clé de partition Clusterisation possible des tables: clé composite Ajout possible d’index secondaires Tip: a good rule of a thumb is one column family per query since you optimize column families for read performance Type des données Types usuels: int, double, varchar, boolean, timestamp, blob Collections : set, list, map Autres types : counter, inet, uuid, timeuuid Cluster de test Cluster à 3 nœuds I5-3470 ( 4 CPU ) 32 GB RAM 4 TB HDD ( 7200 RPM ) Carte réseau à 100 Mb/s Ubuntu 12.04 LTS Commodity hardware Installation Pré-requis: Sudo JRE Oracle 7 Accès internet => apt-get Installation rapide ( < 1 jour si ports ouverts ) Documentation: http://www.datastax.com/documentation/getting_started/ doc/getting_started/gettingStartedDeb_t.html Insertion massive de données Méthode 1 : commande Copy ( cql ) Import de fichiers csv Exemple: copy T from ‘/home/user/file’ with delimiter = ‘|’ Méthode 2: outil sstableloader Générer une SS table à partir d’un fichier csv via un programme Java à créer Utiliser l’outil pour charger la SS table créée dans Cassandra Pas d’outil pour insérer des données semi-structurées => Création d’un outil en java SsTableLoad SsTableLoad <node_address> <nb_iter> <nb_insert> <table_name> <min_key> Il se connecte à un nœud du ring, lance n itérations sur une table au format prédéfini. Pour chaque itération, il exécute un bulk-insert de m lignes. La première ligne insérée a comme clé min_key, puis on incrémente de 1 pour chaque nouvelle insertion. SsTableLoad CREATE TABLE test_insert ( string varchar, nb bigint, bool boolean, list list<varchar>, map map<timestamp,text>, val blob, PRIMARY KEY (nb)); alter table test_insert with gc_grace_seconds = 30; Insertion d’un BLOB de 1 MB Exceptions Java Exceptions java rencontrées durant la phase de développement: Exception in thread "main" com.datastax.driver.core.exceptions.NoHostAvailableException: All host(s) tried for query failed (tried: /192.168.41.26 (com.datastax.driver.core.exceptions.DriverException: Timeout during read), /192.168.41.71 (com.datastax.driver.core.TransportException: [/192.168.41.71] Error writing), /192.168.41.86 (com.datastax.driver.core.exceptions.DriverException: Timeout during read)) Dans le fichier /etc/cassandra/cassandra.yaml: read_request_timeout_in_ms: 5000 => 1 minute write_request_timeout_in_ms: 2000 => 24 secondes Exceptions Java Exception in thread "main" com.datastax.driver.core.exceptions.InvalidQueryExc eption: Request is too big: length 524366013 exceeds maximum allowed length 268435456 => nb_insert = 250 java.lang.OutOfMemoryError: Java heap space => changer la taille de la heap size dans le fichier /etc/cassandra/cassandra-env.sh : 8 GB => 12 GB Scalabilité Un processus charge 64 GB en 14m25s, soit 1 GB en 14s. Deux processus chargent 64 GB en 8m47s, soit 1 GB en 8s. Saturation si lancement de 3 processus, un par noeud Activité réseau Etude des requêtes Objectif: comprendre le fonctionnement interne de quelques requêtes => tracing on sous cqlsh Liste des requêtes étudiées ( CRUD ) : Insert Update Select Count(*) Scan full, utilisation d’index secondaire, order by Delete Description des tables CREATE TABLE test_insert_x ( nb bigint, bool boolean, "list" list<text>, "map" map<timestamp, text>, string text, val blob, PRIMARY KEY (nb) ) WITH bloom_filter_fp_chance=0.010000 AND caching='ALL' AND comment='' AND dclocal_read_repair_chance=0.000000 AND gc_grace_seconds=30 AND index_interval=128 AND read_repair_chance=0.100000 AND replicate_on_write='true' AND populate_io_cache_on_flush='false' AND default_time_to_live=0 AND speculative_retry='99.0PERCENTILE' AND memtable_flush_period_in_ms=0 AND compaction={'class': 'SizeTieredCompactionStrategy'} AND compression={'sstable_compression': 'LZ4Compressor'}; CREATE TABLE test_select_x ( nb bigint, string text, bool boolean, "list" list<text>, "map" map<timestamp, text>, val blob, PRIMARY KEY (nb, string) ) WITH bloom_filter_fp_chance=0.010000 AND caching='KEYS_ONLY' AND comment='' AND dclocal_read_repair_chance=0.000000 AND gc_grace_seconds=30 AND index_interval=128 AND read_repair_chance=0.100000 AND replicate_on_write='true' AND populate_io_cache_on_flush='false' AND default_time_to_live=0 AND speculative_retry='99.0PERCENTILE' AND memtable_flush_period_in_ms=0 AND compaction={'class': 'SizeTieredCompactionStrategy'} AND compression={'sstable_compression': 'LZ4Compressor'}; Insert insert into test_insert_1 (nb,bool,list,map,string)values (12001, true, ['azerty', 'qwerty'], { '2014-03-28 12:00' : 't1'}, '12000'); Count(*) select count(*) from test_insert_1 limit 20000; Utilisation de l’index de la clé primaire select nb, list, string from test_insert_1 where nb = 1535 ; Utilisation d’un index secondaire CREATE INDEX test_insert_1_string_idx ON test_insert_1 (string); select nb, list, string from test_insert_1 where string = 'VvmEQQwkPEtypCrmBRrKUbhpXXxtfe'; Order by select nb,string, bool,list,map from test_select_1 where nb = 1221 order by string Update update test_select_1 set bool = true where nb = 1221 and string = 'LfazkllbGORcyHSwmiZgLVWcmbaWHL' ; Delete delete from test_select_1 where nb = 840; Interrogation des données Grammaire du select très limitée, peu d’index, accès disque en lecture => comment mieux exploiter cette immense quantité de données collectée ? Une solution: version Enterprise de Datastax Partie batch ( map-reduce, hive, apache mahout ) Moteur de recherche full-text: solr ( = elasticsearch ) Ajout d’une couche in-memory In-memory Changement de paradigme: disque & RAM => RAM & cache processeur Solution 1: coupler Cassandra à un moteur in-memory ( Spark, Shark, MLlib, … ) Solution 2: coupler Cassandra à une base in-memory en mode colonne ( Hana de SAP, Vertica de HP, Amazon RedShift, … ) => cible: BI