Script Shell de backup de base de données via FTP

Après avoir modifié mon script de backup de dossier pour y intégrer le transfert via FTP, j'ai aussi entrepris de modifier le script de backup de base de donnée pour faire de même.

Là aussi, pas de grandes difficultés, le code ayant été déjà en partie réalisé, j'ai juste implémenté la partie transfert via FTP. J'en ai aussi profité pour optimiser le code afin de proposer quelque chose de plus fonctionnel. Vous m'en direz des nouvelles :)

Comme les autres scripts (backups de dossier/base de données), mettez ceci en Cron pour être exécuté quotidiennement, et modifiez les paramètres pour vos besoins, et tout devrait fonctionner (dites le sinon ;)).

Voici le code :

#!/bin/bash
#
# Shell script (BASH) used in cron to made a backup of all the MySQL databases

# In order to run this script, you must have following tools installed:
# - /usr/bin/mysql
# - /usr/bin/mysqlcheck
# - /usr/bin/mysqldump
# - /usr/bin/gpg
# - ncftp
#
# And we will use theses :
# - tar
# - rm
# - chown
# - chmod
#
#
# Installation
# Customize the script according to your need. You need to setup :
# - The MySQL credentials
# - The FTP credentials
# - The directory where to put the backups in the distant ftp
# - A GPG passphrase
#
# - The Email to send reports when error occurs
# - The file where to log the messages                         (default /var/log/backup.log)
# - The number of days the backups are stored (default 5)
#
# --------------------------------------------------------------------
# This is a free shell script under GNU GPL version 3.0 or above
# Copyright (C) 2005 ReFlectiv project.
# Feedback/comment/suggestions : http://www.reflectiv.net/
# -------------------------------------------------------------------------

# User & Password for the MySQL user used for the backup
DB_USER="root"; # Could be another user with good rights
DB_PASS="root";

# FTP Credentials
FTP_SERVER="ftp.serveur.tld"
FTP_USER="user"
FTP_PASS="password"

# Backup directory on the FTP :
FTP_DIR="/backups/database"

# The password for the archive
GPG_PASS="my_password";

# Email to send when error occurs
USER_EMAIL="email"

# File name where to log the messages
LOG_FILE="/var/log/backup.log";

# Number of days the archives are keeped
KEEPING_DAYS=5;


# Only change if your UNIX stores bin in diffrent location
MYSQL="/usr/bin/mysql";
MYSQLCHECK="/usr/bin/mysqlcheck";
MYSQLDUMP="/usr/bin/mysqldump";
GPG="/usr/bin/gpg";
NCFTP="/usr/bin/ncftp"

#######################################################################
# Do not change anything below
#######################################################################

# We mades some vars
DATE="$(date +"%Y-%m-%d")";
CURRENT_DATE=`date +"%d/%m/%Y - %H:%I:%S"`;
ARCHIVE="mysql_backup_$(date +"%Y_%m_%d").tbz";
GPG_ARCHIVE=$ARCHIVE".gpg";
OLD_ARCHIVE="mysql_backup_"`date --date $KEEPING_DAYS' days ago' "+%Y_%m_%d"`".tbz.gpg";

# Preparing the field :
mkdir -p /tmp/mysql_backup/
[ $? -eq 0 ] || exit 1;

cd /tmp/mysql_backup/
[ $? -eq 0 ] || exit 1;

# Adding new clear entry to the log file
echo "" >> $LOG_FILE."tmp";
echo "----------------------------------------------------------------------------------------------------" >> $LOG_FILE."tmp";
echo "" >> $LOG_FILE."tmp";

# Function called to log info into the file
function log {
    echo "["$CURRENT_DATE"] - "$1 >> $LOG_FILE".tmp";

    if [ ! $2 == "" ]; then
                mail -s "[$(hostname)] - Remote backup error" $USER_EMAIL > $LOG_FILE;
                cd /
                rm -rf "/tmp/mysql_backup";
                rm $LOG_FILE".tmp";
        exit 1;
    fi
}

log "MySQL Complete DUMP started";

# Preliminary tests
[ -x $MYSQL ] || log "mysql not found" 1;
[ -x $MYSQLCHECK ] || log "mysqlcheck not found" 1;
[ -x $MYSQLDUMP ] || log "mysqldump not found" 1;
[ -x $GPG ] || log "gpg not found" 1;
[ -x $NCFTP ] || log "ncftp not found" 1;

# We create te temporary directory for the dump
if [ ! -d $DATE ]; then
    mkdir -p $DATE"/";
    # If an error occured ...
        [ $? -eq 0 ] || log "An error occured with the command mkdir" 1;

    chmod 500 $DATE;
    # If an error occured ...
        [ $? -eq 0 ] || log "An error occured with the command chmod" 1;
fi

# We lists the bases
DATABASES="$(mysql -u $DB_USER -p$DB_PASS -Bse 'show databases;')";
# If an error occured ...
[ $? -eq 0 ] || log "An error occured while retrieving the list of all the databases" 1;

# For each Databases
for BASE in $DATABASES
do
        # We analyse each bases
        $MYSQLCHECK -u $DB_USER -p$DB_PASS -c -a $BASE > /dev/null
        # If an error occured ...
        [ $? -eq 0 ] || log "An error occured while checking the database "$BASE 1;

        log "Dump of the database '"$BASE"'";

        # And we saving them in a file
        $MYSQLDUMP -u $DB_USER -p$DB_PASS --add-drop-database  --add-drop-table --complete-insert --routines --triggers --allow-keywords --max_allowed_packet=50M --force $BASE -R > $DATE"/"$BASE".sql";

        # An error occured while dumping the database
        [ $? -eq 0 ] || log "An error occured while dumping the database '"$BASE"'" 1;
done

# We made the archive
tar -cjf $ARCHIVE $DATE"/";
# If an error occured ...
[ $? -eq 0 ] || log "An error occured with the command tar" 1;

# We encrypt the archive
$GPG --yes -c --passphrase $GPG_PASS --s2k-cipher-algo RIJNDAEL256 $ARCHIVE;
# If an error occured ...
[ $? -eq 0 ] || log "An error occured with the command gpg" 1;

# We delete the archive
rm -f $ARCHIVE;
# If an error occured ...
[ $? -eq 0 ] || log "An error occured with the command rm" 1;

# We modify the rights for the file
chmod 400 $GPG_ARCHIVE;
# If an error occured ...
[ $? -eq 0 ] || log "An error occured with the command chmod" 1;

# We modify the owner and group of the file
chown $UID $GPG_ARCHIVE;
# If an error occured ...
[ $? -eq 0 ] || log "An error occured with the command chown" 1;

log "Starting update to FTP server "$FTP_SERVER;

$NCFTP -u$FTP_USER -p$FTP_PASS $FTP_SERVER > $LOG_FILE;
rm $LOG_FILE".tmp";

exit 0;

Comme pour le script de backup de dossier via FTP, je n'ai pas trouvé comment faire taire ncftp. Donc si vous trouvez comment faire, je suis preneur !!

Toutes remarques seront les bienvenues :)

Filed under  //  Development   Scripts   Unix/Linux   backup   base   database   donnée   ftp   mysql   shell  
Posted by Cyril Nicodème 

N'utilisez plus les fonctions mysql_*

Dans cet article, nous verrons en quoi il est préférable d'utiliser PDO et d'oublier les fonctions telles que mysql_*

0. Sommaire

  1. Introduction
  2. Mise en place de l'environnement
  3. Instanciation de l'objet PDO
  4. Requêtes simples
  5. Requêtes préparées
    1. Sans bind de valeurs
    2. Avec bind de valeurs
  6. Les transactions
  7. Conclusion
  8. Ressources
  9. Notes

1. Introduction

La sortie de PHP 5 le 13 Juillet 2004, a apporté son lot de nouveauté. Parmis eux, se trouve la librairie PDO, originalement fournie en tant qu'extension PECL, mais ensuite adaptée dans le coeur de PHP dans sa version 5.1. PDO fournit une abstraction des bases de données, permettant ainsi au développeur de communiquer avec une base de donnée sans (trop) se soucier du serveur avec qui il communique (MySQL, PostgreSQL, MsSql, Oracle, etc). La puissance de PDO réside dans le fait que cette librairie est écrite en C, contrairement aux Pear:DB, AdoDb et companie, écrite elles, en Php. De ce fait, on constate un gain de performance et le développeur n'a pas à se soucier de télécharger les classes * au cas ou * le serveur ne les aurait pas à disposition (cas de PEAR par exemple).

Mais alors pourquoi trop de monde utilise encore mysql_* ?

Nous verrons dans cet article que PDO est très simple d'usage et qu'il existe maintenant plus aucune excuse à utiliser mysql_* !

Read the rest of this post »

Filed under  //  Development   Php   begin   commit   execute   mysql   pdo   prepare   query   rollback   transaction  
Posted by Cyril Nicodème 

Script de Backup MySQL à mettre en cron

Il existe divers scripts de backup pour votre système. Je n'ai pas trouvé celui qui me convenait le plus (quelque chose de simple), j'ai donc réalisé le mien.

Il ne fait que lister les bases de données existantes, crée un fichier .sql contenant le dump de chacune de ces bases ensuite, et créer une archive de ces fichiers.

Les actions sont loggués dans un fichier (par défaut /var/log/backup.log) et sont envoyé par mail lors de l'exécution du script.

Ce que j'aimerai ajouter, c'est l'envoi de mail de l'archive crée, mais chiffré symétriquement par gpg. Or, je n'ai pas trouvé de solutions pour donner un mot de passe symétrique à gpg directement dans un script shell ... je n'ai donc pas activé l'envoi de mail ! :)

* Petite mise à jour, maintenant le script utilise l'encryption symétrique par gpg, avec l'algorithme reconnus comme le plus sûr, RIJNDAEL256. Il ne me reste plus qu'un envoi correct par mail et le tout sera parfait ! :)

#!/bin/bash
#
# Shell script (BASH) used in cron to made a backup of all the MySQL databases

# In order to run this script, you must have following tools installed:
# - /usr/bin/mysql
# - /usr/bin/mysqlcheck
# - /usr/bin/mysqldump
# - /bin/tar
# - /bin/gzip
# - /usr/bin/gpg
#
#
# Installation
# Customize the script according to your need. You need to setup :
# - The directory where to put the backups
# - A MySQL User and his Password
# - A GPG passphrase
# - The number of days the backups are stored (default 5)
#
# --------------------------------------------------------------------
# This is a free shell script under GNU GPL version 2.0 or above
# Copyright (C) 2005 ReFlectiv project.
# Feedback/comment/suggestions : http://www.reflectiv.net/
# -------------------------------------------------------------------------
#
# @See : http://www.ruas-blog.com/index.php/2006/03/06/33-script-de-sauvegarde-des-bases-mysql
# @See : http://www.shelldorado.com/articles/mailattachments.html

# Directory where the backups goes
BACKUP="/root/backups/mysql/"; # Don't forget the last "/" !

# User & Password for the MySQL user used for the backup
DB_USER="root"; # Could be another user with good rights
DB_PASS="password";

# Password for the symetric encryption in GPG
GPG_PASS="Gpg_password";

# Email where to send the Email
EMAIL="root";

# The number of days the backup are stored
KEEPING_DAYS=`date --date '5 days ago' "+%Y-%m-%d"`;

# Log file
LOG="/var/log/backup.log";

# Day when sending the mail backup (if no day, no mail will be sent)
DAY_MAIL="dim";

# Only change if your UNIX stores bin in diffrent location
MYSQL="/usr/bin/mysql";
MYSQLCHECK="/usr/bin/mysqlcheck";
MYSQLDUMP="/usr/bin/mysqldump";
TAR="/bin/tar";
GPG="/usr/bin/gpg";


#######################################################################
# Do not change anything below
#######################################################################

DATE="$(date +"%Y-%m-%d")";
DATE_NOW=`date +"%d/%m/%Y - %H:%I:%S"`;

cd $BACKUP;

TMP_LOG="tmp_backup.log";
>$TMP_LOG;

echo "["$DATE_NOW"] - MySQL Complete DUMP started" >> $TMP_LOG;

# WE create te temporary directory for the dump
if [ ! -d $DATE ]; then
       mkdir $DATE"/";
       chmod 500 $DATE;
fi

# We lists the bases
DATABASES="$(mysql -u $DB_USER -p$DB_PASS -Bse 'show databases;')";

# An error occured while retrieving the list of all the databases
if [ ! $? -eq 0 ]; then
       rm -rf $DATE;
       echo "["$DATE_NOW"] - An error occured while retrieving the list of all the databases" >> 
$TMP_LOG;
       echo $TMP_LOG >> $LOG;
       rm $TMP_LOG;
       exit 1;
fi

# For each Databases
for BASE in $DATABASES
do
       # We analyse each bases
       mysqlcheck -u $DB_USER -p$DB_PASS -c -a $BASE > /dev/null

       echo "["$DATE_NOW"] - Dump of the database '"$BASE"'" >> $TMP_LOG;

       # And we saving them in a file
       mysqldump -u $DB_USER -p$DB_PASS --add-drop-database  --add-drop-table --complete-insert
 --routines --triggers --allow-keywords --max_allowed_packet=50M --force $BASE -R > 
$DATE"/"$BASE".sql";

       # An error occured while dumping the database
       if [ ! $? -eq 0 ]; then
               rm -rf $DATE;
               echo "["$DATE_NOW"] - An error occured while dumping the database '"$BASE"'" >> 
$TMP_LOG;
               echo $TMP_LOG >> $LOG;
               rm $TMP_LOG;
               exit 1;
       fi
done

# Name of the Archive
ARCHIVE="mysql_backup_"$DATE".tar.gz";
OLD_ARCHIVE="mysql_backup_"$KEEPING_DAYS".tar.gz";

# We made the archive
tar -czf $ARCHIVE $DATE"/";

# An error occured while creating the archive
if [ ! $? -eq 0 ]; then
       rm -rf $DATE;
       echo "["$DATE_NOW"] - An error occured while creating the archive '"$ARCHIVE"'" >> 
$TMP_LOG;
       echo $TMP_LOG >> $LOG;
       rm $TMP_LOG;
       exit 1;
fi

# Deleting the temporary directory
rm -rf $DATE;

# Creating a GPG version of the archive
gpg --yes -c --passphrase $GPG_PASS --s2k-cipher-algo RIJNDAEL256 $ARCHIVE;

# An error occured while creating the encrypted archive
if [ ! $? -eq 0 ]; then
        rm -f $ARCHIVE;
        echo "["$DATE_NOW"] - An error occured while creating the encrypted archive '"$ARCHIVE"'" >>
 $TMP_LOG;
        echo $TMP_LOG >> $LOG;
        rm $TMP_LOG;
        exit 1;
fi

# Deleting the original archive
rm -f $ARCHIVE;

ARCHIVE=$ARCHIVE".gpg";

# Securying current archive
chmod 400 $ARCHIVE;

echo "["$DATE_NOW"] - Archive '"$ARCHIVE"' successfully created" >> $TMP_LOG;

# Deleting old archives
if [ -f $OLD_ARCHIVE ]; then
       rm $OLD_ARCHIVE;
       echo "["$DATE_NOW"] - Archive '"$OLD_ARCHIVE"' deleted" >> $TMP_LOG;
fi

#if [ `date +"%a"` == $DAY_MAIL ]; then
#       mail -s "MySQL Backup "$DATE $EMAIL -a "Content-Type: application/octet-stream" 
-a "Content-Disposition: inline; filename="$ARCHIVE > $TMP_LOG;
#fi

mail -s "MySQL Daily Backup ("$DATE")" $EMAIL > $LOG;
rm $TMP_LOG;

exit 0;

Filed under  //  Development   Scripts   Unix/Linux   automatique   backup   cron   mysql   sauvegarde   shell  
Posted by Cyril Nicodème 

Quelques optimisations pour MySQL

Cette liste, donnée par Dublish.com, regroupe quelques bonnes astuces à prendre pour améliorer le traitement de vos donnée stockées sur votre SGBD.

La partie PHP n'est pas traitée ici puisqu'elle à fait le sujet d'un autre article publié précédement.

  • MySQL interprète de la droite vers la gauche, de ce fait, mettez les signifiants limiteurs le plus loin possible de la droite.
  • Sélectionnez les colonnes, plutôt que * (tout).
  • Évitez de mettre des informations qui changent rarement dans la base de donnée, préférez les dans un tableau contenu dans un fichier que vous inclurez.
  • Utilisez des indexes dans les colonnes contenues dans les clauses WHERE et ORDER BY.
  • Les indexes sont très intéressants quand vous faites des recherches mais ralentissent considérablement l'insertion.
  • Utilisez la requête "EXPLAIN" pour analyser vos indexes.
  • Si vous ne voulez qu'une ligne de résultat, limitez votre requête avec la clause LIMIT 1; Cela aura pour effet de stopper MySQL à la première ligne trouvée plutôt que de continuer à parcourir TOUTE la table pour une donnée qui est déjà trouvée.
  • Préferez un FETCH_ASSOC ou FETCH_NUM plutôt qu'un FETCH_BOTH qui déclarera deux variables par colonne au lieu d'une seule.
  • Parfois, mysql_free_result (ou similaire en fonction de ce que vous utilisez) finis par consommer plus de mémoire qu'en gagner. Regardez la différence avec memory_get_usage ();.
  • Évitez de demander à la base de donnée la même chose encore et encore. Stocker la requête et réutilisez la !
  • Utilisez autant que possible NOT NULL en tant que valeur par défaut. Cela améliore l'éxécution et save 1 bit.
  • Utilisez des types de colonnes qui correspondent à votre utilisation. Un INT non signé peux contenir jusqu'à 4294967295 valeurs. En avez-vous vraiment besoin ? Préférez l'utilisation de SMALLINT ou TINYINT en fonction de l'usage.
  • Profitez des valeurs par défaut. Lors de l'insertion, insérer que ce qui n'est pas valeur par défaut afin d'améliorer l'insertion.

Beaucoup de choses sont logiques, d'autres le son après la lecture.
En suivant ces quelques règles, ainsi que celles sur le php, vous pourrez améliorer l'éxécution de votre code considérablement.
Ce qui est un avantage quand le nombre de visiteur augmente !

Filed under  //  Development   améliorer   astuces   fast   gain   mysql   optimisation   performance   rapidité   règle   speed   temps   tricks  
Posted by Cyril Nicodème 

Les fonctions mysql_* sont à considérer comme obsolètes !

On voit encore beaucoup de fonctions mysql_* traîner sur les applications web car les développeurs ont toujours connu cette pratique, surtout ceux qui ont démarré PHP à l'ère de PHP 3 et PHP 4. Néanmoins, ces fonctions sont aujourd'hui beaucoup trop limitantes car elles ne permettent pas de profiter des spécificités d'un environnement MySQL 5 à savoir :

  • Les transactions,
  • Les triggers,
  • Les requêtes préparées,
  • Les procédures stockées...

C'est pour ces raisons que l'extension MySQLi est apparue en PHP 5 pour venir petit à petit essayer de supplanter l'extension MySQL classique. Ainsi en utilisant l'extension MySQLi, le développeur peut jouir des mêmes fonctionnalités qu'avec MySQL ainsi que des nouveautés propres à MySQL 5 avec des performances légèrement meilleures.

Quant à PDO, il est vrai que cette extension est légèrement moins performante en terme de vélocité du fait notamment de la POO mais a le mérite d'être beaucoup plus performante en terme de fonctionnalités offertes (support des transactions, procédures stockées, triggers, requêtes préparées...) et d'indépendance. Avec PDO, on dépend beaucoup moins de son SGBDR contrairement aux fonctions propres à MySQL. Dans le cadre d'une application de forum open-source, c'est un réel avantage de pouvoir offrir aux utilisateurs un support de l'application sur plusieurs SGBDR. Grâce à PDO, la difficulté de cette tâche est largement réduite puisqu'il n'est pas nécessaire d'avoir à écrire soi même les connecteurs pour chaque base. Il suffit juste d'adapter les requêtes SQL. Il est bon de noter qu'en PHP, seul PDO est capable de se connecter à une base de type SQLite 3 par exemple.

Je tiens à préciser également qu'une version 2 de PDO est en cours de réécriture et devrait paraître avec PHP 6 si je ne me trompe pas (à vérifier). Toujours est-il que c'est un chantier en cours du côté de l'équipe de développement de PHP. Le succès de PDO grandit de jour en jour, notamment grâce aux frameworks. Il suffit par exemple de constater combien d'applications d'ORM intégrées aux frameworks PHP 5 actuels s'appuient sur PDO. C'est le cas par exemple de Propel 1.3 et Doctrine 0.9 intégrés à Symfony ou bien encore le composant Zend_Db du framework Zend. C'est notamment grâce aux différents feedbacks des développeurs concernant les performances et la potentielle lourdeur de PDO que l'équipe de développement a ouvert un chantier pour améliorer tout ça dans les mois à venir.

Enfin, je rappelle que la nouvelle extension native MySQLnd, dédiée à MySQL, sera packagée avec PHP 5.3 au moment de sa sortie en version bêta dans le courant du mois d'août et en version stable à l'automne. Donc entre MySQL, MySQLi, PDO, MySQLnd et PDO 2, le choix des connecteurs pour MySQL est large et on peut affirmer aujourd'hui que le driver simple MySQL est obsolète. D'autant plus que PHP 4 ne sera plus supporté à partir du 08 août prochain et MySQL est à l'origine une extension PHP 4 destinée à interagir avec des bases de données MySQL 4. Elle a rendu de très bons services aux développeurs jusqu'à maintenant et je pense que ça continuera encore malgré tout, mais il est temps de montrer aux développeurs d'aujourd'hui qu'il existe des solutions plus fiables, plus "performantes", plus pratiques, plus fonctionnelles et plus pérennes. :)

Commentaire de Hugo HAMON (Webdeb) sur le site de phpcs.com

Filed under  //  Development   Howtos   Php   database   mysql   obsolete   pdo   sgbd   sqlite  
Posted by Cyril Nicodème 

How to install DBDesigner 4

Hey !

Today I will explain how to install dbdesigner4 on Fedora. It's a bit tricky ! :/

So first of all, download these two files :

After, extract dbdesigner (tar -xvzf DBDesigner4.0.5.4.tar.gz).

Extract also kylixlibs3-borqt-3.0-2.tar.gz.

Go to the kylixlibs3-borqt folder, and do this :

tar -xvzf DBDesigner4.0.5.4.tar.gz
tar -xvzf kylixlibs3-borqt-3.0-2.tar.gz
cd kylixlibs3-borqt
su -
cp libborqt-6.9.0-qt2.3.so /usr/lib/libborqt-6.9.0-qt2.3.so
ln -s /usr/lib/libborqt-6.9.0-qt2.3.so /lib/libborqt-6.9-qt2.3.so
/sbin/ldconfig
chown -R user:user ../DBDesigner4
cd ../DBDesigner4/
./startdbd

And thats it ! :)

But now, you want to connect DBDesigner to your database to automatically manage your table from your design ! And apparently It doesn't work !

Okay, okay, I give you the solution !

Donwload this file :
http://www.devart.com/dbx/download.html (the right for you, sure !)

Extract it and copy it to /usr/lib :

tar -xvzf dbxmda.tar.gz
su -
cd DbxMda
mv libsqlmda.so.4.20 /usr/lib/

Then, open DBDesigner, go to Database > Connect to Database, click on "New connection Database", choose the "advance" tab, and modify these informations :

LibraryName: libsqlmda.so.4.60
VendorLib: libmysqlclient.so.10.0.0
GetDriverFunc: getSQLDriverMySQL

BE CAREFUL ! The current value for GetDriverFunc is NOT the same as the one indicated here ! Yep ! The Y is in upper caps but mine is in lower caps. It's a big difference because in the libsqlmda.so, the function is getSQLDriverMySQL and NOT getSQLDriverMYSQL, so if you leave the Y, it will not work ! :p

Now you can play with mysql ^^

Filed under  //  Fedora   dbdesigner   fabforce   liborqt   mysql   workbench  
Posted by Cyril Nicodème