Affichage des articles dont le libellé est mongodb. Afficher tous les articles
Affichage des articles dont le libellé est mongodb. Afficher tous les articles
mardi 5 avril 2011
Livre MongoDB
Si vous cherchez un livre sur MongoDB, concis et précis et par dessus le marché gratuit, allez lire The Little MongoDB Book de Karl Seguin, le créateur de mongly.
Tutorial interactif MongoDB
Si vous voulez un tutorial MongoDB, rapide, efficace et totalement interactif, sans rien à installer, rendez vous sur mongly pour vous entrainer :
mercredi 15 décembre 2010
Réaliser un blog avec Ruby On Rails 3 et MongoDB
Objectifs
Le but de cet article est assez simple : réaliser un moteur de blog (minimaliste) en utilisant Ruby on Rails 3 et MongoDB.Installation
Parce qu'il faut bien commencer quelque part, on va installer notre environnement. L'installation détaillée ici est pour ubuntu 10.10.On va utiliser rvm (Ruby Version Manager), qui permet de gérer facilement plusieurs version de Ruby simultanément.
On commence par installer git, qui nous sera utile tout au long du projet, notamment pour l'installation de certaines gems, ou encore pour le déploiement.
sudo apt-get install git-core
Puis les dépendance nécessaires pour compiler ruby.
sudo apt-get install build-essential bison openssl libreadline5 libreadline5-dev curl git-core zlib1g zlib1g-dev libssl-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libmysqlclient-dev libreadline-dev
Ensuite on installe la dernière version de rvm en suivant la doc officielle :
# RVM install bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head ) # Load RVM into shell echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"' >> ~/.bashrc # RVM bash completion complete echo '[[ -r $rvm_path/scripts/completion ]] && . $rvm_path/scripts/completion' >> ~/.bashrc # One-time source of RVM source ~/.rvm/scripts/rvm
rvm est installé, on peut maintenant compiler et installer ruby :
rvm install 1.9.2 rvm use 1.9.2 --default
On a maintenant un environnement ruby à jour est prêt à être utilisé.
Initialisation du projet
On va tout d'abord créer le squelette de l'application grace à rails (avec les flags qui vont bien -O pour desactver ActiveRecord, -J pour utilier jquery, -T pour ne pas générer les Tests/rails new MongoOnRailsBlog -O -J -T
Notre application rails est en place, on peut déjà voir le résultat en lancant
rails server
Et en visitant l'adresse http://localhost:3000.
Par défaut, rails va utiliser sqlite comme backend, et nous voulons utiliser mongodb. Pour cela, on va utiliser MongoID, un ORM pour MongoDB.
On va l'installer via bundle, en modifiant le fichier GemFile
source 'http://rubygems.org' gem 'rails', '3.0.3' gem "mongoid", ">= 2.0.0.beta.20" gem "bson_ext" gem "will_paginate" gem "devise" gem "haml", ">= 3.0.0" gem "haml-rails", :group => :development gem "ruby_parser", :group => :development gem "hpricot", :group => :development gem "formtastic" gem "jquery-rails" gem "heroku"
Et en lancant un
bundle install
On commence par un peu de configuration, pour que les générateurs nous machent le travail pour MongoId et pour haml.
application.rb
# Configure generators config.generators do |g| g.orm :mongoid g.template_engine :haml end
rails generate mongoid:config rails devise:install rails generate jquery:install rails generate formtastic:install
Code
Les articles
Les articles sont bien entendu au centre de notre moteur de blog, ils correspondront à une collection MongoDB à part entière.Un article possède donc :
- Un slug (l'url SEO Friendly), qui nous servira d'identifiant
- Un titre
- Un contenu
- Une date de publication et d'édition
- Un auteur (facultatif)
- Des tags
- Des commentaires
Pour les champs "complexes" (auteur, tags, commentaires), plusieurs solutions s'offrent à nous.
- Pour les tags, la solution la plus simple est un tableau, largement suffisant dans la mesure où le tag est une simple chaîne de caractères
- Les commentaires sont fortement liés à un document, et n'ont aucun sens sans eux, la meilleure solution est donc de les embarquer dans l'article : voir la doc de MongoId sur les associations.
- Pour les auteurs, le choix est plus compliqué. On peut aussi embarquer l'auteur dans l'article, ce qui sera plus performant, mais nécessitera de mettre à jour tous les articles d'une personne si par exemple elle change son nom. On va ici ne stocker qu'une référence à l'auteur dans l'article, ce qui est moins performant, mais sera plus rapide à implémenter : voir la doc de MongoId sur les associations.
On va commence par générer le squelette pour la gestion des articles grace au scaffold, ce qui à le grand avantage de faire gagner du temps sur l'écriture des controlleurs, vues, ... :
rails g scaffold article slug:string title:string body:text published_date:dateOn complète bien sur notre modèle :
class Article include Mongoid::Document include Mongoid::Timestamps field :slug, :type => String field :title, :type => String field :body, :type => String field :published_at, :type => DateTime field :edited_at, :type => DateTime field :tags, :type => Array referenced_in :user, :stored_as => :array, :inverse_of => :articles embeds_many :comments key :slug before_save :set_edited_date before_create :set_slug,:set_published_date def set_published_date self.published_at = DateTime.now end def set_edited_date self.edited_at = DateTime.now end def set_slug self.slug = "#{title.parameterize}" end validates_presence_of :title, :body validates_uniqueness_of :slug index [[:published_date, Mongo::DESCENDING]] index :tags index :user end
Comme on peut le voir, on complexifie un peu notre modèle pour mettre automatiquement les dates de publication et d'édition (notez que ces dates font doublons avec les Mongoid::Timestamps), et générer automatiquement le slug.
On peut donc déjà s'amuser à créer un article via la console rails :
ruby-1.9.2-p0 > Article.create(:title => "My First Post", :body => "blablabla", :tags => ["misc"]) => #
Les commentaires étant imbriqués dans les articles, le formulaire de saisie de commentaire sera affiché sur la page de vue d'un article :
resources :articles do resources :comments end
class ArticlesController < ApplicationController def show @article = Article.find(params[:id]) respond_to do |format| format.html format.xml { render :xml => @article } end end end
class CommentsController < ApplicationController def create @article = Article.find(params[:article_id]) @comment = @article.comments.create!(params[:comment]) redirect_to @article, :notice => "Commented!" end end %h1 New Comment = semantic_form_for [@article, Comment.new] do |f| = f.inputs do = f.input :title = f.input :body, :as => :text = f.buttons do = f.commit_button
On va maintenant ajouter à notre blog un nuage de tag, et on va pour cela utiliser le map-reduce intégrer à MongoDB :
class Article def self.tag_cloud map = <<EOF function() { for (index in this.tags) { emit(this.tags[index],1); } } EOF reduce = <<EOF function(previous, current) { var count = 0; for (index in current) { count += current[index] } return count; } EOF if Mongoid.master.collection(collection.name).count != 0 collection.map_reduce(map,reduce).find() else [] end end endEt pour l'afficher :
%ul.tagcloud - Article.tag_cloud.each do |tag| %li{:style => "font-size: #{tag['value']}em"} %a{:href => "#"} = link_to tag["_id"], articles_by_tag_path(tag["_id"])
On a donc déjç, en seulement 1h de développement, un blog où l'on peut poster, mettre des commentaires, et naviguer par tag.
On va maintenant ajouter la notion d'auteur sur les articles et les commentaires, et on va pour cela utiliser Devise
rails generate devise User rails generate devise:views users
Devise a le grand avantage de s'occuper de tout le côté inscription, authentification récupération de mot de passe, ... pour nous.
Il suffit pour cela d'ajouter dans le routes.rb :
devise_for :usersEt les URLs /users/sign_up, /users/sign_in, /users/sign_out sont disponibles.
On peut donc mantenant lié un article et un commentaire à un utilisateur, en modifiant le controller de création.
class ArticlesController < ApplicationController def create params[:article][:tags] = params[:article][:tags].split(',') @article = Article.new(params[:article]) if user_signed_in? @article.user = current_user end respond_to do |format| if @article.save format.html { redirect_to(@article, :notice => 'Article was successfully created.') } format.xml { render :xml => @article, :status => :created, :location => @article } else format.html { render :action => "new" } format.xml { render :xml => @article.errors, :status => :unprocessable_entity } end end end end
class CommentsController < ApplicationController def create @article = Article.find(params[:article_id]) if user_signed_in? params[:comment][:user] = current_user end @comment = @article.comments.create!(params[:comment]) redirect_to @article, :notice => "Commented!" end end
Grace à un simple plugin, et à la puissance de rails, il nous a fallu moins d'une heure pour mettre en place un système d'authentification complet sur notre application. On peut même ajouter les fonctionnalité d'affichage de l'auteur d'un article, ou la liste des articles d'un auteur :
class ArticlesController < ApplicationController def author @author = User.find(params[:author]) @articles = @author.articles.desc(:published_at) respond_to do |format| format.html format.xml { render :xml => @articles } end end end
Mise en ligne
Pour déployer notre blog, nous allons utiliser la plateforme d'hébergement cloud pour Ruby on Rails Heroku, qui permet de déployer très facilement et gratuitement une application rails, avec à disposition un grand nombre d'addon, dont MongoDB.Il suffit de s'inscrire sur le site, et ensuite d'installer la gem heroku via bundler.
Je vous invite à lire la documentation d'heroku.
Une fois le déploiement fait via git, l'application est tout de suite visible en ligne via
http://mongoonrailsblog.heroku.com/.
Le code source est disponible sur github.
Voila, c'est la fin de mon premier essai en Ruby on Rails, qui m'a permis de construire un moteur de blog très minimaliste en quelques heures seulement, et de le mettre en ligne.
Libellés :
mongodb,
ruby on rails
mercredi 1 septembre 2010
MongoDB : scalabilité, réplication et failover grâce au sharding et aux replica set
Deux des fonctionnalités les plus attendues de MongoDB arrivent à maturation avec la sortie de la version 1.6 : le sharding et les replica set.
Le sharding, ou partitionnement, permet de rendre MongoDB parfaitement "horizontally scalable".
Les replica set permettent eux de répliquer les données entre des instances MongoDb, c'est une amélioration du mode master/slave existant, en ajoutant le failover automatique et la récupération automatiques des noeuds.
Si le noeud primaire tombe, un autre noeud prendra le relai automatiquement.
Le changement de master se fait via un système d'élection, ou chaque noeud actif du set vote pour élire un nouveau master. Un noeud "arbitre", qui appartient au set mais ne reçoit ou n'envoie aucune données peut être ajouté. L'ajout de cet arbitre est obligatoire dans le cas où le set ne comporte que 2 noeuds : en effet si le master tombe, il ne reste plus qu'un noeud qui votera pour lui même, il aura donc 1 voix sur 2, ce qui est insuffisant pour qu'il soit élu.
On va donc commencer par lancer 2 serveurs mongod + l'arbitre (avec l'option --shardsvr pour préparer la suite), en indiquant qu'ils appartiennent à un Replica Set.
Un petit coup d'oeil aux logs du premier noeud :
Les 3 noeuds sont maintenant lancés, on va pouvoir configurer la réplication
On va pouvoir regarder les logs pour vérifier que tout se passe bien :
On peut donc voir que node1 a été élu PRIMARY, node2 est donc SECONDARY, et arbiter ne fait pas parti du groupe.
On va maintenant essayé d'ajouter un noeud a chaud :
Encore une fois on jete un coup d'oeil aux logs :
L'arrivée de node3 a donc entrainé un nouveau vote pour élire le master, et c'est maintenant node2 le nouveau PRIMARY.
Si on essaye maintenent de tuer le master, on va voir qu'un nouveau noeud est élu pour prendre son relai
Voilà, c'est aussi simple que ça, on a maintenant 3 serveurs MongoDB répliqués, en master/slave, avec élection automatique du master en cas de problème.
Passons maintenant au sharding
On va donc passer à une architecture à 9 serveurs, répartis en 3 replica set.
Il y a 3 éléments à mettre en place pour que le sharding fonctionne :
Nous allons maintenant activer le sharding sur notre replica set. Pour cela, on va commencer par créer nos replica set :
Nos 3 replica set sont prêts, on va pouvoir passer au sharding : en commençant par lancer les serveurs de configuration et le routeur :
Par defaut, la taille minimale d'un chunk est de 50Mo, pour des raisons de facilité de test, on passe à 1Mo via l'option chunkSize.
Ensuite, on va activer le sharding sur une collection db.people :
On a donc partitionner la collection "people" de la base "test" sur la clef autogénérée par MongoDb.
Voila, le sharding est maintenant appliqué , on va pouvoir insérer un peu de données. Dans les stats, on voit bien que pour l'instant, un seul shard est utilisé par notre base, la répartition sur plusieurs shards se faisant uniquement quand le besoin s'en fait ressentir.
Si on regarde les stats de la base immédiatement, on va se rendre compte qu'il y a plusieurs chunks, mais qu'ils sont tous sur le même shard, il faut attendre quelques minutes pour que la répartition se fasse entre les shards.
Apres 2 minutes d'insertions, on voit bien que les 3 shards sont utilisés :
Pour optimiser un peu l'utilisation des serveurs, on pourrait faire se recouper les replica set, c'est un dire qu'un serveur physique ferait partie de plusieurs replica.
Par contre, si 2 noeuds sur les 3 disponibles d'un même replica set tombent, ce replica set passera en ReadOnly.
De même, un bonne architecture serait d'avoir au moins 4 serveurs par replica set, repartis sur 3 datacenter.
Le sharding, ou partitionnement, permet de rendre MongoDB parfaitement "horizontally scalable".
Les replica set permettent eux de répliquer les données entre des instances MongoDb, c'est une amélioration du mode master/slave existant, en ajoutant le failover automatique et la récupération automatiques des noeuds.
Mettre en place le Replica Set
Il est possible d'avoir autant de membres que voulu dans un replica set, et les données existeront sur chacun des noeuds du set. Cela permet de répartir les serveurs entre différents datacenters, et ainsi d'assurer une redondance totale. Un seul serveur est "primaire" et peut recevoir des lectures et des écriture, les autres sont "secondaires" et ne peuvent recevoir que des lectures.Si le noeud primaire tombe, un autre noeud prendra le relai automatiquement.
Le changement de master se fait via un système d'élection, ou chaque noeud actif du set vote pour élire un nouveau master. Un noeud "arbitre", qui appartient au set mais ne reçoit ou n'envoie aucune données peut être ajouté. L'ajout de cet arbitre est obligatoire dans le cas où le set ne comporte que 2 noeuds : en effet si le master tombe, il ne reste plus qu'un noeud qui votera pour lui même, il aura donc 1 voix sur 2, ce qui est insuffisant pour qu'il soit élu.
On va donc commencer par lancer 2 serveurs mongod + l'arbitre (avec l'option --shardsvr pour préparer la suite), en indiquant qu'ils appartiennent à un Replica Set.
mongod --port=10001 --shardsvr --replSet=replset --logpath=${path}/nodes/node1/logs/node.log --logappend --dbpath=${path}/nodes/node1/data/ --fork --rest mongod --port=10002 --shardsvr --replSet=replset --logpath=${path}/nodes/node2/logs/node.log --logappend --dbpath=${path}/nodes/node2/data/ --fork --rest mongod --port=10009 --shardsvr --replSet=replset --logpath=${path}/nodes/arbiter/logs/node.log --logappend --dbpath=${path}/nodes/arbiter/data/ --fork --rest
Un petit coup d'oeil aux logs du premier noeud :
[initandlisten] ****** [websvr] web admin interface listening on port 11001 [initandlisten] connection accepted from 127.0.0.1:53323 #1 [startReplSets] replSet can't get local.system.replset config from self or any seed (EMPTYCONFIG)
Les 3 noeuds sont maintenant lancés, on va pouvoir configurer la réplication
mongo --port 10001
cfg = { _id: "replset", members: [{_id: 0, host: "ubuntu:10001"},{_id: 1, host: "ubuntu:10002"},{_id: 2, host: "ubuntu:10009", arbiterOnly: true }]} rs.initiate(cfg)
On va pouvoir regarder les logs pour vérifier que tout se passe bien :
tail nodes/node1/logs/node.log [conn1] replSet replSetInitiate admin command received from client [conn1] replSet replSetInitiate config object parses ok, 3 members specified [initandlisten] connection accepted from 127.0.1.1:60557 #3 [conn1] replSet replSetInitiate all members seem up [conn1] replSet info saving a newer config version to local.system.replset [conn1] replSet replSetInitiate config now saved locally. Should come online in about a minute. [conn1] end connection 127.0.0.1:53512 [rs Manager] replSet can't see a majority, will not try to elect self [initandlisten] connection accepted from 127.0.1.1:49305 #4 [initandlisten] connection accepted from 127.0.1.1:49306 #5 [ReplSetHealthPollTask] replSet info ubuntu:10002 is now up [ReplSetHealthPollTask] replSet info ubuntu:10009 is now up [rs Manager] replSet info electSelf 0 [rs Manager] replSet PRIMARY [initandlisten] connection accepted from 127.0.1.1:49308 #6
tail nodes/node2/logs/node.log [startReplSets] replSet got config version 1 from a remote, saving locally [startReplSets] replSet info saving a newer config version to local.system.replset [rs Manager] replSet can't see a majority, will not try to elect self [conn2] replSet info voting yea for 0 [ReplSetHealthPollTask] replSet info ubuntu:10001 is now up [ReplSetHealthPollTask] replSet info ubuntu:10009 is now up [rs_sync] replSet initial sync pending [rs_sync] building new index on { _id: 1 } for local.me [rs_sync] Buildindex local.me idxNo:0 { name: "_id_", ns: "local.me", key: { _id: 1 } } [rs_sync] done for 0 records 0.001secs [initandlisten] connection accepted from 127.0.1.1:37633 #3 [rs_sync] replSet initial sync drop all databases [rs_sync] dropAllDatabasesExceptLocal 1 [rs_sync] replSet initial sync cloning db: admin [rs_sync] replSet initial sync query minValid [rs_sync] replSet initial sync copy+apply oplog [rs_sync] replSet initial sync finishing up [rs_sync] replSet set minValid=4c72a31e:1 [rs_sync] building new index on { _id: 1 } for local.replset.minvalid [rs_sync] Buildindex local.replset.minvalid idxNo:0 { name: "_id_", ns: "local.replset.minvalid", key: { _id: 1 } } [rs_sync] done for 0 records 0secs [rs_sync] replSet initial sync done [rs_sync] replSet SECONDARY
tail nodes/arbiter/logs/node.log [startReplSets] replSet got config version 1 from a remote, saving locally [startReplSets] replSet info saving a newer config version to local.system.replset [initandlisten] connection accepted from 127.0.1.1:51923 #3 [ReplSetHealthPollTask] replSet info ubuntu:10001 is now up [ReplSetHealthPollTask] replSet info ubuntu:10002 is now up
On peut donc voir que node1 a été élu PRIMARY, node2 est donc SECONDARY, et arbiter ne fait pas parti du groupe.
On va maintenant essayé d'ajouter un noeud a chaud :
mongod --port=10003 --shardsvr --replSet=replset/localhost:10001 --logpath=${path}/nodes/node3/logs/node.log --logappend --dbpath=${path}/nodes/node3/data/ --fork --rest mongo --port 10001 rs.add("ubuntu:10003")
Encore une fois on jete un coup d'oeil aux logs :
tail nodes/node3/logs/node.log [startReplSets] replSet got config version 2 from a remote, saving locally [startReplSets] replSet info saving a newer config version to local.system.replset [rs Manager] replSet warning total number of votes is even - considering giving one member an extra vote [rs Manager] replSet can't see a majority, will not try to elect self [ReplSetHealthPollTask] replSet info ubuntu:10001 is now up [ReplSetHealthPollTask] replSet info ubuntu:10002 is now up [ReplSetHealthPollTask] replSet info ubuntu:10009 is now up [rs_sync] replSet initial sync pending [rs_sync] building new index on { _id: 1 } for local.me [rs_sync] Buildindex local.me idxNo:0 { name: "_id_", ns: "local.me", key: { _id: 1 } } [rs_sync] done for 0 records 0.007secs [rs_sync] replSet initial sync drop all databases [rs_sync] dropAllDatabasesExceptLocal 1 [rs_sync] replSet initial sync cloning db: admin [rs_sync] replSet initial sync query minValid [rs_sync] replSet initial sync copy+apply oplog [rs_sync] replSet initial sync finishing up [rs_sync] replSet set minValid=4c72a3fe:1 [rs_sync] building new index on { _id: 1 } for local.replset.minvalid [rs_sync] Buildindex local.replset.minvalid idxNo:0 { name: "_id_", ns: "local.replset.minvalid", key: { _id: 1 } } [rs_sync] done for 0 records 0secs [rs_sync] replSet initial sync done [rs_sync] replSet SECONDARY
tail nodes/node2/logs/node.log [rs Manager] replset msgReceivedNewConfig version: version: 2 [rs Manager] replSet info saving a newer config version to local.system.replset [rs Manager] replSet replSetReconfig new config saved locally [rs Manager] replSet warning total number of votes is even - considering giving one member an extra vote [rs Manager] replSet can't see a majority, will not try to elect self [ReplSetHealthPollTask] replSet info ubuntu:10001 is now up [ReplSetHealthPollTask] replSet info ubuntu:10009 is now up [rs Manager] replSet info electSelf 1 [rs Manager] replSet PRIMARY [ReplSetHealthPollTask] replSet info ubuntu:10003 is now up
L'arrivée de node3 a donc entrainé un nouveau vote pour élire le master, et c'est maintenant node2 le nouveau PRIMARY.
Si on essaye maintenent de tuer le master, on va voir qu'un nouveau noeud est élu pour prendre son relai
kill -9 6486 [rs_sync] replSet syncThread: 10278 dbclient error communicating with server [conn2] end connection 127.0.0.1:55409 [ReplSetHealthPollTask] replSet info ubuntu:10002 is now down (or slow to respond) [rs Manager] replSet info electSelf 2 [rs Manager] replSet PRIMARY
Voilà, c'est aussi simple que ça, on a maintenant 3 serveurs MongoDB répliqués, en master/slave, avec élection automatique du master en cas de problème.
Passons maintenant au sharding
Mise en place du sharding
Maintenant qu'on a 3 serveurs MongoDB répliqués, on va pouvoir y ajouter le partitionnement horizontal, ou sharding.On va donc passer à une architecture à 9 serveurs, répartis en 3 replica set.
Il y a 3 éléments à mettre en place pour que le sharding fonctionne :
- Les Shard servers : ou instances mongod, c'est ce que l'on vient de faire.
- Les Config servers : Serveur de configuration qui va stocker les metadat du shard. La documentation conseille au moins 3 serveurs de configuration dans un environnement de production, on va içi se limiter à un seul.
- Le mongos : Sert de routeur vers les différents shards.
Nous allons maintenant activer le sharding sur notre replica set. Pour cela, on va commencer par créer nos replica set :
mongod --port=10011 --shardsvr --replSet=rs1 --logpath=${path}/nodes/node1-1/logs/node.log --logappend --dbpath=${path}/nodes/node1-1/data/ --fork --rest mongod --port=10012 --shardsvr --replSet=rs1 --logpath=${path}/nodes/node1-2/logs/node.log --logappend --dbpath=${path}/nodes/node1-2/data/ --fork --rest mongod --port=10013 --shardsvr --replSet=rs1 --logpath=${path}/nodes/node1-3/logs/node.log --logappend --dbpath=${path}/nodes/node1-3/data/ --fork --rest mongo 127.0.0.1:10011/admin > cfg = { _id: "rs1", members: [{_id: 0, host: "ubuntu:10011"},{_id: 1, host: "ubuntu:10012"},{_id: 2, host: "ubuntu:10013"}]}; > rs.initiate(cfg); mongod --port=10021 --shardsvr --replSet=rs2 --logpath=${path}/nodes/node2-1/logs/node.log --logappend --dbpath=${path}/nodes/node2-1/data/ --fork --rest mongod --port=10022 --shardsvr --replSet=rs2 --logpath=${path}/nodes/node2-2/logs/node.log --logappend --dbpath=${path}/nodes/node2-2/data/ --fork --rest mongod --port=10023 --shardsvr --replSet=rs2 --logpath=${path}/nodes/node2-3/logs/node.log --logappend --dbpath=${path}/nodes/node2-3/data/ --fork --rest mongo 127.0.0.1:10021/admin > cfg = { _id: "rs2", members: [{_id: 0, host: "ubuntu:10021"},{_id: 1, host: "ubuntu:10022"},{_id: 2, host: "ubuntu:10023"}]}; > rs.initiate(cfg); mongod --port=10031 --shardsvr --replSet=rs3 --logpath=${path}/nodes/node3-1/logs/node.log --logappend --dbpath=${path}/nodes/node3-1/data/ --fork --rest mongod --port=10032 --shardsvr --replSet=rs3 --logpath=${path}/nodes/node3-2/logs/node.log --logappend --dbpath=${path}/nodes/node3-2/data/ --fork --rest mongod --port=10033 --shardsvr --replSet=rs3 --logpath=${path}/nodes/node3-3/logs/node.log --logappend --dbpath=${path}/nodes/node3-3/data/ --fork --rest mongo 127.0.0.1:10031/admin > cfg = { _id: "rs3", members: [{_id: 0, host: "ubuntu:10031"},{_id: 1, host: "ubuntu:10032"},{_id: 2, host: "ubuntu:10033"}]}; > rs.initiate(cfg);
Nos 3 replica set sont prêts, on va pouvoir passer au sharding : en commençant par lancer les serveurs de configuration et le routeur :
mongod --port=11001 --configsvr --logpath=${path}/nodes/config1/logs/node.log --logappend --dbpath=${path}/nodes/config$i/data/ --fork mongod --port=11002 --configsvr --logpath=${path}/nodes/config2/logs/node.log --logappend --dbpath=${path}/nodes/config$i/data/ --fork mongod --port=11003 --configsvr --logpath=${path}/nodes/config3/logs/node.log --logappend --dbpath=${path}/nodes/config$i/data/ --fork mongos --port=9999 --configdb=ubuntu:11001,ubuntu:11002,ubuntu:11003 --logpath=${path}/nodes/mongos/logs/node.log --logappend --fork --chunkSize=1
Par defaut, la taille minimale d'un chunk est de 50Mo, pour des raisons de facilité de test, on passe à 1Mo via l'option chunkSize.
Ensuite, on va activer le sharding sur une collection db.people :
mongo 127.0.0.1:9999/admin > db.runCommand({addshard: "rs1/ubuntu:10011,ubuntu:10012,ubuntu:10013"}) { "shardAdded" : "shard0000", "ok" : 1 } > db.runCommand({addshard: "rs2/ubuntu:10021,ubuntu:10022,ubuntu:10023"}) { "shardAdded" : "shard0001", "ok" : 1 } > db.runCommand({addshard: "rs3/ubuntu:10031,ubuntu:10032,ubuntu:10033"}) { "shardAdded" : "shard0002", "ok" : 1 } > db.runCommand({enablesharding: "test"}) { "ok" : 1 } > db.runCommand({shardcollection: "test.people", key:{"_id":1}}) { "collectionsharded" : "db.people", "ok" : 1 }
On a donc partitionner la collection "people" de la base "test" sur la clef autogénérée par MongoDb.
> db.printShardingStatus() --- Sharding Status --- sharding version: { "_id" : 1, "version" : 3 } shards: { "_id" : "shard0000", "host" : "rs1/ubuntu:10011,ubuntu:10012,ubuntu:10013" } { "_id" : "shard0001", "host" : "rs2/ubuntu:10021,ubuntu:10022,ubuntu:10023" } { "_id" : "shard0002", "host" : "rs3/ubuntu:10031,ubuntu:10032,ubuntu:10033" } databases: { "_id" : "admin", "partitioned" : false, "primary" : "config" } { "_id" : "db", "partitioned" : true, "primary" : "shard0000" } db.people chunks: { "_id" : { $minKey : 1 } } -->> { "_id" : { $maxKey : 1 } } on : shard0000 { "t" : 1000, "i" : 0 }
Voila, le sharding est maintenant appliqué , on va pouvoir insérer un peu de données. Dans les stats, on voit bien que pour l'instant, un seul shard est utilisé par notre base, la répartition sur plusieurs shards se faisant uniquement quand le besoin s'en fait ressentir.
mongo 127.0.0.1:9999 > for (var i=1;i<=100000;i++) db.people.save({index:i, data:'Just for filling'})
Si on regarde les stats de la base immédiatement, on va se rendre compte qu'il y a plusieurs chunks, mais qu'ils sont tous sur le même shard, il faut attendre quelques minutes pour que la répartition se fasse entre les shards.
Apres 2 minutes d'insertions, on voit bien que les 3 shards sont utilisés :
mongo 127.0.0.1:9999 > db.people.stats() { "sharded" : true, "ns" : "test.people", "count" : 89264, "size" : 12854112, "avgObjSize" : 144.00107546155226, "storageSize" : 33546240, "nindexes" : 1, "nchunks" : 13, "shards" : { "shard0000" : { "ns" : "test.people", "count" : 44609, "size" : 6423696, "avgObjSize" : 144, "storageSize" : 11182080, "numExtents" : 6, "nindexes" : 1, "lastExtentSize" : 8388608, "paddingFactor" : 1, "flags" : 1, "totalIndexSize" : 1867776, "indexSizes" : { "_id_" : 1867776 }, "ok" : 1 }, "shard0001" : { "ns" : "test.people", "count" : 20975, "size" : 3020448, "avgObjSize" : 144.0022884386174, "storageSize" : 11182080, "numExtents" : 6, "nindexes" : 1, "lastExtentSize" : 8388608, "paddingFactor" : 1, "flags" : 1, "totalIndexSize" : 876544, "indexSizes" : { "_id_" : 876544 }, "ok" : 1 }, "shard0002" : { "ns" : "test.people", "count" : 26220, "size" : 3775728, "avgObjSize" : 144.00183066361555, "storageSize" : 11182080, "numExtents" : 6, "nindexes" : 1, "lastExtentSize" : 8388608, "paddingFactor" : 1, "flags" : 1, "totalIndexSize" : 1089536, "indexSizes" : { "_id_" : 1089536 }, "ok" : 1 } }, "ok" : 1 }
Conclusion
En conclusion, par rapport au sharding sans replica set, on ne gagne pas vraiment en performances, on perd en stockage (les shards sont dupliqués sur les membres du set), mais on gagne fortement en fiabilité.Pour optimiser un peu l'utilisation des serveurs, on pourrait faire se recouper les replica set, c'est un dire qu'un serveur physique ferait partie de plusieurs replica.
Notes sur le failover
Si une instance mongod crashe, ou un serveur de configuration, il n'y aura aucun impact sur la disponibilités des données, et toutes les opérations seront disponibles.Par contre, si 2 noeuds sur les 3 disponibles d'un même replica set tombent, ce replica set passera en ReadOnly.
De même, un bonne architecture serait d'avoir au moins 4 serveurs par replica set, repartis sur 3 datacenter.
Extension
Pour scaler notre architecture, il suffit donc de créer un nouveau replica set, et de l'ajouter au shard, les données seront réparties automatiquement par MongoDB.jeudi 19 août 2010
MongoDB 1.6 : réplication, partitionnement horizontal
Encore une bonne nouvelle dans le monde NoSQL, MongoDB 1.6 est sortie il y a peu de temps.
La combinaison de ces 2 éléments augmentent encore la scalabilité de MongoDB;
On peut donc construire des clusters MongoDB, fortement "horizontally scalable", sans "single points of failure".
Pour voir le release note complet, c'est par içi.
Voir un exemple de configuration complète d'un cluster avec sharding et replication.
Scalabilité
La plus grosse nouveauté, et la plus attendue, de cette release est sans conteste le sharding, ou partitionnement horizontal et les replica sets.La combinaison de ces 2 éléments augmentent encore la scalabilité de MongoDB;
On peut donc construire des clusters MongoDB, fortement "horizontally scalable", sans "single points of failure".
Réplication
La réplication était jusqu’alors assurée par une architecture master/slave qui souffrait d’un single point of failure à cause justement de cette notion de master. MongoDB 1.6 introduit la notion de replica set qui est un ensemble de noeuds qui possèderont des replicas d’une même donnée. Une élection de master permet alors de définir un noeud unique qui sera responsable des écritures.Partitionnement
Le sharding est maintenant production ready dans MongoDB. L’architecture de partitionnement repose sur un ou plusieurs proxy intermédiaire entre les clients et les instances MongoDB.Pour voir le release note complet, c'est par içi.
Voir un exemple de configuration complète d'un cluster avec sharding et replication.
Inscription à :
Articles (Atom)