Aller au contenu | Aller au menu

grab our rss feed

/var/log/greg.log

Réplication MySQL multiple sources

Trop de données, trop d'écritures, la solution la plus rapide et facile à mettre en place consiste à déplacer des tables sur d'autres serveurs. Ainsi, on s'est retrouvé avec plusieurs "bulles" composées d'un master et de N slaves. Ce qui complique un peu quand même.

Déjà, les backups. Maintenant, il faut un slave supplémentaire par bulle pour le backup. Rappel: il faut arrêter la réplication pour avoir un bon backup capable de repartir, de reconfigurer un slave from scratch.

Autre problème, bien plus complexe: souvent, notamment pour les statistiques, il faut faire de grosses requêtes avec des JOINs inter tables et inter base de donnée.

Il existe des solutions à ces problèmes, rajouter des slaves, faire de la consolidation de donnée par batch pour les stats... et le multiple source replication. Cette fonctionnalité est hyper demandé chez MySQL, comme on peut le voir sur le worklog, il est d'ailleur en 1ère position !

Ca fait quelques semaines que je suis sur un script pour émuler le multiple source. En fait, plutôt que de me taper le protocole, j'utilise directement le binaire mysqlbinlog fournit avec mysql, qui permet de se connecter à un master, de lui demander les logs. On peut même lui demander les logs à partir d'un fichier précis, d'une position précise, et jusqu'à ce qu'il en ai plus !! Ce script utilise une base de donnée MySQL pour stocker les informations relatives à la réplication: position en cours, position suivante, fichier de log, erreurs... Il est capable de répliquer depuis plusieurs master (pas vraiment de limite, si ce n'est les performances du slave), mais attention, il se contente de lire les logs et de les executer, il est donc super préférable d'avoir des noms de base de données distinctes sur chaque master !

slave A ------- db a, b, c -------> \
slave B ------- db d, e ---------->  > msr slave
slave C ------- all db -----------> /

Vous trouverez les sources sur Rubyforge. Ce script tourne en environnement de production répliquant 3 slaves et 5 bases de données. J'ai fais quelques tests d'intégrité de données et n'ai pas constaté d'erreurs !

Restorer plus rapidement un dump MySQL

Mon backup comprend un dossier par base de donnée, un fichier SQL compressé par table. Voici une astuce pour restaurer ce backup plus rapidement, surtout sur un serveur multi-cores.

find -print0 | xargs -0 -n 1 -P 4 -I {} sh -c "zcat '{}' | mysql mydatabase"

Je n'ai pas fais de tests comparatifs, je sais juste que ça va vraiment beaucoup plus vite !!

Benchmark rapide comparant un disque dur, NFS et GlusterFS

Sur quelques serveurs j'ai la chance d'avoir un montage NFS ainsi qu'un montage GlusterFS 2.0-rc4.

  • un fichier de 17ko: cache_c21f969b5f03d33d43e04f8f136e7682_c57d91c82f2ed2e96c13765e33fcd5ae
  • un fichier de 50mo: 50.MB

J'ai copié ces 2 fichiers sur le disque dur dans /tmp, ainsi que sur les 2 montages réseaux, puis effectuer un simple cat (plusieurs fois), et voici les résultats :

php-14:~# for f in
/var/www/cache/cache_c/cache_cf/cache_cfb/cache_c21f969b5f03d33d43e04f8f136e7682_c57d91c82f2ed2e96c13765e33fcd5ae
/nfs/tmp/cache_c21f969b5f03d33d43e04f8f136e7682_c57d91c82f2ed2e96c13765e33fcd5ae
/tmp/cache_c21f969b5f03d33d43e04f8f136e7682_c57d91c82f2ed2e96c13765e33fcd5ae
/glusterfs/50.MB /nfs/tmp/50.MB /tmp/50.MB; do
echo $f; time cat $f >/dev/null; done
/var/www/cache/cache_c/cache_cf/cache_cfb/cache_c21f969b5f03d33d43e04f8f136e7682_c57d91c82f2ed2e96c13765e33fcd5ae

real    0m0.011s
user    0m0.000s
sys     0m0.000s
/nfs/tmp/cache_c21f969b5f03d33d43e04f8f136e7682_c57d91c82f2ed2e96c13765e33fcd5ae

real    0m0.002s
user    0m0.000s
sys     0m0.000s
/tmp/cache_c21f969b5f03d33d43e04f8f136e7682_c57d91c82f2ed2e96c13765e33fcd5ae

real    0m0.001s
user    0m0.000s
sys     0m0.000s
/glusterfs/50.MB

real    0m0.081s
user    0m0.000s
sys     0m0.068s
/nfs/tmp/50.MB

real    0m0.037s
user    0m0.000s
sys     0m0.036s
/tmp/50.MB

real    0m0.028s
user    0m0.000s
sys     0m0.028s

GlusterFS est donc plus lent que NFS pour les petits fichiers ET les "gros" fichiers, mais j'en ai pas finis avec le paramètrage ! Le serveur NFS est plutot bien optimisé, alors que je découvre seulement GlusterFS et que je commence seulement les tests...

Mettre à jour GlusterFS

Aujourd'hui, j'ai mis à jour GlusterFS sur les serveurs et les clients. Malgré que cette techno soit génial, on ne peut pas mettre à jour GlusterFS n'importe comment: il faut impérativement que les clients et les serveurs aient exactement la même version, même mineure ! Sinon, on obtient cette erreur, et le client ne se connecte pas :

SETVOLUME on remote-host failed: Version mismatch: client(2.0.0rc7) Vs server (2.0.0rc4)

Voici donc la procédure pour mettre à jour GlusterFS dans une architecture de type AFR client side, et en partant du principe que vous avez 2 serveurs.

1/ mettre à jour glusterfs sur le 1er serveur. Au restart, les clients ne pourront plus se connecter sur ce serveur. Vérifier dans les logs qu'il a bien démarré et qu'il n'y a pas eut de changement dans les paramètres.

2/ mettre à jour les clients, en arrêtant au préalable les services utilisant les montages glusterfs: il faut démonter puis remonter chaque partition pour avoir la dernière version du client. Les clients changent de serveurs au passage et se connecte au 1er serveur, qui a la même version.

3/ mettre à jour le 2ème serveur. Les clients vont automatiquement se connecter dessus.

4/ resynchroniser les données. Normalement la version 2.0 gère bien la synchro des données grâce à sa feature "self-healing". Dans le doute, on peut forcer, en faisant depuis un client connecté aux 2 nodes:

nice -20 find /glusterfs >/dev/null

That's all folks !

Redimensionnement d'une partition ext3

Sur un serveur, je suis tombé à court d'inodes. Initialement prévu pour MySQL, la partition de données était plus ou moins optimisé pour cet usage. Oui mais voilà, ce serveur est amené à faire plusieurs choses, et entre autre, à devenir reverse-proxy squid.

server_smurtz:/data# df -i
Filesystem            Inodes   IUsed   IFree IUse% Mounted on
/dev/sda1             122624   11922  110702   10% /
tmpfs                2059274       6 2059268    1% /lib/init/rw
udev                 2059274     979 2058295    1% /dev
tmpfs                2059274       1 2059273    1% /dev/shm
/dev/sda9             407680  407680       0  100% /data
/dev/sda5             122624    2734  119890    3% /tmp
/dev/sda6             122624   29619   93005   25% /usr
/dev/sda7             732960    6261  726699    1% /var

On ne peut pas augmenter le nombre d'inodes d'une partition. C'est bête. Par contre, on peut diminuer la partition avec resize2fs ! Je vais donc diminuer la partition, afin de laisser uniquement mysql sur celle-ci, et créer une nouvelle partition sur l'espace ainsi libéré, avec un max d'inodes.

  • voici les infos utiles, avant resize:
server_smurtz:~# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda1               964500    236836    678668  26% /
tmpfs                  8237096         0   8237096   0% /lib/init/rw
udev                     10240       112     10128   2% /dev
tmpfs                  8237096         0   8237096   0% /dev/shm
/dev/sda5               964500    308220    607284  34% /tmp
/dev/sda6               964500    880784     34720  97% /usr
/dev/sda7              5763616    738920   4731916  14% /var
/dev/sda9            417378712 179547644 216958220  46% /data

server_smurtz:~# df -B 4k
Filesystem           4K-blocks      Used Available Use% Mounted on
/dev/sda1               241125     59209    169667  26% /
tmpfs                  2059274         0   2059274   0% /lib/init/rw
udev                      2560        28      2532   2% /dev
tmpfs                  2059274         0   2059274   0% /dev/shm
/dev/sda5               241125     77055    151821  34% /tmp
/dev/sda6               241125    220196      8680  97% /usr
/dev/sda7              1440904    184730   1182979  14% /var
/dev/sda9            104344678  44886911  54239555  46% /data
 
server_smurtz:~# cfdisk -P s /dev/sda
Partition Table for /dev/sda

               First       Last
 # Type       Sector      Sector   Offset    Length   Filesystem Type (ID) Flag
-- ------- ----------- ----------- ------ ----------- -------------------- ----
 1 Primary           0     1959929     63     1959930 Linux (83)           Boot
 2 Primary     1959930   856409084      0   854449155 Extended (05)        None
 5 Logical     1959930     3919859     63     1959930 Linux (83)           None
 6 Logical     3919860     5879789     63     1959930 Linux (83)           None
 7 Logical     5879790    17591174*    63    11711385 Linux (83)           None
 8 Logical    17591175*   21494969     63     3903795 Linux swap / So (82) None
 9 Logical    21494970   856409084     63   834914115 Linux (83)           None

server_smurtz:~# fdisk -s /dev/sda9
417457026
  • il faut arrêter MySQL qui utilise cette partition, puis démonter la partition
server_smurtz:~# /etc/init.d/mysql.server stop
server_smurtz:~# umount /data
  • ensuite on force un fsck, sinon resize2fs refusera toute action
server_smurtz:~# e2fsck -f /dev/sda9
e2fsck 1.41.3 (12-Oct-2008)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
data: 127010/407680 files (0.7% non-contiguous), 44906489/104364256 blocks

server_smurtz:~# e2fsck -D /dev/sda9
e2fsck 1.41.3 (12-Oct-2008)
data: clean, 127010/407680 files, 44906489/104364256 blocks
  • on repartitionne à 198Go, pour laisser 2Go de marge pour le repartitionnement de la partition "physique"
server_smurtz:~# date -R; resize2fs -p /dev/sda9 198G; date -R
Thu, 26 Feb 2009 08:53:36 +0100
resize2fs 1.41.3 (12-Oct-2008)
Resizing the filesystem on /dev/sda9 to 51904512 (4k) blocks.
Begin pass 2 (max = 36858691)
Relocating blocks             XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Begin pass 3 (max = 3185)
Scanning inode table          XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Begin pass 4 (max = 9302)
Updating inode references     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The filesystem on /dev/sda9 is now 51904512 blocks long.

Thu, 26 Feb 2009 12:59:00 +0100
  • un peu plus de 4h tout de même, sur des disques SAS 15kpm...
  • je modifie le point de montage ainsi que la config de mysql
/data devient /var/local

server_smurtz:~# cat /etc/fstab
# /etc/fstab: static file system information.
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults        0       0
/dev/sda1       /               ext3    defaults,errors=remount-ro 0       1
/dev/sda5       /tmp            ext3    defaults        0       2
/dev/sda6       /usr            ext3    defaults        0       2
/dev/sda7       /var            ext3    defaults        0       2
/dev/sda8       none            swap    sw              0       0
/dev/sda9       /var/local      ext3    defaults        0       2
/dev/scd0       /media/cdrom0   udf,iso9660 user,noauto     0       0
/dev/hda        /media/cdrom1   udf,iso9660 user,noauto     0       0


server_smurtz:~# rgrep /var/local /etc/mysql
/etc/mysql/my.cnf:datadir                                       = /var/local/mysql
/etc/mysql/my.cnf:#log-bin                              = /var/local/mysql_binlog/mysql-bin.log
/etc/mysql/my.cnf:innodb_data_home_dir                  = /var/local/mysql
/etc/mysql/conf.d/server_smurtz.cnf:#log-bin              = /var/local/mysql_binlog/mysql-bin.log
/etc/mysql/conf.d/server_smurtz.cnf:#log                  = /var/local/mysql_binlog/mysql.log
/etc/mysql/conf.d/server_smurtz.cnf:log-slow-queries      = /var/local/mysql_log/mysql-slow.log
  • re fsck après le redimensionnement, et avant le montage
server_smurtz:~# e2fsck -f /dev/sda9
e2fsck 1.41.3 (12-Oct-2008)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
data: 127010/202752 files (0.7% non-contiguous), 44893808/51904512 blocks
  • test de montage
server_smurtz:~# mount /var/local
server_smurtz:~# umount /var/local
  • maintenant il faut réduire la partition, en la supprimant, et en créant une partition de 200Go. 200Go = 200x1024x1024 = 209715200k
server_smurtz:~# fdisk /dev/sda

The number of cylinders for this disk is set to 53309.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)

Command (m for help): p

Disk /dev/sda: 438.4 GB, 438489317376 bytes
255 heads, 63 sectors/track, 53309 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x00000080

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1         122      979933+  83  Linux
/dev/sda2             123       53309   427224577+   5  Extended
/dev/sda5             123         244      979933+  83  Linux
/dev/sda6             245         366      979933+  83  Linux
/dev/sda7             367        1095     5855661   83  Linux
/dev/sda8            1096        1338     1951866   82  Linux swap / Solaris
/dev/sda9            1339       53309   417457026   83  Linux

Command (m for help): d
Partition number (1-9): 9

Command (m for help): n
Command action
   l   logical (5 or over)
   p   primary partition (1-4)
l
First cylinder (1339-53309, default 1339): 1339
Last cylinder or +size or +sizeM or +sizeK (1339-53309, default 53309): +209715200K

Command (m for help): p

Disk /dev/sda: 438.4 GB, 438489317376 bytes
255 heads, 63 sectors/track, 53309 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x00000080

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1         122      979933+  83  Linux
/dev/sda2             123       53309   427224577+   5  Extended
/dev/sda5             123         244      979933+  83  Linux
/dev/sda6             245         366      979933+  83  Linux
/dev/sda7             367        1095     5855661   83  Linux
/dev/sda8            1096        1338     1951866   82  Linux swap / Solaris
/dev/sda9            1339       27447   209720511   83  Linux

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 16: Device or resource busy.
The kernel still uses the old table.
The new table will be used at the next reboot.
Syncing disks.
  • reboot
server_smurtz:~# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda1             942M  232M  663M  26% /
tmpfs                 7.9G     0  7.9G   0% /lib/init/rw
udev                   10M  112K  9.9M   2% /dev
tmpfs                 7.9G     0  7.9G   0% /dev/shm
/dev/sda5             942M   18M  877M   2% /tmp
/dev/sda6             942M  861M   34M  97% /usr
/dev/sda7             5.5G  724M  4.6G  14% /var
/dev/sda9             198G  171G   18G  91% /var/local
  • on peut maintenant ajuster la partition pour qu'elle fit bien dans les 200Go
server_smurtz:~# /etc/init.d/mysql.server stop
Shutting down MySQL................................
server_smurtz:~# umount /var/local

server_smurtz:~# e2fsck -f /dev/sda9
e2fsck 1.41.3 (12-Oct-2008)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
mysql: 127016/202752 files (0.7% non-contiguous), 45231149/51904512 blocks
server_smurtz:~# date -R; resize2fs /dev/sda9; date -R
Thu, 26 Feb 2009 13:51:54 +0100
resize2fs 1.41.3 (12-Oct-2008)
Resizing the filesystem on /dev/sda9 to 52430127 (4k) blocks.
The filesystem on /dev/sda9 is now 52430127 blocks long.

Thu, 26 Feb 2009 13:52:04 +0100
server_smurtz:~# e2fsck -f /dev/sda9
e2fsck 1.41.3 (12-Oct-2008)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
mysql: 127016/204928 files (0.7% non-contiguous), 45231251/52430127 blocks
server_smurtz:~# mount /var/local
 * on a gagné (52430127-51904512) x 4048 / 1024 / 1024 = 2029Mo
  • maintenant, création d'une partition sur sda10 avec un max d'inodes
server_smurtz:~# mkfs.ext3 -L data -O dir_index -b 2048 -i 2048 /dev/sda10
mke2fs 1.41.3 (12-Oct-2008)
Filesystem label=data
OS type: Linux
Block size=2048 (log=1)
Fragment size=2048 (log=1)
103874560 inodes, 103868240 blocks
5193412 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=641728512
6340 block groups
16384 blocks per group, 16384 fragments per group
16384 inodes per group
Superblock backups stored on blocks:
        16384, 49152, 81920, 114688, 147456, 409600, 442368, 802816, 1327104,
        2048000, 3981312, 5619712, 10240000, 11943936, 35831808, 39337984,
        51200000

Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 34 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.


server_smurtz:~# tune2fs -l /dev/sda10
tune2fs 1.41.3 (12-Oct-2008)
Filesystem volume name:   data
Last mounted on:          <not available>
Filesystem UUID:          189850f6-fb66-40e3-b188-750a7f5901e7
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype sparse_super
Filesystem flags:         signed_directory_hash
Default mount options:    (none)
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              103874560
Block count:              103868240
Reserved block count:     5193412
Free blocks:              90827363
Free inodes:              103874549
First block:              0
Block size:               2048
Fragment size:            2048
Reserved GDT blocks:      512
Blocks per group:         16384
Fragments per group:      16384
Inodes per group:         16384
Inode blocks per group:   2048
Filesystem created:       Thu Feb 26 13:43:59 2009
Last mount time:          n/a
Last write time:          Thu Feb 26 13:49:34 2009
Mount count:              0
Maximum mount count:      34
Last checked:             Thu Feb 26 13:43:59 2009
Check interval:           15552000 (6 months)
Next check after:         Tue Aug 25 14:43:59 2009
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:               256
Required extra isize:     28
Desired extra isize:      28
Journal inode:            8
Default directory hash:   half_md4
Directory Hash Seed:      41f7656a-cf10-4abe-b88c-483c919b3750
Journal backup:           inode blocks
  • fstab devient, avec l'option noatime pour ces 2 partitions de données :
server_smurtz:~# cat /etc/fstab
# /etc/fstab: static file system information.
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults        0       0
/dev/sda1       /               ext3    defaults,errors=remount-ro 0       1
/dev/sda5       /tmp            ext3    defaults        0       2
/dev/sda6       /usr            ext3    defaults        0       2
/dev/sda7       /var            ext3    defaults        0       2
/dev/sda8       none            swap    sw              0       0
/dev/sda9       /var/local      ext3    defaults,noatime        0       2
/dev/sda10      /data           ext3    defaults,noatime        0       2
/dev/scd0       /media/cdrom0   udf,iso9660 user,noauto     0       0
/dev/hda        /media/cdrom1   udf,iso9660 user,noauto     0       0
  • et voilà ! Après redimenssionnement, voilà ce que donne l'état des partitions et des inodes :
server_smurtz:~# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda1             942M  232M  663M  26% /
tmpfs                 7.9G     0  7.9G   0% /lib/init/rw
udev                   10M  116K  9.9M   2% /dev
tmpfs                 7.9G     0  7.9G   0% /dev/shm
/dev/sda5             942M   18M  877M   2% /tmp
/dev/sda6             942M  861M   34M  97% /usr
/dev/sda7             5.5G  724M  4.6G  14% /var
/dev/sda10            174G   83M  164G   1% /data
/dev/sda9             200G  173G   18G  91% /var/local
server_smurtz:~# df -i
Filesystem            Inodes   IUsed   IFree IUse% Mounted on
/dev/sda1             122624   11923  110701   10% /
tmpfs                2059274       6 2059268    1% /lib/init/rw
udev                 2059274     987 2058287    1% /dev
tmpfs                2059274       1 2059273    1% /dev/shm
/dev/sda5             122624      18  122606    1% /tmp
/dev/sda6             122624   29656   92968   25% /usr
/dev/sda7             732960    6266  726694    1% /var
/dev/sda10           103874560      11 103874549    1% /data
/dev/sda9             204928  127020   77908   62% /var/local

Ca bouge chez MySQL !

Ces 2 dernières semaines ont été bien mouvementées chez MySQL !

Sortie de la 5.1.31

Cette version est importante car elle corrige de nombreux bugs critiques de la version GA 5.1.30, qui n'était tout simplement... pas stable. Cette nouvelle version est sortie le 19 janvier mais n'est apparu sur le site que bien plus tard... Il faut mieux surveiller les mirroirs FTP. En prod sur 6 serveurs depuis fin janvier, pour l'instant aucun crash à signaler, le bug semble donc bien corrigé.

Le père de MySQL, Monty, quitte SUN

Monty quitte SUN en bon terme, et monte sa société Monty Program AB, basée sur un modèle idéaliste ou tous les employés sont actionnaire (voir le détail), et surtout, ou l'open-source a toute son importance. Monty n'abandonne pas MySQL et va se consacrer à son moteur crash safe MariaDB. Mixer MariaDB, le moteur XtraDB de Percona, le tout dans Drizzle, et on a la base de donnée la plus performante du monde :)

Marten Mikos aussi !

Marten Mikos, directeur de MySQL, vice president de la section base de donnée chez SUN, quitte SUN à son tour.

Un an après le rachat par Sun de MySQL, les départs commencent... Avaient-ils un contrat leur obligeant de rester 1 an ? Une rumeur (à prendre comme tel) parle d'un départ d'une 20ène de développeurs MySQL pour Monty Program AB.

MySQL 5.1.31

MySQL devrait sortir ce mois-ci la version 5.1.31, qui corrigera entre autre le bug #38883 concernant InnoDB, qui cause des crashs aléatoires, lors de l'interrogation du status innoDB du serveur:

  • SHOW INNODB STATUS
  • innodb-status-file=1
  • MySQL enterprise monitor

Performances MySQL 5.1: 2nd round

Dans le billet précédent, j'ai eu un premier aperçu de MySQL 5.1.30 au niveau des performances, à comparaison égale avec 5.0.76, où on peut constater une nette dégradation des performances, notamment au niveau des accès disques (IO).

J'ai voulu profiter de cet environnement de prod en test un peu plus, parce que c'est pas tous les jours qu'on a ce genre d'occasion ! Parce que la prod, comme son nom l'indique, n'est pas un environnement de tests.... passons, je sais que c'est mal, mais je sais que si ça crash, rien ne se verra, les applications continueront de fonctionner sans erreurs, vive le web 2.0 !!

J'ai donc pu tester une des nouvelles feature de 5.1: le partitionnement. En terme de performance (toujours, toujours...) ça donne quoi ?

partionnement mysql 5.1

Ce graph CPU représente la phase de partitionnement: 3 tables de ~100 millions d'enregistrements chacunes. 99% des requêtes SQL concerne un status: WHERE status=1, j'ai donc partitionné ces tables par HASH sur cette colonne: la partition interrogé ne fait plus qu' 1/3 !!

L'impact sur les perfs est sans appels, et là MySQL 5.1 devient plus performant que 5.0. Sur le graph CPU suivant, on peut distinguer 3 périodes :

  • MySQL 5.0.76
  • MySQL 5.1.30
  • MySQL 5.1.30 avec tables partitionnées

Impact 5.1 partionnement

Au niveau stabilité, le serveur n'a jamais planté, que ce soit lors du partitionnement, avant ou après. Cependant j'ai quelques problèmes avec son master qui est aussi en 5.1.30 et qui a crashé 2x en 1 mois... Un ticket est ouvert chez mysql.

MySQL 5.1 en prod: impact sur les performances

MySQL 5.1 GA est sortie en grande pompe, et tout le monde y va de son billet, pour ou contre. J'ai pu le mettre en environnement de production en mode master/slave et slave: le premier est slave d'un 5.0.56, et master d'un 2ème serveur en 5.1.30.

On peut nettement apercevoir sur le graph CPU suivant, la mise en prod de la version 5.1.30, avec une très nette augmentation des IO disques ! Le slave (22), en 5.0, prenait parfois du délais lors de la regénération de tables, en 5.1 il prend nettement plus de délais. Je n'ai même pas testé les nouvelles fonctionnalités comme le partitionnement, ayant lu sur plusieurs articles que cette version était encore trop buguée si on les utilisait...

Ah oui, et c'est pas tout... Le master/slave (21) a lamentablement crashé, après 6 jours seulement d'utilisation ! Un ticket est ouvert chez MySQL, qui n'a pas de solution, et attend le prochain crash avec cette fois les core dump d'activés. En espérant que ce crash n'arrive pas pendant les fêtes !

MySQL 5.0 => 5.1

GlusterFS: création des packages Debian

Des packages existent déjà, merci à Leonardo Rodrigues de Mello. Mais ils sont en version 1.3.8, et depuis pas mal de bugs ont été corrigés, de nouvelles features sont apparues, il m'a parut nécessaire de construire mes propres packages.

Une version modifiée de FUSE est conseillée, et après l'installation et quelques mois d'utilisation non-intensive, je n'ai pas rencontré de problèmes.

  • Pour les packages fuse, je ne les ai pas recompilé, il existe déjà en version patchées.
gregbox:/usr/src/glusterfs# wget "http://lmello.virt-br.org/debian/fuse/libfuse2_2.7.2-glfs8-0_amd64.deb"
gregbox:/usr/src/glusterfs# wget http://lmello.virt-br.org/debian/fuse/libfuse-dev_2.7.2-glfs8-0_amd64.deb
gregbox:/usr/src/glusterfs# wget http://lmello.virt-br.org/debian/fuse/fuse-utils_2.7.2-glfs8-0_amd64.deb
  • l'installation :
gregbox:/usr/src/glusterfs# dpkg -i libfuse2_2.7.2-glfs8-0_amd64.deb libfuse-dev_2.7.2-glfs8-0_amd64.deb
Selecting previously deselected package libfuse2.
(Reading database ... 45253 files and directories currently installed.)
Unpacking libfuse2 (from libfuse2_2.7.2-glfs8-0_amd64.deb) ...
Selecting previously deselected package libfuse-dev.
Unpacking libfuse-dev (from libfuse-dev_2.7.2-glfs8-0_amd64.deb) ...
Setting up libfuse2 (2.7.2-glfs8-0) ...

Setting up libfuse-dev (2.7.2-glfs8-0) ...
  • pour les packages gluster, ceux de lmello datent un peu, je décide donc de construire la dernière version 1.3.12 à partir de ses sources.
# http://github.com/NeilW/deb-glusterfs/tree/master
gregbox:/usr/src/glusterfs# tar xzf NeilW-deb-glusterfs-5b5312670fa6c3a87c25df577cda8c0f8198cc06.tar.gz
gregbox:/usr/src/glusterfs# mv NeilW-deb-glusterfs-5b5312670fa6c3a87c25df577cda8c0f8198cc06 deb-glusterfs
  • on récupère les sources 1.3.12
# récupération des sources de gluster
gregbox:/usr/src/glusterfs# wget http://ftp.gluster.com/pub/gluster/glusterfs/1.3/glusterfs-CURRENT.tar.gz
gregbox:/usr/src/glusterfs# tar xzf glusterfs-CURRENT.tar.gz
# suppression de patchs inutiles avec cette version :
gregbox:/usr/src/glusterfs/deb-glusterfs# rm debian/patches/02patch-server-vol-spec.patch
# on édite le changelog pour changer la version
gregbox:/usr/src/glusterfs/deb-glusterfs# vim debian/changelog
gregbox:/usr/src/glusterfs/deb-glusterfs# aapt-get install cdbs debhelper bison flex libibverbs-dev  help2man libdb4.4-dev
  • enfin, la construction elle meme. Oui, ils sont construit sous root, c'est mal, pas bien, ne reproduisez pas ça chez vous :
gregbox:/usr/src/glusterfs/deb-glusterfs# dpkg-buildpackage
gregbox:/usr/src/glusterfs/deb-glusterfs# ls -altr .. | grep 1.3.12
drwxrwxrwx 10 mysql users    4096 2008-09-02 17:39 glusterfs-1.3.12
-rw-r--r--  1 root  src    783616 2008-09-18 15:16 glusterfs_1.3.12-0.tar.gz
-rw-r--r--  1 root  src       821 2008-09-18 15:16 glusterfs_1.3.12-0.dsc
-rw-r--r--  1 root  src     12790 2008-09-18 15:18 glusterfs-examples_1.3.12-0_all.deb
-rw-r--r--  1 root  src     17862 2008-09-18 15:18 glusterfs-client_1.3.12-0_amd64.deb
-rw-r--r--  1 root  src     10740 2008-09-18 15:18 glusterfs-server_1.3.12-0_amd64.deb
-rw-r--r--  1 root  src    363240 2008-09-18 15:18 libglusterfs0_1.3.12-0_amd64.deb
-rw-r--r--  1 root  src     84916 2008-09-18 15:18 libglusterfs-dev_1.3.12-0_amd64.deb
-rw-r--r--  1 root  src    971132 2008-09-18 15:18 glusterfs-dbg_1.3.12-0_amd64.deb
-rw-r--r--  1 root  src      1603 2008-09-18 15:18 glusterfs_1.3.12-0_amd64.changes
  • installation des packages :
gregbox:/usr/src/glusterfs/deb-glusterfs# dpkg -i ../*1.3.12*.deb
(Reading database ... 46297 files and directories currently installed.)
Preparing to replace glusterfs-client 1.3.10-1 (using .../glusterfs-client_1.3.12-0_amd64.deb) ...
Unpacking replacement glusterfs-client ...
Preparing to replace glusterfs-dbg 1.3.10-1 (using .../glusterfs-dbg_1.3.12-0_amd64.deb) ...
Unpacking replacement glusterfs-dbg ...
Preparing to replace glusterfs-examples 1.3.10-1 (using .../glusterfs-examples_1.3.12-0_all.deb) ...
Unpacking replacement glusterfs-examples ...
Preparing to replace glusterfs-server 1.3.10-1 (using .../glusterfs-server_1.3.12-0_amd64.deb) ...
Stopping GlusterFS server: glusterfsd.
Unpacking replacement glusterfs-server ...
Preparing to replace libglusterfs0 1.3.10-1 (using .../libglusterfs0_1.3.12-0_amd64.deb) ...
Unpacking replacement libglusterfs0 ...
Preparing to replace libglusterfs-dev 1.3.10-1 (using .../libglusterfs-dev_1.3.12-0_amd64.deb) ...
Unpacking replacement libglusterfs-dev ...
Setting up glusterfs-examples (1.3.12-0) ...
Setting up libglusterfs0 (1.3.12-0) ...

Setting up libglusterfs-dev (1.3.12-0) ...
Setting up glusterfs-client (1.3.12-0) ...
Setting up glusterfs-dbg (1.3.12-0) ...
Setting up glusterfs-server (1.3.12-0) ...
Installing new version of config file /etc/glusterfs/glusterfs-server.vol ...
Starting GlusterFS server: glusterfsd.

And voilà ! C'était facile, même si le package source de Leonardo m'a amplement simplifié la tâche. Une autre méthode possible aurait été d'utiliser les debhelpers (dh_make).

Présentation de GlusterFS

Présentation

  • Gluster.org
  • Création d'un cluster de fichiers à la fois serveur et client

logo glusterfs

Avantages

  • GlusterFS est comparable à NFS, en beaucoup plus performant, plus scalable et prévu pour la haute-dispo.
  • Le wiki est complet avec beaucoup de docs et de tutorials.
  • L'architecture du projet permet d'empiler les fonctionnalités, on peut donc mixer les fonctionnalités pour faire du mirroring, striping, unification, read-ahead, write-behind, encryption etc.
  • Automagic self-healing si un serveur tombe.
  • Roadmap importantes avec plein de fonctionnalités intéressantes comme le mod apache, hot-plug de brick, interface web, etc.

Inconvénients

  • Le projet est jeune.
  • pas vraiment de retour d'expériences en environnement de production.
  • Fonctionne en Userspace (FUSE) plutôt que dans un module kernel. Voir les implications sur les perfs.
  • la doc est uniquement dans le wiki, un trac serait mieux...
  • à vérifier: les données ne semble synchronisés qu'une fois lu, c'est à dire que si on écrit un fichier sur serveur B, les données sont tout de suite lisible, MAIS si on écrit d'autres fichiers sur serveur B, et qu'on ne les lit pas, ils ne sont pas synchronisés. Il faut donc forcer une lecture, par exemple:
find /mnt/rep -type f -exec head -n 1 {} \;
ou, plus efficace :
find /mnt/rep -type f -mtime -1 -exec head -c 1 {} > /dev/null \;

Dans le prochain billet: l'installation.

Session PHP: Le choix

Voilà, j'ai pu passer un peu de temps sur le gestionnaire de session mcache, enfin, surtout à compléter le peu de docs que j'avais sur ce sujet. J'ai rapidement écarté mcache de mes choix, ayant un sentiment moyen, de soft pas finit, un sentiment du genre je vais avoir des problèmes avec ce truc, je sais pas pourquoi.

Déjà, il ne fonctionne qu'en 32 bits, ce qui confirme mon sentiment de soft pas finit / plus maintenu. Ensuite, dans la doc, je suis tombé sur l'appendix B, je cite: Can mcache be set up to work across a cluster of computers? No puis; sur l'appendix C: Is mcache redundant? No J'ai du loupé un chapitre ! Exit mcache.

Passons à plus intéressant: sharedance. Trivial à installer, je l'ai d'abord mis en place sur notre serveur de développement, puis j'ai fais quelques scripts de tests: stockage d'objets énormes (24Mo), mesure des temps.... mmmh ça fonctionne bien ! Aller soyons fou, je développe un script PHP pour.... la prod. Afin de ne pas affecter le site de production en cas de plantage de sharedance, ou du serveur, je rajoute sur la page principale 2 appels AJAX sur mon script:

Le premier stocke une valeur envoyé par l'appel ajax en GET, puis sur le retour de l'ajax j'appel une 2ème fois le script avec un paramètre lui demandant de récupérer la valeur précédemment stockée et de comparer avec le meme GET. Puis, je lui demande de stocker les temps de PUT, les temps de FETCH des valeurs, temps totals, les erreurs détectés.... et je lui demande aussi de stocker une chaine de 1 millions de caractères, ce qui créé des fichiers de sessions de 1.2Mo. Histoire de le soliciter encore plus, j'ai paramétré sharedance pour que les sessions expirent au bout de seulement 60 secondes, ce qui provoque des "clean" plus souvent.

Avec ce test réel, on est monté à environ 900 sessions pour 1.2Go de données sur la partition tmpfs de 2Go.

Sharedance cpu-network

Ce graph représente à droite l'utilisation CPU, à gauche le traffic réseau. En haut le serveur sharedance, en bas un des serveurs PHP. On voit nettement quand j'ai arrêté le test à 17:13. On peut constater que le serveur sharedance encaisse 300Mb/s entrant + autant en sortant, sans broncher ! La charge du serveur n'a jamais atteint 1, le serveur ayant 4 coeurs (bi-dual). Via un top, je constate aussi que sharedance utilise de manière homogène les 4 coeurs.

Les petits pics rouge sur le graph CPU de sharedance sont du à un cron qui se lance pour analyser les temps mini, avg et max stockés dans les sessions.

Les dernières stats :

  • Sessions: 1082
  • put mini: 0.000153
  • put avg: 0.019551
  • put max: 0.260032
  • fetch mini: 0.003848
  • fetch avg: 0.041149
  • fetch max: 0.525085
  • total min: 0.001805
  • total avg: 0.343540
  • total max: 0.981712

Je n'ai pas eu le temps de faire un beau graph excel :) On voit qu'il a mis au maximum 0.9 seconde pour stocker puis récupérer 1.2Mo de données, auquel il faudrait soustraire les temps d'accès aux pages PHP x2 !!

Le choix est fait !

Les gestionnaires de sessions PHP

Actuellement, pour les sessions, nous utilisons un gros serveur et MySQL avec des tables en HEAP pour stocker les sessions. Problème, ces tables ne supportent pas les types BLOB et TEXT, ce qui implique d'utilise un champ VARCHAR, limité à 255 caractères, on atteint très vite cette limite.

Je suis donc à la recherche d'alternatives, sachant qu'on va stocker plus d'informations pour éviter au maximum les cookies, et que les performances requises sont importantes: aujourd'hui environ 2000 requêtes de sessions / secondes, demain ce sera bien pire à cause du web 2.0.

MySQL Cluster

MySQL n'est pas encore sortie en version stable en 5.1, version nécéssaire pour avoir un MySQL Cluster. Avec tous les problèmes que l'on rencontre avec 5.1 (enfin, uniquement les nouvelles fonctionnalités), le temps de mise en oeuvre, le coût des serveurs, cette solution est fortement compromise. D'autant plus que MySQL n'utilise pas sa propre solution pour ses propres besoins...

Zend Cluster

Alléchant sur le papier, mais beaucoup trop cher ! Ils vont devoir s'aligner pour espérer continuer à en vendre...

memcached

http://www.danga.com/memcached/

Comme stipulé à plusieurs endroits dans la doc, memcached est un serveur de cache avant tout. Ce qui veut dire perte de données en cas d'arrêt du démon. Il a été prévu pour faire du cache, et le fait d'ailleurs très bien. Son modèle cluster est orienté scalability et pas du tout redondance ni failover. Admettons que nous avons 3 serveurs memcached en cluster, A B et C. A chaque requête PHP en écriture (ajout, remplacement d'une session), le client se connecte à tous les serveurs, créé une clef en fonction de ces serveurs, et choisis un des serveurs: B. Seul B aura la valeur pour cette clef. La requête suivante, en lecture, veut lire cette clef, le client PHP se connecte aux 3 serveurs, génère la clef, et va chercher la valeur sur B. Très bien, on répartie la charge sur ces 3 serveurs, et la mémoire n'est pas gaspillé car les données ne sont pas duppliqué. Ainsi, si B tombe, on perd uniquement les données de sessions de ce serveur, on déconnecte donc seulement 1/3 des utilisateurs. Seulement voilà, c'est pas aussi beau. Si un noeud tombe, la liste des serveurs dans le pool change, et le client va regénérer toutes les clefs !! En d'autre terme, si un noeud tombe, on perd tout le cache ! On se retrouve donc avec autant de POF (Point Of Failure) que de serveurs. Dropped.

memcachedb

http://memcachedb.org/

Reprenant le moteur de memcached, le principe est différent, et cette fois le but n'est pas de faire du cache mais de stocker des données persistentes. En mode cluster, on a un fonctionnement identique à MySQL: un noeud est maitre, les autres noeuds répliquent et peuvent être accédé en lecture. Les performances sont là, leur benchmark indique atteindre 20000 écritures / secondes, et 45000 lectures / secondes, ce qui est amplement suffisant. Mais le principe du single master to many slaves est genant: il m'arrive régulièrement de retirer un serveur de la production, pour le mettre à jour, le restaurer, ou en cas de problèmes hardware... et si c'est le master qui a un problème ? on doit créer un autre master, et changer la config des slaves afin qu'ils répliquent leurs données depuis un autre master ! Dropped.

Sharedance

http://sharedance.pureftpd.org/

Ce logiciel a été créé pour cet usage et uniquement pour cet usage. Tout comme memcached, il excèle dans son travail. Tous les commentaires que j'ai pu trouvé sur le net en font l'éloge, et surtout on retrouve tout le temps "Ca n'a jamais planté". Un utilisateur atteint 200Mbps x 2 sur un seul serveur, époustouflant ! C'est la solution quasiment idéal... et oui, le soucis, c'est que c'est un SPOF ! Si ce serveur tombe, on n'a plus de sessions. Alors on pourrait implémenter un 2ème serveur avec Heartbeat, mais ce 2ème serveur n'aurait pas les données, en cas de crash on déconnecte alors tout le monde. Sharedance reste pour l'instant la meilleur solution, même si je ne l'ai pas encore testée, ça ne saurait tardé. Il manquerait une réplication type DRBD sur une partition tmpfs, pour allier performance et failover.

mcache

http://www.mohawksoft.org/?q=node/8

mcache semble le produit idéal, sur le papier. Il stock les données en ram et les copie (si besoin) sur disque régulièrement, possède un garbage collector pour supprimer les sessions périmées, et fonctionne en environnement distribué: un mcache par serveur PHP. Il faut que je le mette en place avec plusieurs noeuds, et vérifier les points suivants : si un noeud tombe, que se passe t'il ? Il faut aussi valider les performances.

Je vais donc créer plusieurs images Xen 32bits afin de tester mcache, et en parallèle tester sharedance. La suite aux prochains billets !