lundi 6 décembre 2010

Berkeley DB XML

Berkeley DB XML est une surcouche à Berkeley DB qui permet d'hériter de toutes ses fonctionnalités pour le stockage de documents XML.

En plus du stockage, BDB XML apporte les fonctionnalités suivantes :
- Indexation des noeuds, des attributs et des metadata
- Indexation au niveau noeud ou document
- QueryPlan
- Xquery 1.0
- Possibilité d'ajouter/requêter des metadata associées aux documents
- Documents modifiables (remplacer un sous ensemble du document peut être plus rapide)
- Validation (Il est possible de fournir un schema xsd qui est vérifié à l'insertion d'un document)
- Compression zlib par défaut (personnalisable)

XQUERY (XML Query Language) est disponible pour effectuer des recherches/modifications/ajouts de documents XML.
Il s'appuie sur Xqilla (bibliothèque C++) pour effectuer le parsing des requêtes Xquery/XPath.
Xqilla utilise Xerces (bibliothèque C++) pour effectuer le parsing et la validation des documents XML.
XQuery supporte les «Expressions FLWOR» (For/Let/Where/Order/Return) qui permettent d'effectuer toute sorte de traitements sur les données comme les boucles, les tris, le filtrage.
Il n'est pas encore possible de grouper les données avec XQuery 1.0, mais cette fonctionnalité founie par XQuery 1.1 sera certainement supportée dans l'une des prochaines versions de Berkeley DB XML. Pour plus d'information, vous pouvez vous rendre sur http://www.w3.org/TR/xquery.

Voyons quelques exemples simples de traitements qu'il vous est possible d'effectuer avec le language XQuery (dans l'ensemble des exemples qui suivent, vous pouvez utiliser l'interpréteur dbxml) :

- Pour ajouter un document au container test.dbxml
dbxml -h /var/tmp/
dbxml>createContainer c1.dbxml
dbxml> put p1 '<person name="pignon" age="32" Taille="160" genre="M">Charles</person>'
dbxml> put p2 '<person age="31" Taille="190" genre="M">Cyril Scetbon</person>'

Pour mettre à jour la valeur de l'attribut age du noeud person dont la valeur de l'attribut name vaut pignon
- replace value of node collection('c1.dbxml')/person[@name='pignon']/@age with '54'

Pour récupérer à partir du container c1.dbxml la valeur du noeud person dont la valeur de l'attribut Taille est supérieur à 180 et dont la valeur de l'attribut genre vaut M
- collection("c1.dbxml")/person[@Taille>180 and genre="M"]/text()
Cyril Scetbon

Pour compter le nombre de valeur distinctes de l'attribut Taille de tous les noeuds person contenus dans le container c1.dbxml
- count(distinct-values(collection("cont2.dbxml")/person/@Taille))
3

Et voici d'autres exemples utilisant des expressions FLWOR :

Pour afficher les chiffres de 1 à 10
- for $c in 1 to 3 return $c
1
2
3

Pour afficher le nom de tous les auteurs (valeur de l'attribut name du noeud author des documents du container authors.dbxml) qui ont écrit le livre dont le titre est 'MySQL 5 Administration et Optimisation' (vérification en faisant une jointure entre les containers books.dbxml et authors.dbxml sur la relation [valeur de l'attribut id du noeud /author = valeur de l'attribut id du noeud /book/author])
-for $book in collection("books.dbxml")/book[title='MySQL 5 Administration et Optimisation']
for $author in collection("authors.dbxml")/author[@id=$book/author/@id]
order by $author/name
return <author>{$author/name/string()}</author>
<author>Stephane Combaudon</author>
<author>Olivier Dasini</author>
<author>Cyril Scetbon</author>

Pour afficher l'ensemble des noeuds person du container c2.dbxml pour lesquels la valeur de l'attribut Taille est supérieur à 180 en les insérant entre des balises <response> </response>
- for $p in collection("c2.dbxml")/person[@Taille>180]
order by $p/@Taille descending
return <response>{$p}</respoonse>
<person genre="F" Taille="185">Brigitte Nielsen</person>
<person genre="M" Taille="182">Cyril Scetbon</person>


Dans un prochain article, nous verrons comment créer des index et vérifier que nos requêtes les utilisent bien.
Lire la suite...

lundi 18 octobre 2010

Mais où est mysqld_safe ?

Vous l'aurez peut être remarqué, mais dans la distribution Lucid d'Ubuntu,  mysqld_safe n'est plus présent.
Pour rappel, mysqld_safe est un script fourni avec MySQL pour lancer mysqld, le monitorer et le relancer s'il vient à mourir. C'est pourquoi lorsque mysqld_safe tourne, si vous arrêtez mysqld il est automatiquement relancé.
Cependant, il a disparu depuis la version mysqld 5.1.37 fournie dans la Lucid (la version actuelle étant la 5.1.41). Ceci ne veut cependant pas dire que le démon mysqld n'est plus monitoré afin d'être redémarré au cas où. En fait, c'est upstart qui est utilisé pour effectuer cette tâche.
Upstart , qui est un remplaçant du système sysvinit, s'occupe de démarrer et gérer les services au démarrage, ainsi que durant l'activité du système Linux. Des évènements sont déclenchés à l'arrêt ou démarrage de tâches et services et peuvent être captés par d'autres processus afin de déclencher des opérations.

Vous saurez maintenant qu'il n'y a pas à s'inquiéter sur un système Ubuntu où vous ne voyez pas de processus mysqld_safe tourne !

lundi 23 août 2010

Rechercher des motifs dans une arborescence de code source

J'ai découvert le soft global disponible dans les dépôt ubuntu et debian. Cet outil permet de tagger du code écrit en C, C++, Yacc, Java et PHP4, ce qui vous servira si vous voulez rechercher un pattern et par exemple modifier directement le fichier concerné.

Commençons par installer le produit sur un serveur ubuntu :

sudo apt-get install global
Lecture des listes de paquets... Fait
Construction de l'arbre des dépendances      
Lecture des informations d'état... Fait
Paquets suggérés :
  doxygen apache httpd id-utils
Les NOUVEAUX paquets suivants seront installés :
  global
0 mis à jour, 1 nouvellement installés, 0 à enlever et 0 non mis à jour.
Il est nécessaire de prendre 532ko dans les archives.
Après cette opération, 1 323ko d'espace disque supplémentaires seront utilisés.
Réception de :1 http://mc.archive.ubuntu.com/ubuntu/ lucid/universe global 5.7.1-1 [532kB]
532ko réceptionnés en 0s (1 721ko/s)
Sélection du paquet global précédemment désélectionné.
(Lecture de la base de données... 197595 fichiers et répertoires déjà installés.)
Dépaquetage de global (à partir de .../global_5.7.1-1_amd64.deb) ...
Traitement des actions différées (« triggers ») pour « man-db »...
Traitement des actions différées (« triggers ») pour « install-info »...
Paramétrage de global (5.7.1-1) ...
Ignoring install-info called from maintainer script
The package global should be rebuilt with new debhelper to get trigger support

Récupérons les sources d'un projet (nous allons avoir besoin de git dans l'exemple)

apt-get install git-core
Reading package lists... Done
Building dependency tree      
Reading state information... Done
The following extra packages will be installed:
  libdigest-sha1-perl liberror-perl
Suggested packages:
  git-doc git-arch git-cvs git-svn git-email git-daemon-run git-gui gitk gitweb
The following NEW packages will be installed:
  git-core libdigest-sha1-perl liberror-perl
0 upgraded, 3 newly installed, 0 to remove and 48 not upgraded.
Need to get 5,673kB of archives.
After this operation, 11.9MB of additional disk space will be used.
Do you want to continue [Y/n]?
Get:1 http://us.archive.ubuntu.com/ubuntu/ lucid/main liberror-perl 0.17-1 [23.8kB]
Get:2 http://us.archive.ubuntu.com/ubuntu/ lucid/main libdigest-sha1-perl 2.12-1build1 [26.2kB]
Get:3 http://us.archive.ubuntu.com/ubuntu/ lucid/main git-core 1:1.7.0.4-1 [5,623kB]
Fetched 5,673kB in 1s (4,984kB/s)  
Selecting previously deselected package liberror-perl.
(Reading database ... 32518 files and directories currently installed.)
Unpacking liberror-perl (from .../liberror-perl_0.17-1_all.deb) ...
Selecting previously deselected package libdigest-sha1-perl.
Unpacking libdigest-sha1-perl (from .../libdigest-sha1-perl_2.12-1build1_i386.deb) ...
Selecting previously deselected package git-core.
Unpacking git-core (from .../git-core_1%3a1.7.0.4-1_i386.deb) ...
Processing triggers for man-db ...
Setting up liberror-perl (0.17-1) ...
Setting up libdigest-sha1-perl (2.12-1build1) ...
Setting up git-core (1:1.7.0.4-1) ...

git clone http://github.com/Intel/wow.git
Initialized empty Git repository in /home/cyril/src-repo/git-repo/wow/.git/
remote: Counting objects: 5170, done.
remote: Compressing objects: 100% (4124/4124), done.
remote: Total 5170 (delta 1184), reused 4963 (delta 1009)
Receiving objects: 100% (5170/5170), 8.88 MiB | 2.89 MiB/s, done.
Resolving deltas: 100% (1184/1184), done.

cd wow/src

Pour indexer les fichiers nous utiliserons la commande gtags qui crée les fichiers GTAGS GPATH GRTAGS GSYMS utilisés par global pour nos futures recherches.Etant donné que le répertoire contient des fichiers autres que des fichiers sources c++ (.cpp et .h), on utilise la commande find pour les filtrer :

find . -name "*.cpp" -o -name "*.h"|gtags -v -f -
[Mon Aug 22 09:39:52 CEST 2010] Gtags started.
 Using default configuration.
[Mon Aug 22 09:39:52 CEST 2010] Creating 'GTAGS'.
 [1] extracting tags of tools/git_id/git_id.cpp
 [2] extracting tags of tools/map_extractor/wdt.cpp
 [3] extracting tags of tools/map_extractor/adt.cpp
 [4] extracting tags of tools/map_extractor/loadlib.cpp
 [5] extracting tags of tools/map_extractor/mpq_libmpq.cpp
 [6] extracting tags of tools/map_extractor/wdt.h
 [7] extracting tags of tools/map_extractor/adt.h
 [8] extracting tags of tools/map_extractor/dbcfile.cpp
 [9] extracting tags of tools/map_extractor/System.cpp
..
 [1096/1099] extracting tags of server/shared/Threading/Threading.cpp
 [1097/1099] extracting tags of server/shared/Threading/LockedQueue.h
 [1098/1099] extracting tags of server/shared/Threading/Threading.h
 [1099/1099] extracting tags of server/shared/Threading/DelayExecutor.cpp
[Mon Aug 22 09:40:18 CEST 2010] Done.

Tous les fichiers sont à présent indexés. On peut vérifier la taille des fichiers générés par gtags :

du -sh G*
280K    GPATH
3,2M    GRTAGS
6,6M    GSYMS
2,1M    GTAGS

Vous pouvez aussi choisir de les placer ailleurs, mais je vous laisse le faire en exercice. Ce qui est intéressant dans cet outil c'est qu'il est possible de rechercher la définition d'une fonction aussi simplement que :

global -x EndQuery
EndQuery           60 server/shared/Database/QueryResult.cpp void QueryResult::EndQuery()

On peut aussi vouloir rechercher les références à cette fonction dans le code :

global -rx EndQuery
EndQuery           37 server/shared/Database/QueryResult.cpp     EndQuery();
EndQuery           50 server/shared/Database/QueryResult.cpp         EndQuery();
EndQuery           59 server/shared/Database/QueryResult.h         void EndQuery();

Comme vous le voyez il est possible de faire des recherches intéressantes. Il est aussi possible d'utiliser des expressions régulières et de rechercher des motifs diverses :

global -gx mCurrentRow
mCurrentRow        28 server/shared/Database/QueryResult.cpp     mCurrentRow = new Field[mFieldCount];
mCurrentRow        29 server/shared/Database/QueryResult.cpp     ASSERT(mCurrentRow);
mCurrentRow        32 server/shared/Database/QueryResult.cpp          mCurrentRow[i].SetType(ConvertNativeType(fields[i].type));
mCurrentRow        55 server/shared/Database/QueryResult.cpp         mCurrentRow[i].SetValue(row[i]);
mCurrentRow        62 server/shared/Database/QueryResult.cpp     if (mCurrentRow)
mCurrentRow        64 server/shared/Database/QueryResult.cpp         delete [] mCurrentRow;
mCurrentRow        65 server/shared/Database/QueryResult.cpp         mCurrentRow = 0;
mCurrentRow        45 server/shared/Database/QueryResult.h         Field *Fetch() const { return mCurrentRow; }
mCurrentRow        47 server/shared/Database/QueryResult.h         const Field & operator [] (int index) const { return mCurrentRow[index]; }
mCurrentRow        53 server/shared/Database/QueryResult.h         Field *mCurrentRow;

Reportez vous à la documentation pour en savoir plus. Dernier outil que je trouve très intéressant est le wrapper globash qui permet en plus de se promener directement dans les fichiers à partir des résultats obtenus. Pour cela, lancez la commande globash et acceptez de créer le répertoire .globash lors du premier lancement :

globash

GloBash --- Global facility for Bash

GloBash needs working directory.

Create '/home/cyril/.globash'? ([y]/n) y

Created.

Welcome to Globash! When you need help, please type 'ghelp'.

Vous pouvez ensuite utiliser les mêmes commandes mais sans l'option x qui est activée par défaut :

[/home/cyril/src-repo/git-repo/wow/src] g mCurrentRow
>    1    mCurrentRow        28 server/shared/Database/QueryResult.cpp     mCurrentRow = new Field[mFieldCount];
     2    mCurrentRow        29 server/shared/Database/QueryResult.cpp     ASSERT(mCurrentRow);
     3    mCurrentRow        32 server/shared/Database/QueryResult.cpp          mCurrentRow[i].SetType(ConvertNativeType(fields[i].type));
     4    mCurrentRow        55 server/shared/Database/QueryResult.cpp         mCurrentRow[i].SetValue(row[i]);
     5    mCurrentRow        62 server/shared/Database/QueryResult.cpp     if (mCurrentRow)
     6    mCurrentRow        64 server/shared/Database/QueryResult.cpp         delete [] mCurrentRow;
     7    mCurrentRow        65 server/shared/Database/QueryResult.cpp         mCurrentRow = 0;
     8    mCurrentRow        45 server/shared/Database/QueryResult.h         Field *Fetch() const { return mCurrentRow; }
     9    mCurrentRow        47 server/shared/Database/QueryResult.h         const Field & operator [] (int index) const { return mCurrentRow[index]; }
    10    mCurrentRow        53 server/shared/Database/QueryResult.h         Field *mCurrentRow;

Vous pouvez lister à nouveau les résultats obtenus :

[/home/cyril/src-repo/git-repo/wow/src] list
>    1    mCurrentRow        28 server/shared/Database/QueryResult.cpp     mCurrentRow = new Field[mFieldCount];
     2    mCurrentRow        29 server/shared/Database/QueryResult.cpp     ASSERT(mCurrentRow);
     3    mCurrentRow        32 server/shared/Database/QueryResult.cpp          mCurrentRow[i].SetType(ConvertNativeType(fields[i].type));
     4    mCurrentRow        55 server/shared/Database/QueryResult.cpp         mCurrentRow[i].SetValue(row[i]);
     5    mCurrentRow        62 server/shared/Database/QueryResult.cpp     if (mCurrentRow)
     6    mCurrentRow        64 server/shared/Database/QueryResult.cpp         delete [] mCurrentRow;
     7    mCurrentRow        65 server/shared/Database/QueryResult.cpp         mCurrentRow = 0;
     8    mCurrentRow        45 server/shared/Database/QueryResult.h         Field *Fetch() const { return mCurrentRow; }
     9    mCurrentRow        47 server/shared/Database/QueryResult.h         const Field & operator [] (int index) const { return mCurrentRow[index]; }
    10    mCurrentRow        53 server/shared/Database/QueryResult.h         Field *mCurrentRow;

Mieux encore, vous pouvez demander à vous rendre directement à la ligne du fichier concerné (cela s'appuie sur la définition de la variable EDITOR)

[/home/cyril/src-repo/git-repo/wow/src] show 2
[/home/cyril/src-repo/git-repo/wow/src] l
    1    mCurrentRow        28 server/shared/Database/QueryResult.cpp     mCurrentRow = new Field[mFieldCount];
>     2    mCurrentRow        29 server/shared/Database/QueryResult.cpp     ASSERT(mCurrentRow);
     3    mCurrentRow        32 server/shared/Database/QueryResult.cpp          mCurrentRow[i].SetType(ConvertNativeType(fields[i].type));
     4    mCurrentRow        55 server/shared/Database/QueryResult.cpp         mCurrentRow[i].SetValue(row[i]);
     5    mCurrentRow        62 server/shared/Database/QueryResult.cpp     if (mCurrentRow)
     6    mCurrentRow        64 server/shared/Database/QueryResult.cpp         delete [] mCurrentRow;
     7    mCurrentRow        65 server/shared/Database/QueryResult.cpp         mCurrentRow = 0;
     8    mCurrentRow        45 server/shared/Database/QueryResult.h         Field *Fetch() const { return mCurrentRow; }
     9    mCurrentRow        47 server/shared/Database/QueryResult.h         const Field & operator [] (int index) const { return mCurrentRow[index]; }
    10    mCurrentRow        53 server/shared/Database/QueryResult.h         Field *mCurrentRow;

Vous pouvez taper exit pour sortir ou ghelp pour en savoir plus.

Cet outil est vraiment très rapide pour indexer le contenu et très utile avec son wrapper pour se balader directement dans les fichiers et effectuer des modifications si nécessaire. N'oubliez pas bien sûr de rafraîchir le contenu indexé ensuite en ajoutant -i à la commande initiale pour activer l'indexation incrémentale (Vous pouvez bien sûr retirer l'option -v qui active le mode verbeux) :

find . -name "*.cpp" -o -name "*.h"|gtags -i -v -f -
checking /home/cyril/src-repo/git-repo/wow/src/GTAGS
GTAGS found at '/home/cyril/src-repo/git-repo/wow/src/GTAGS'.
[Mon Aug 22 10:08:12 CEST 2010] Gtags started.
 Using default configuration.
 Tag found in '/home/cyril/src-repo/git-repo/wow/src'.
 Incremental update.
 Global databases are up to date.
[Mon Aug 22 10:08:12 CEST 2010] Done.

Je m'étais aussi intéressé à l'outil gonzui qui est aussi disponible sous forme de paquet et s'appuie sur BerkeleyDB pour stocker ses tags. Cependant, cet outil est beaucoup plus lent que global à l'indexation puisque que l'on passe de 3 à 130 secondes, et aussi lors des recherches sur des expressions régulières. Il ne dispose pas d'un wrapper semblable à globash qui est sans nul doute très utile lorsqu'il faut débugger, et occupe nécessite beaucoup plus de places que global (167 Mo contre 13 Mo).

Voilà, j'espère que ce billet vous sera utile dans vos prochaines investigations sur du code concernant les bases de données ou autres, mais comme vous avez pu le remarquer j'ai sciemment cherché un exemple dans la branche database :)



Lire la suite...

jeudi 5 août 2010

Berkeley DB 5.0.26

Pour ceux qui ne connaissent pas Berkeley DB (BDB), ou qui en ont vaguement entendu parler, sachez que si vous êtes sous un système opensource, vous avez de grande chances d'utiliser BDB sans même le savoir.

Par exemple, si vous utilisez pidgin ou evolution vous utilisez BDB :

~$ lsof -n|grep 'libdb-'|awk '{print $1," ",$9}'|sort|uniq
evolution /usr/lib/libdb-4.8.so
pidgin /usr/lib/libdb-4.8.so

Je peux par exemple voir sur mon système le nombre de paquets qui ont déclaré dépendre de BDB :

~$ apt-cache rdepends libdb4.8| wc -l
96

On peut aussi en voir un aperçu :

~$ apt-cache rdepends libdb4.8| head
libdb4.8
Reverse Depends:
squidguard
libapache2-mod-php5filter
php5-cli
php5-cgi
openoffice.org-core
libpam-modules
libedata-cal1.2-6
libedata-book1.2-2

Et encore ce ne sont que les paquets qui l'ont déclaré ! Tout ça pour vous dire que BDB est indispensable à tout système open-source qui se respecte. Mais qu'est-ce que BDB ?

BDB est une bibliothèque permettant d'opérer sur des données (stockage, modification, recherche) et que l'on lie à une application pour lui fournir ce type de service. L'application peut être codée en utilisant des languages différents (JAVA, C++, C, Perl, PHP, Python, etc...) et choisi la structure la plus adaptée à ses données parmi les types Btree, Hash, Queue et Recno. (Vous pouvez consulter la documentation pour en savoir un peu plus sur ces différents types).

BDB supporte les transactions ACID, le multithreading et le multiprocessing, l'encryptage de l'environnement (répertoire stockant les données), l'indexation, les sauvegardes à chaud et la récupération des données en cas de crash (grâce à la journalisation des transactions), ainsi que la replication maître/esclaves !

Sachez qu'il est possible de configurer les environnements participant à un groupe de réplication de sorte que les rôles de maître/esclaves soient redistribués (failover) en cas d'erreur sur le maître ou au niveau du canal de communication. Il est à noter que pour utiliser la réplication il est nécessaire de développer du code c, c++ ou java :(
Oracle qui a racheté la société Sleepycat Software propriétaire de BDB en février 2006 annonce une capacité de stockage en teraoctets et des milliards d'enregistrements !!

Cette bibliothèque est disponible sous 2 licences :
  • commerciale
  • GPL (celle qui vous permet de l'utiliser sur votre système opensource)
Il est à noter qu'étant une bibliothèque, il n'y a pas de serveur de données. l'application accède et manipule directement les fichiers physiques grâce au code de la bibliothèque BDB. De même, ce n'est pas un RDBMS (relational database management system) ne supportant pas les relations entre les données stockées.

Dans un prochain article je vous montrerai comment installer et utiliser rapidement un environnement BDB XML. Soyez patients ...
Lire la suite...

jeudi 24 juin 2010

Un livre MySQL à acquérir

Après 6 bons mois de rédactions, d'échanges de mails et de relectures, je vous annonce la sortie d'un nouveau livre sur MySQL 5 en français :
MySQL5, Administration et optimisation

Il reprend et explique tous les points propres à l'administration (configuration, mise à jour, sauvegardes/restaurations, maintenance, sécurité, ..) et à l'optimisation (nouvelles fonctionnalités, systèmes de caches, indexation, tuning, ..) en rendant abordables des concepts complexes.

En attendant de vous le procurer, vous pouvez consulter la TDM_MySQL5_Admin_Optim et un Extrait_MySQL5_Admin_Optim consacré aux verrous et transactions.

Inutile de vous dire que le livre est disponible dans toutes les bonnes librairies informatiques (FNAC, Amazon, ...). Pensez donc à vous le procurer pour l'étudier pendant vos vacances !

lundi 3 mai 2010

MySQL Cluster impose des limites aux méta-données

Lorsque vous mettez en place une configuration MySQL Cluster, ayez à l'esprit que celui-ci impose par défaut des limites aux méta-données. Vous ne pourrez donc pas créer autant de tables, d'index, de colonnes que vous le désirez sans modifier sa configuration. Il est possible de le faire plus tard, mais cela nécessitera d'effectuer un rolling restart (un redémarrage de l'ensemble des composants du cluster).
Voici les quelques paramètres qu'il faudra modifier selon les besoins de votre cluster (les valeurs par défaut sont indiquées entre parenthèses) :

- MaxNoOfAttributes fixe le nombre maximum de colonnes pouvant être créées au total dans l'ensemble des tables stockées (1000)
- MaxNoOfOrderedIndexes fixe le nombre maximum d'index ordonnés (128)
- MaxNoOfUniqueHashIndexes, comme le précédent mais pour les index uniques (64)
- MaxNoOfTables fixe le nombre maximum de tables (128)

Vous pourrez donc modifier la section [NDBD DEFAULT] de votre fichier de configuration ndb_mgmd.cnf et y ajouter la configuration suivante par exemple :

MaxNoOfAttributes=10000
MaxNoOfOrderedIndexes=3000
MaxNoOfUniqueHashIndexes=1500
MaxNoOfTables=1000

Pour plus d'information, vous pouvez visiter la documentation en ligne à l'adresse http://dev.mysql.com/doc/mysql-cluster-excerpt/5.1/en/mysql-cluster-mgm-definition.html

Vous ne pourrez pas dire que vous n'avez pas été prévenu :)

vendredi 26 mars 2010

2 bases exemple pour MySQL

Sur le site de MySQL vous pouvez télécharger les bases sakila et world afin de vous familiariser avec le SGBD.
Pour installer ces 2 bases sur votre serveur sous Ubuntu, suivez la procédure suivante :

sudo wget -c http://downloads.mysql.com/docs/sakila-db.tar.gz
sudo tar Ozvxf sakila-db.tar.gz sakila-db/sakila-schema.sql|sudo mysql --defaults-file=/etc/mysql/debian.cnf
sudo tar Ozvxf sakila-db.tar.gz sakila-db/sakila-data.sql|sudo mysql --defaults-file=/etc/mysql/debian.cnf sakila

sudo wget http://downloads.mysql.com/docs/world.sql.gz
sudo mysql --defaults-file=/etc/mysql/debian.cnf -e 'CREATE DATABASE world'
sudo zcat world.sql.gz|sudo mysql --defaults-file=/etc/mysql/debian.cnf world

Voila vos 2 bases sont créées et prêtes à être utilisées :

sudo mysql --defaults-file=/etc/mysql/debian.cnf
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 46
Server version: 5.1.37-1ubuntu5.1 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SELECT count(*) TABLES, table_schema,
-> concat(round(sum(table_rows)/1000000,2),'M') rows,
-> concat(round(sum(data_length)/(1024*1024),2),'M') DATA,
-> concat(round(sum(index_length)/(1024*1024),2),'M') idx,
-> concat(round(sum(data_length+index_length)/(1024*1024),2),'M') total_size,
-> round(sum(index_length)/sum(data_length),2) idxfrac
-> FROM information_schema.TABLES
-> WHERE table_schema IN ('sakila','world')
-> GROUP BY table_schema;
+--------+--------------------+-------+--------+-------+------------+---------+
| TABLES | table_schema | rows | DATA | idx | total_size | idxfrac |
+--------+--------------------+-------+--------+-------+------------+---------+
| 23 | sakila | 0.05M | 4.10M | 2.52M | 6.62M | 0.62 |
| 3 | world | 0.01M | 0.36M | 0.07M | 0.43M | 0.19 |
+--------+--------------------+-------+--------+-------+------------+---------+
2 rows in set (0,03 sec)

mysql> exit
Bye

Pour en savoir plus sur ces 2 bases vous pouvez vous rendre sur le site web de MySQL aux adresses http://dev.mysql.com/doc/sakila/en/sakila.html et http://dev.mysql.com/doc/world-setup/en/world-setup.html

A vous de jouer !

jeudi 18 février 2010

MySQL Cluster 7.1

La version 7.1 du moteur de stockage NDB n'est pas encore sortie mais on peut déjà savoir quelles seront les prochaines nouveautés non garanties pour le moment :

- Deux nouveaux connecteurs JAVA

ClusterJ et ClusterJPA permettent d'accéder au cluster MySQL sans utiliser de serveur MySQL (ni JDBC) ou pour le second de passer ou non par un serveur MySQL si la requête exécutée peut être améliorée par l'optimiseur MySQL.

Cette base permet d'accéder à des informations tel que la mémoire utilisée (table memoryusage), le statut des noeuds (table nodes), etc... nécessitant auparavant l'utilisation des commandes ALL DUMP {CODE}

- Une nouvelle base de données nommé ndbinfo

Cette base permet d'accéder à des informations tel que la mémoire utilisée (table memoryusage), le statut des noeuds (table nodes), etc... nécessitant auparavant l'utilisation des commandes ALL DUMP {CODE}

- Le support natif des valeurs par défaut des colonnes

Les valeurs par défaut des colonnes sont à présent stockées dans le noyau NDB et non par le serveur MySQL. Ceci permet au serveur MySQL de ne plus avoir à transférer ces données aux noeuds et donc potentiellement d'accélérer les requêtes d'insertions

- Une nouvelle option (--nowait-nodes) pour les managers

Cette option permet de ne plus avoir besoin de démarrer plus d'un manager pour démarrer un cluster configuré avec plusieurs managers.

Pour suivre l'évolution de ces fonctionnalités n'hésitez pas à consulter la page dédiée.

Récupérez les dernières versions de MySQL Cluster 7.X

Nous avons déjà vu dans un billet précédent les nouveautés de la version 7.0 (ancienne 6.4 renommée).

Il est cependant important de pouvoir vérifier l'arrivée des nouvelles versions sur le dépot officiel, et pourquoi pas de les récupérer pour les tester. Pour cela voici les commandes que j'utilise :

- Pour vérifier l'arrivée de nouvelles versions, je m'appuie sur les dates de création des répertoires

export LANG=C
wget -O - -q ftp://ftp.mysql.com/pub/mysql/download/cluster_telco/|grep Directory|awk -FDirectory '!/old/ {print $1}'|perl -MDate::Manip -lne 'if(Date_Cmp(ParseDate($_),ParseDate("2010 Feb 15 21:27"))>0){print "Nouvelle version disponible !";exit}'

- Pour récupérer les nouvelles versions disponibles j'utilise l'alias mirror_mysql_cge que j'ai défini ainsi :

alias mirror_mysql_cge='(cd /var/www && wget -nH --cut-dirs 3 -rc -R "mysqlcom*" -A "*-7.*.gz" --exclude-directories=/pub/mysql/download/cluster_telco/old,/pub/mysql/download/cluster_telco/newbuilds,/pub/mysql/download/cluster_telco/*-6.*,/pub/mysql/download/cluster_telco/*/*-6.*,/pub/mysql/download/cluster_telco/evaluation* ftp://ftp.mysql.com/pub/mysql/download/cluster_telco/)'

A chaque récupération de nouvelles archives il est cependant obligatoire de modifier la date utilisée dans la commande wget pour repérer la dernière archive récupérée. Pour obtenir cette date, la commande suivante sera d'un grand secours :

export LANG=C
wget -O - -q ftp://ftp.mysql.com/pub/mysql/download/cluster_telco/|grep Directory|awk -FDirectory '!/old/ {if(index($1,":")){print $1}}'| tr '\n' , |sed 's/,$//'|perl -MDate::Manip -MMemoize -lne 'sub sortDate {(Date_Cmp(ParseDate($a),ParseDate($b)))} print [reverse sort sortDate split(",",$_)]->[0]'

Ainsi je retrouverai toutes les versions 7.X sous l'arborescence /var/www/cluster_telco

lundi 4 janvier 2010

Nouvelles fonctionnalités dans le partitionnement de MySQL 5.5

Etant donné que cela fait un long moment que je n'ai pas bloggé je vais tenter de me rattrapper un peu :)

Je me suis penché sur l'une des nouvelles fonctionnalités de MySQL 5.5 concernant le partitionnement multi-colonnes en mode RANGE sur des types qui ne sont plus limités à l'entier.

En effet, il est possible de partitionner une table t1 sur 2 colonnes comme suit :

CREATE TABLE t1 (
valeur TINYINT UNSIGNED NOT NULL,
quand DATE NOT NULL,
libelle varchar(120)
)
PARTITION BY RANGE COLUMNS(valeur,quand) (
PARTITION p0 VALUES LESS THAN (10,'2006-10-02'),
PARTITION p1 VALUES LESS THAN (10,'2008-04-12'),
PARTITION p2 VALUES LESS THAN (100,MAXVALUE),
PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE)
);

Cependant l'algorithme qui répartit les données sur les différentes partitions créées n'est pas si intuitif que cela. Ainsi, j'imaginais dans un premier temps que l'enregistrement (100,'2005-10-02') ne pouvait se retrouver dans la partition p2 car 100 n'est pas inférieur à 100 ! En effet, je pensais que l'opérator LESS THAN sur un couple sous entendait que pour qu'un enregistrement (valeur,quand,libelle) appartienne à la partition p0 il fallait que valeur<10 et quand<'2006-10-02'.
Or ce n'est pas le cas, la preuve :

mysql> SELECT IF(10<10,'TRUE','FALSE'),IF('2005-10-02'<'2006-10-02','TRUE','FALSE'),IF((10,'2005-10-02')<(10,'2006-10-02'),'TRUE','FALSE')\G
*************************** 1. row ***************************
                              IF(10<10,'TRUE','FALSE'): FALSE
          IF('2005-10-02'<'2006-10-02','TRUE','FALSE'): TRUE
IF((10,'2005-10-02')<(10,'2006-10-02'),'TRUE','FALSE'): TRUE

Il faut dire qu'entre la documentation officielle qui a été corrigée (BUG 49875) suite à mon premier BUG 49861, et l'article de Giuseppe Maxia qui affirmait que si toutes les premières valeurs des listes de colonnes assignées aux partitions étaient différentes alors le partitionnement était identique au partitionnement sur cette seule colonne (corrigé depuis) j'ai un peu perdu la tête...

Cependant, cette histoire a bien fait de débuter puisqu'elle a débouché sur la correction de la documentation officielle, la correction d'un article avancé sur les nouveautés de la 5.5 concernant le partitionnement et sur la remise en cause des mots clés LESS THAN dans ce type de partitionnement avec une proposition de remplacement par NO GREATER THAN ou RANGE BOUNDED BY.

Nous verrons bien ce qu'il se passera dans les semaines à venir.