22
0
Fork 0

HowtoMySQL éclaté en 4

This commit is contained in:
Jérémy Lecour 2016-11-02 12:12:48 +01:00 committed by Jérémy Lecour
parent 1d89794411
commit 177d2dc795
4 changed files with 1149 additions and 989 deletions

View File

@ -1,10 +1,10 @@
---
title: Howto MySQL
title: Howto MySQL : installation et utilisation courante.
---
Documentation officielle : <http://dev.mysql.com/doc/refman/5.5/en/>
# Installation
## Installation
Sous Debian Wheezy et Jessie, on trouve par défaut la version 5.5 :
@ -61,7 +61,7 @@ Pour changer le mot de passe, on fera simplement :
# mysqladmin password <nouveau-pass-pour-adminmysql>
~~~
# Configuration
## Configuration
Les fichiers de configuration se trouvent dans `/etc/mysql/` et notamment `my.cnf` qui centralise toutes les directives.
@ -78,385 +78,116 @@ bind-address = 0.0.0.0
Selon les ressources de la machine, il faut optimiser les directives dans `my.cnf` (par défaut, la configuration est adaptée… pour un petit serveur !).
Sous Debian, on trouvera quelques exemples dans le répertoire `/usr/share/doc/mysql-server-5.0/examples/`
Voici les paramètres essentiels :
Popur plus d'information sur l'optimisation, consultez le guide [HowtoMySQLOptimize].
~~~{.ini}
[mysqld]
## Utilisation courante
###### Connexions
# Maximum de connexions concurrentes (defaut = 100)… provoque un "Too many connections"
max_connections = 250
# Maximum de connexions en attente en cas de max_connections atteint (defaut = 50)
back_log = 100
# Maximum d'erreurs avant de blacklister un hote
max_connect_errors = 10
# Loguer les requetes trop longues
log_slow_queries = /var/log/mysql/mysql-slow.log
long_query_time = 2
###### Tailles
# Taille reservee au buffer des index MyIsam
# A ajuster selon les resultats
key_buffer = 512M
# Taille max des paquets envoyés/reçus … provoque un "Packet too large"
max_allowed_packet = 64M
# Taille de la memoire reserve pour un thread
thread_stack = 192K
# A mettre le nombre de CPU x2
thread_cache_size = 8
# Taille maximum des tables de type MEMORY
max_heap_table_size = 64M
###### Cache
# max_connections x nbre max de tables dans une jointure (defaut = 64)
table_cache = 1500
# Taille max des requetes cachees (defaut = 1M)
query_cache_limit = 8M
# Taille reservee pour le cache (defaut = 0)
query_cache_size = 256M
# Type de requetes a cacher (defaut tout peut etre cache)
query_cache_type = 1
###### InnoDB
# Si InnoDB n'est pas utilise… le desactiver
#skip-innodb
# En general, il est plus optimum d'avoir un fichier par table
innodb_file_per_table
# Taille memoire allouee pour le cache des datas et index
# A ajuster en fonction de sa RAM (si serveur dedie a MySQL, on peut aller jusqu'a 80%)
innodb_buffer_pool_size = 2G
# Taille buffer memoire pour structures internes InnoDB
innodb_additional_mem_pool_size = 16M
# Nombre maximum de threads systeme concurents
innodb_thread_concurrency = 16
# Ajuste la valeur des logs InnoDB
# (attention, il faut ensuite stopper MySQL et effacer les fichiers ib_logfile*)
innodb_log_file_size = 128M
innodb_log_files_in_group = 2
###### Misc
# charset utf8 par defaut
default-character-set=utf8
~~~
Après quelques temps d'utilisation, il est très intéressant de regarder les résultats du script `mysqltuner.pl` téléchargeable sur http://mysqltuner.pl/
Créer une nouvelle base de données nommée _NOM_BASE_ :
~~~
perl mysqltuner.pl
>> MySQLTuner 1.0.1 - Major Hayden <major@mhtx.net>
>> Bug reports, feature requests, and downloads at http://mysqltuner.com/
>> Run with '--help' for additional options and output filtering
-------- General Statistics --------------------------------------------------
[--] Skipped version check for MySQLTuner script
[OK] Currently running supported MySQL version 5.0.51a-24+lenny3
[OK] Operating on 64-bit architecture
-------- Storage Engine Statistics -------------------------------------------
[--] Status: +Archive -BDB -Federated +InnoDB -ISAM -NDBCluster
[--] Data in MyISAM tables: 556M (Tables: 3831)
[--] Data in InnoDB tables: 34M (Tables: 968)
[--] Data in MEMORY tables: 3K (Tables: 9)
[!!] Total fragmented tables: 335
-------- Performance Metrics -------------------------------------------------
[--] Up for: 20h 59m 23s (2M q [29.301 qps], 31K conn, TX: 5B, RX: 300M)
[--] Reads / Writes: 23% / 77%
[--] Total buffers: 58.0M global + 2.6M per thread (100 max threads)
[OK] Maximum possible memory usage: 320.5M (10% of installed RAM)
[OK] Slow queries: 0% (1/2M)
[OK] Highest usage of available connections: 11% (11/100)
[OK] Key buffer size / total MyISAM indexes: 16.0M/216.0M
[OK] Key buffer hit rate: 97.0% (31M cached / 942K reads)
[OK] Query cache efficiency: 77.2% (921K cached / 1M selects)
[!!] Query cache prunes per day: 78490
[OK] Sorts requiring temporary tables: 0% (0 temp sorts / 59K sorts)
[!!] Joins performed without indexes: 29333
[OK] Temporary tables created on disk: 25% (22K on disk / 88K total)
[OK] Thread cache hit rate: 99% (26 created / 31K connections)
[!!] Table cache hit rate: 0% (64 open / 82K opened)
[OK] Open file limit used: 12% (126/1K)
[OK] Table locks acquired immediately: 99% (1M immediate / 1M locks)
[!!] InnoDB data size / buffer pool: 34.4M/8.0M
-------- Recommendations -----------------------------------------------------
General recommendations:
Run OPTIMIZE TABLE to defragment tables for better performance
MySQL started within last 24 hours - recommendations may be inaccurate
Enable the slow query log to troubleshoot bad queries
Adjust your join queries to always utilize indexes
Increase table_cache gradually to avoid file descriptor limits
Variables to adjust:
query_cache_size (> 16M)
join_buffer_size (> 128.0K, or always use indexes with joins)
table_cache (> 64)
innodb_buffer_pool_size (>= 34M)
mysql> CREATE DATABASE NOM_BASE;
~~~
Il y a aussi tunning-prime qui est plus verbeux, et il se complète bien avec mysql-tuner. Dispo [ici](https://launchpad.net/mysql-tuning-primer/trunk).
Voir les bases de données créées :
~~~
-- MYSQL PERFORMANCE TUNING PRIMER --
- By: Matthew Montgomery -
MySQL Version 5.1.49-3-log x86_64
Uptime = 102 days 22 hrs 6 min 25 sec
Avg. qps = 196
Total Questions = 1747455439
Threads Connected = 29
Server has been running for over 48hrs.
It should be safe to follow these recommendations
To find out more information on how each of these
runtime variables effects performance visit:
http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html
Visit http://www.mysql.com/products/enterprise/advisors.html
for info about MySQL's Enterprise Monitoring and Advisory Service
SLOW QUERIES
The slow query log is enabled.
Current long_query_time = 2.000000 sec.
You have 533228 out of 1747455460 that take longer than 2.000000 sec. to complete
Your long_query_time seems to be fine
BINARY UPDATE LOG
The binary update log is NOT enabled.
You will not be able to do point in time recovery
See http://dev.mysql.com/doc/refman/5.1/en/point-in-time-recovery.html
WORKER THREADS
Current thread_cache_size = 32
Current threads_cached = 12
Current threads_per_sec = 0
Historic threads_per_sec = 0
Your thread_cache_size is fine
MAX CONNECTIONS
Current max_connections = 250
Current threads_connected = 28
Historic max_used_connections = 250
The number of used connections is 100% of the configured maximum.
You should raise max_connections
INNODB STATUS
Current InnoDB index space = 34 M
Current InnoDB data space = 77 M
Current InnoDB buffer pool free = 98 %
Current innodb_buffer_pool_size = 6.00 G
Depending on how much space your innodb indexes take up it may be safe
to increase this value to up to 2 / 3 of total system memory
MEMORY USAGE
Max Memory Ever Allocated : 7.67 G
Configured Max Per-thread Buffers : 671 M
Configured Max Global Buffers : 7.01 G
Configured Max Memory Limit : 7.67 G
Physical Memory : 23.58 G
Max memory limit seem to be within acceptable norms
KEY BUFFER
Current MyISAM index space = 4.13 G
Current key_buffer_size = 512 M
Key cache miss rate is 1 : 4965
Key buffer free ratio = 8 %
You could increase key_buffer_size
It is safe to raise this up to 1/4 of total system memory;
assuming this is a dedicated database server.
QUERY CACHE
Query cache is enabled
Current query_cache_size = 512 M
Current query_cache_used = 52 M
Current query_cache_limit = 8 M
Current Query cache Memory fill ratio = 10.34 %
Current query_cache_min_res_unit = 4 K
Your query_cache_size seems to be too high.
Perhaps you can use these resources elsewhere
MySQL won't cache query results that are larger than query_cache_limit in size
SORT OPERATIONS
Current sort_buffer_size = 2 M
Current read_rnd_buffer_size = 256 K
Sort buffer seems to be fine
JOINS
Current join_buffer_size = 132.00 K
You have had 9527 queries where a join could not use an index properly
You should enable "log-queries-not-using-indexes"
Then look for non indexed joins in the slow query log.
If you are unable to optimize your queries you may want to increase your
join_buffer_size to accommodate larger joins in one pass.
Note! This script will still suggest raising the join_buffer_size when
ANY joins not using indexes are found.
OPEN FILES LIMIT
Current open_files_limit = 5260 files
The open_files_limit should typically be set to at least 2x-3x
that of table_cache if you have heavy MyISAM usage.
You currently have open more than 75% of your open_files_limit
You should set a higher value for open_files_limit in my.cnf
TABLE CACHE
Current table_open_cache = 2500 tables
Current table_definition_cache = 256 tables
You have a total of 5084 tables
You have 2500 open tables.
Current table_cache hit rate is 0%
, while 100% of your table cache is in use
You should probably increase your table_cache
You should probably increase your table_definition_cache value.
TEMP TABLES
Current max_heap_table_size = 64 M
Current tmp_table_size = 256 M
Of 8549110 temp tables, 35% were created on disk
Effective in-memory tmp_table_size is limited to max_heap_table_size.
Perhaps you should increase your tmp_table_size and/or max_heap_table_size
to reduce the number of disk-based temporary tables
Note! BLOB and TEXT columns are not allow in memory tables.
If you are using these columns raising these values might not impact your
ratio of on disk temp tables.
TABLE SCANS
Current read_buffer_size = 128 K
Current table scan ratio = 50592 : 1
You have a high ratio of sequential access requests to SELECTs
You may benefit from raising read_buffer_size and/or improving your use of indexes.
TABLE LOCKING
Current Lock Wait ratio = 1 : 115
You may benefit from selective use of InnoDB.
If you have long running SELECT's against MyISAM tables and perform
frequent updates consider setting 'low_priority_updates=1'
If you have a high concurrency of inserts on Dynamic row-length tables
consider setting 'concurrent_insert=2'.
mysql> SHOW DATABASES;
~~~
## Activer le support des large pages
Le support des _large pages_ permet d'avoir une gestion différente de la mémoire, notamment
utile pour les requêtes avec des besoins en mémoire important. Ceci n'est utile dans la plupart
des cas a priori.
Pour l'activer, il faudra vérifier que le kernel supporte les _large pages_ :
Utiliser la base de données _NOM_BASE_ :
~~~
# cat /proc/meminfo | grep -i huge
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
mysql> USE NOM_BASE
~~~
Si cela ne renvoie rien c'est que le support des _large pages_ est désactivé.
(options `CONFIG_HUGETLBFS` et `CONFIG_HUGETLB_PAGE` du noyau).
Pour l'activer, on fera les opérations suivantes :
Voir les tables créées :
~~~
# Set the number of pages to be used.
# Each page is normally 2MB, so a value of 20 = 40MB.
# This command actually allocates memory, so this much
# memory must be available.
# echo 20 > /proc/sys/vm/nr_hugepages
# Set the group number that is permitted to access this
# memory (110 in this case). The mysql user must be a
# member of this group.
# echo 110 > /proc/sys/vm/hugetlb_shm_group
# Increase the amount of shmem permitted per segment
# (900M in this case).
# echo 943718400 > /proc/sys/kernel/shmmax
mysql> SHOW TABLES;
~~~
Ajouter dans le `/etc/security/limits.conf` :
Créer une table nommée test avec différents champs :
~~~
ulimit -l unlimited
mysql> CREATE TABLE test (id INT not null AUTO_INCREMENT, prenom VARCHAR
(50) not null , nom VARCHAR (50) not null , ne_le DATE not null ,
ville VARCHAR (90), enfants INT, PRIMARY KEY (id));
~~~
Enfin dans la partie `[mysqld]` de `my.cf` ajouter :
Décrire une table :
~~~
large-pages
mysql> DESC test;
~~~
Mais encore une fois, cela n'est pas nécessaire dans la plupart des cas.
### À propos de shmmax
Since shm (shared memory) in Unix/Linux is only used to share memory
between independent processes and mysql server is a single process
(multi-threaded), I am convinced that shmmax value has no consequence
on mysql. (OTOH Oracle has a multiprocess architecture and uses
shared memory).
Il semblerait que contrairement à PostgreSQL, il ne soit pas nécessaire d'augmenter le kernel.shmmax dans la plupart des cas [Source](http://lists.mysql.com/mysql/135019).
## Benchmark avec sql-bench
Récupérer les fichiers de sql-bench avec bazar :
Ajouter un champ à une table :
~~~
$ bzr branch lp:sql-bench
mysql> ALTER TABLE test ADD another VARCHAR(100) DEFAULT NULL;
~~~
Installer quelques outils nécessaire pour le benchmark :
Ajouter un champ à une table en précisant sa place :
~~~
# aptitude install libclass-dbi-perl libdbd-mysql-perl mysql-client-5.1
mysql> ALTER TABLE test ADD another VARCHAR(100) DEFAULT NULL AFTER prenom;
~~~
Créer une base de données de test :
Supprimer un champ à une table :
~~~
# mysqladmin create test
# mysql
mysql> GRANT ALL PRIVILEGES ON test.* TO 'test'@localhost IDENTIFIED BY 'test';
mysql> ALTER TABLE test DROP another;
~~~
Résultat du benchmark :
Renommer un champ :
~~~
~/mysql-5.5.12/sql-bench/sql-bench# perl run-all-tests --user='test' --password='test'
Benchmark DBD suite: 2.15
Date of test: 2011-05-19 14:01:18
Running tests on: Linux 2.6.32-5-amd64 x86_64
Arguments:
Comments:
Limits from:
Server version: MySQL 5.1.49 3
Optimization: None
Hardware:
Operation seconds usr sys cpu tests
alter-table: Total time: 16 wallclock secs ( 0.08 usr 0.00 sys + 0.00 cusr 0.00 csys = 0.08 CPU)
ATIS: Total time: 4 wallclock secs ( 2.34 usr 0.77 sys + 0.00 cusr 0.00 csys = 3.11 CPU)
big-tables: Total time: 5 wallclock secs ( 0.92 usr 2.47 sys + 0.00 cusr 0.00 csys = 3.39 CPU)
connect: Total time: 18 wallclock secs ( 4.85 usr 2.88 sys + 0.00 cusr 0.00 csys = 7.73 CPU)
create: Total time: 53 wallclock secs ( 3.86 usr 1.75 sys + 0.00 cusr 0.00 csys = 5.61 CPU)
insert: Total time: 618 wallclock secs (221.17 usr 14.05 sys + 0.00 cusr 0.00 csys = 235.22 CPU)
select: Total time: 49 wallclock secs (11.28 usr 8.95 sys + 0.00 cusr 0.00 csys = 20.23 CPU)
transactions: Test skipped because the database doesn't support transactions
wisconsin: Total time: 5 wallclock secs ( 1.93 usr 0.47 sys + 0.00 cusr 0.00 csys = 2.40 CPU)
TOTALS 788.00 244.02 30.97 274.99 2913950
mysql> ALTER TABLE test CHANGE COLUMN another anotherone TEXT;
~~~
# Administration
Changer le type d'un champ :
~~~
mysql> ALTER TABLE test CHANGE another another enum('foo',bar');
~~~
Insertion de données dans une table :
~~~
mysql> INSERT INTO test VALUES (1,'jp','papin','2005-06-12','Marseille',2);
INSERT INTO test (id,prenom,nom,ne_le) VALUES (2,'c','waddle','2004-06-17');
~~~
Sélectionner tous les champs d'une table :
~~~
mysql> SELECT * FROM test;
~~~
Effacer des données d'une table :
~~~
mysql> DELETE FROM test WHERE nom='waddle';
~~~
Effacer TOUTES les données d'une table :
~~~
mysql> DELETE FROM test;
~~~
Supprimer une table :
~~~
mysql> DROP TABLE test;
~~~
Supprimer une base de données :
~~~
mysql> DROP DATABASE NOM_BASE;
~~~
## Administration
On crée une base de données et un utilisateur associé :
@ -466,7 +197,7 @@ On crée une base de données et un utilisateur associé :
mysql> GRANT ALL PRIVILEGES ON NOM_BASE.* TO 'USER_BASE'@localhost IDENTIFIED BY 'XXX';
~~~
Cette opération revient à insérer des lignes brutes dans les tables `mysql.user` et `mysql.db` ainsi (
Cette opération revient à insérer des lignes brutes dans les tables `mysql.user` et `mysql.db` :
~~~
mysql> INSERT INTO user VALUES ('localhost','USER_BASE',password('XXX'),'N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N',_,'','','',0,0,0,0,_,NULL);
@ -534,7 +265,7 @@ mysql> desc user;
42 rows in set (0.00 sec)
~~~
Par exemple, pour permettre à un utilisateur (ici `debian-sys-maint`) de faire des "SHOW VIEW" :
Par exemple, pour permettre à un utilisateur (ici `debian-sys-maint`) de faire des `SHOW VIEW` :
~~~
mysql> UPDATE user SET Show_view_priv='Y' WHERE User='debian-sys-maint';
@ -559,19 +290,21 @@ On peut aussi gérer des droits sur les tables :
mysql> GRANT Select,Insert,Update ON BASE.TABLE TO 'USER_BASE'@localhost;
~~~
Pour révoquer des droits sur une table, on utilisera REVOKE :
Pour révoquer des droits sur une table, on utilisera `REVOKE` :
~~~
mysql> REVOKE ALL PRIVILEGES ON BASE.TABLE FROM 'USER_BASE'@localhost;
~~~
Un droit est particulier : pour utiliser LOAD DATA INFILE ou SELECT INTO OUTFILE, il faut avoir le droit FILE … mais il est global (et dangereux) !
Un droit est particulier : pour utiliser LOAD DATA INFILE ou SELECT INTO OUTFILE, il faut avoir le droit `FILE` … mais il est global (et dangereux) !
On le positionnera ainsi :
~~~
mysql> GRANT FILE ON *.* TO 'USER_BASE'@localhost;
~~~
### Vérifications et réparations
Pour vérifier et réparer toutes les tables (une sorte de `fsck` pour les tables),
on lancera la commande suivante :
@ -614,31 +347,38 @@ En cas d'echec (segfault par exemple), on peut tenter :
# myisamchk --safe-recover -v -f --key_buffer_size=512M --sort_buffer_size=512M --read_buffer_size=4M --write_buffer_size=4M /var/lib/mysql/BASE/TABLE.MYD
~~~
## Problème de _definer_ avec les vues
Un problème classique qui peut arriver suite à une migration d'une base de données contenant des vues. Si l'utilisateur qui a créé la vue (le _definer_) n'existe pas sur le nouveau serveur, la vue sera inutilisable :
### routines MySQL
~~~
mysql> SELECT * FROM myview;
MySQL ERROR 1449 (HY000) : The user specified as a definer ('root'@'localhost') does not exist
mysql> select * from INFORMATION_SCHEMA.routines;
~~~
Il faut donc soit faire un sed dans le dump MySQL pour remplacer le definer si on est à l'étape de préparation de la migration.
Si la migration a déjà été faite et qu'il n'est plus possible de réinjecter un dump, il faut modifier la vue via `ALTER VIEW`, mais il est nécessaire d'indiquer la définition complète de la vue. Pour la connaitre :
### Changement mot de passe utilisateur
~~~
mysql> SHOW CREATE VIEW myview;
> SET PASSWORD FOR 'user'@'hostname' = PASSWORD('pwd');
~~~
Copier ensuite la définition complète, en remplaçant `CREATE` par `ALTER`, et bien sûr le definer de la vue par un utilisateur existant qui aura les droits sur la vue.
Pour lister les vues dans une base :
Par rapport à un autre utilisateur:
~~~
mysql> SHOW FULL TABLES IN mydb WHERE TABLE_TYPE LIKE 'VIEW';
mysql> use mysql;
mysql> UPDATE USER SET Password='XXXXX' WHERE User='XXXXX' and Host='XX.XX.XX.XX';
~~~
# Sauvegarde MySQL
### Changer variables global d'environnement
~~~
mysql> SET GLOBAL max_connect_errors=50;
~~~
ou
~~~
mysql> SET @@max_connect_errors=50;
~~~
## Sauvegarde
Pour sauvegarder une base de données (sans et avec compression) :
@ -749,114 +489,7 @@ mysqldump -T /tmp --fields-enclosed-by=\" --fields-terminated-by=, --no-create-d
NOTE : il est nécessaire que MySQL ait les droits d'écriture dans le répertoire de destination (ici `/tmp`).
# Utilisation
Créer une nouvelle base de données nommée NOM_BASE :
~~~
mysql> CREATE DATABASE NOM_BASE;
~~~
Voir les bases de données créées :
~~~
mysql> SHOW DATABASES;
~~~
Utiliser la base de données NOM_BASE :
~~~
mysql> USE NOM_BASE
~~~
Voir les tables créées :
~~~
mysql> SHOW TABLES;
~~~
Créer une table nommée test avec différents champs :
~~~
mysql> CREATE TABLE test (id INT not null AUTO_INCREMENT, prenom VARCHAR
(50) not null , nom VARCHAR (50) not null , ne_le DATE not null ,
ville VARCHAR (90), enfants INT, PRIMARY KEY (id));
~~~
Décrire une table :
~~~
DESC test;
~~~
Ajouter un champ à une table :
~~~
mysql> ALTER TABLE test ADD another VARCHAR(100) DEFAULT NULL;
~~~
Ajouter un champ à une table en précisant sa place :
~~~
mysql> ALTER TABLE test ADD another VARCHAR(100) DEFAULT NULL AFTER prenom;
~~~
Supprimer un champ à une table :
~~~
mysql> ALTER TABLE test DROP another;
~~~
Renommer un champ :
~~~
mysql> ALTER TABLE test CHANGE COLUMN another anotherone TEXT;
~~~
Changer le type d'un champ :
~~~
mysql> ALTER TABLE test CHANGE another another enum('foo',bar');
~~~
Insertion de données dans une table :
~~~
mysql> INSERT INTO test VALUES (1,'jp','papin','2005-06-12','Marseille',2);
INSERT INTO test (id,prenom,nom,ne_le) VALUES (2,'c','waddle','2004-06-17');
~~~
Sélectionner tous les champs d'une table :
~~~
mysql> SELECT * FROM test;
~~~
Effacer des données d'une table :
~~~
mysql> DELETE FROM test WHERE nom='waddle';
~~~
Effacer TOUTES les données d'une table :
~~~
DELETE FROM test;
~~~
Supprimer une table :
~~~
DROP TABLE test;
~~~
Supprimer une base de données :
~~~
DROP DATABASE NOM_BASE;
~~~
# Monitoring
## Monitoring
Pour surveiller un service MySQL en production, on pourra faire :
@ -952,61 +585,12 @@ log2mail.
Note : il faut ajouter l'utilisateur `log2mail` dans le groupe `adm`.
# Cache
La première fois que vous faites un SELECT, son résultat sera conservé dans le _Query Cache_ de MySQL.
Si ce cache n'a pas été invalidé, la même requête retournera directement son résultat à partir de cache,
soit un résultat quasi-instantané..
On peut avoir des stats sur ce _Query Cache_ :
~~~
mysql> SHOW STATUS LIKE 'Qcache%';
+-------------------------+-----------+
| Variable_name | Value |
+-------------------------+-----------+
| Qcache_free_blocks | 28 |
| Qcache_free_memory | 242412400 |
| Qcache_hits | 226483735 |
| Qcache_inserts | 22334124 |
| Qcache_lowmem_prunes | 5337097 |
| Qcache_not_cached | 3323695 |
| Qcache_queries_in_cache | 4229 |
| Qcache_total_blocks | 8919 |
+-------------------------+-----------+
8 rows in set (0.00 sec)
~~~
On peut vider ce cache ainsi :
~~~
mysql> RESET QUERY CACHE;
Query OK, 0 rows affected (0.00 sec)
~~~
# tmpdir
Il peut être intéressant de changer le chemin du tmpdir pour le mettre sur un disque plus rapide ou plus gros par exemple.
~~~
# mkdir /ssd/mysqltmp
# chown mysql: /ssd/mysqltmp
# chmod 700 /ssd/mysqltmp
~~~
Puis dans /etc/mysql/my.cnf.
~~~{.diff}
-tmpdir = /tmp
+tmpdir = /ssd/mysqltmp
~~~
# Gestion des binlogs
## Gestion des binlogs
Par défaut, MySQL stocke chaque requête en écriture dans des fichiers appelés *binlogs*.
## Configuration
### Configuration
Par défaut les binlogs sont conservés sur 10 jours, avec des fichiers n'excédant pas 100 Mo :
@ -1019,7 +603,7 @@ max_binlog_size = 100M
binlog_format = mixed
~~~
## Format
### Format
http://dev.mysql.com/doc/refman/5.5/en/binary-log-setting.html
@ -1039,7 +623,7 @@ Il a l'avantage d'être plus fiable pour de la réplication.
Le mode **mixed** est un bon compromis pour de la réplication : il permet de voir la plupart des requêtes en clair, mais évite le problème de fiabilité en passant en mode row quand c'est nécessaire.
## Suppression
### Suppression
Pour supprimer les binlogs antérieurs à `mysql-bin.00NNNN` :
@ -1053,7 +637,7 @@ ou par rapport à une date :
mysql> PURGE BINARY LOGS BEFORE "2011-12-07 00:00:00";
~~~
## Désactivation
### Désactivation
Pour désactiver les binlogs, on ajoutera l'option suivante dans la configuration :
@ -1061,7 +645,7 @@ Pour désactiver les binlogs, on ajoutera l'option suivante dans la configuratio
disable-log-bin
~~~
## Lecture
### Lecture
On pourra lire en ligne de commande le contenu d'un binlog via la commande :
@ -1072,7 +656,7 @@ On pourra lire en ligne de commande le contenu d'un binlog via la commande :
Note : si vous obtenez une erreur `mysqlbinlog: unknown variable 'default-character-set=utf8'` c'est que la directive `default-character-set`
a été placée dans la configuration MySQL (`/etc/mysql` ou `.my.cnf`) dans la mauvaise section : `[client]` au lieu de `[mysql]` (ou `[mysqldump]`).
## Replay
### Replay
**ATTENTION, CES MANIPULATIONS PEUVENT ÊTRE DANGEREUSES VOS DONNÉES, BIEN SAVOIR CE QUE L'ON FAIT.**
@ -1091,7 +675,7 @@ On pourra ainsi injecter le contenu d'un binlog dans une base… tout simplement
~~~
# Log des requêtes lentes
### Log des requêtes lentes
Pour débugger les applications lentes, c'est une fonctionnalité intéressante de trouver quel requête est longue.
Pour cela on peut spécifier quand une requêtes est considéré comme longue, le chemin où stocker les requêtes, et l'activation des logs.
@ -1102,431 +686,12 @@ slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
~~~
# Réplication MySQL
## Préparation
## Multiples instances MySQL
* Prérequis : disposer de deux serveurs MySQL avec un `datadir` identique
Il est possible de faire fonctionenr plusieurs instances de MySQL sur un serveur ; chacune avec ses propres données et sa propre configurtation.
Dans le cas où le futur master est en production et ne peut être arrété :
~~~
# mysqldump --master-data --all-databases > mysql.dump
~~~
`--master-data` ajoute un `CHANGE MASTER TO` dans le dump contenant les informations nécessaires au slave sur les logs (nom de fichier et position). Cette option implique `--lock-all-tables` qui bloquera toutes les tables pendant le dump.
* Autoriser les connections MySQL distantes
* Activer les logs binaires sur chaque serveur : `log_bin mixed`
* Positionner un `server-id` différent sur chaque serveur (a priori, ne pas utiliser 0…)
* Créer un utilisateur dédié pour la réplication sur chaque serveur avec le droit `REPLICATION SLAVE` : `grant replication slave on *.* to repl@'%' identified by 'XXX';`
## Activation
* Récupérer les informations sur `MASTER_LOG_FILE` et `MASTER_LOG_POS` dans le dump avec master-data. (Avec head par exemple).
* Sur le serveur B (le _slave_ en mode master-slave), exécuter :
~~~
CHANGE MASTER TO
MASTER_HOST='$MASTER_IP',
MASTER_USER='repl',
MASTER_PASSWORD='XXX',
MASTER_LOG_FILE='mysql-bin.NNNNNN',
MASTER_LOG_POS=NNN;
~~~
/!\\ Bien que non obligatoire, il est recommandé de toujours indiquer les directives MASTER_LOG_FILE et MASTER_LOG_POS /!\\
* Pour exclure une base de la réplication, dans `/etc/mysql/my.cnf` :
~~~
binlog-ignore-db = mysql
~~~
* Puis démarrer la réplication sur le serveur B avec la commande : `START SLAVE`
* Enfin, exécuter `SHOW SLAVE STATUS` pour vérifier le bon fonctionnement
## Désactivation
Pour supprimer toute trace de réplication (sauf si des infos sont en dur dans la configuration) :
~~~
mysql> RESET SLAVE;
Query OK, 0 rows affected (0.00 sec)
~~~
Pour éviter que la réplication démarre automatiquement au démarrage, on ajoutera dans la configuration :
~~~
skip-slave-start
~~~
## Trucs et astuces pour la réplication MySQL
**Astuce 0** : Une astuce parfois très utile est la possibilité d'exécuter des requêtes qui ne seront pas prises en compte
par le binlog (et donc non répliquée !). Cela nécessite le droit SUPER :
~~~
mysql> SET sql_log_bin = 0;
~~~
**Astuce 1** : Pour divers raisons (notamment la réplication de données déjà répliquées !), on devra activer l'option
suivante dans le my.cnf :
~~~
log-slave-updates
~~~
**Astuce 2** : Sauter une requête déjà présente dans les binlog sur le slave (à tester) :
https://stackoverflow.com/questions/17701524/mysql-replication-skip-statement-is-it-possible
## Etapes supplémentaires pour une réplication master-master
* Positionner la directive `auto-increment-increment = 10` sur chaque serveur
* Positionner la directive `auto-increment-offset` avec une valeur numérique différente sur chaque serveur
Exemple : `auto-increment-offset 2` sur le serveur B
* Effectuer l'étape _Activation_ dans le sens A->B et B->A
## Résolution des erreurs lors de la réplication
On vérifie les erreurs avec les commandes `SHOW SLAVE STATUS` et `SHOW MASTER STATUS`.
En cas d'erreur, il faut "simplement" résoudre l'erreur, puis relancer la réplication
avec la commande `START SLAVE`. Voici quelques erreurs possibles
* Si l'on veut zapper l'erreur en cours : `SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;`
* `Incorrect key file for table './base/table.MYI'; try to repair it` : il faut réparer la table concernée
* `Duplicate entry 'NNNNNN' for key N` : une solution *peut* être de supprimer la ligne concernée (ou de zapper l'erreur)
* Si pour une raison ou un autre, on a plein de `DUPLICATE ENTRY` mais que l'est *sûr* de vouloir les ignorer, on peut faire cela en redémarrant MySQL avec le paramètre : `slave-skip-errors = 1062` ; on peut faire également cela avec d'autres types d'erreurs. Malheureusement, il faut forcément redémarrer MySQL car cette commande ne se fait pas à chaud : http://bugs.mysql.com/bug.php?id=35611
* En cas d'erreur du type `[ERROR] Error reading packet from server: Client requested master to start replication from impossible position (server_errno=1236)`, cela signifie que la position indiquée sur le binlog du master est impossible à récupérer. On peut le vérifier avec une commande du type `mysqlbinlog mysqld-bin.00123 --start-position=251` sur le master. Si l'on constate que le binlog est corrompu avec des erreurs du type `ERROR: Error in Log_event::read_log_event(): 'read error' # Warning: this binlog is either in use or was not closed properly.` ou `ERROR: Error in Log_event::read_log_event(): 'Event too small', data_len: 0, event_type: 0` l'idée sera d'identifier les requêtes non jouées sur le slave dans le binlog corrompu (le Relay_Master_Log_File via un SHOW SLAVE STATUS) et de les rejouer (cf [wiki:HowtoMySQL#Replay procédure décrite]) puis de passer au binlog suivant via une commande du type `CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000124' , MASTER_LOG_POS=106; START SLAVE;` (la position à indiquer est souvent `106`, cf `mysqlbinlog mysql-bin.000124`). Si l'on juge cela non nécessaire (données non critiques), on pourra bien sûr passer directement au binlog suivant en ignorant les requêtes du binlog corrompu. Bien sûr, suite à ces manipulations risquées, on vérifiera ensuite la cohérence de la base de données répliquée (`COUNT(*)` ou outils plus avancés).
* En cas d'erreur du type `Could not parse relay log event entry. The possible reasons are: the master's binary log is corrupted (you can check this by running 'mysqlbinlog' on the binary log), the slave's relay log is corrupted (you can check this by running 'mysqlbinlog' on the relay log), a network problem, or a bug in the master's or slave's MySQL code. If you want to check the master's binary log or slave's relay log, you will be able to know their names by issuing 'SHOW SLAVE STATUS' on this slave.` : souvent un binlog corrompu (le Relay_Master_Log_File via un SHOW SLAVE STATUS), ~~appliquer la même solution que ci-dessus !! ~~ Jusqu'à MySQL <= 5.1 au moins, changer la position dans un Relay_log avec un `CHANGE MASTER TO` ne marche pas. Voir [#ChangementdelapositiondansunRelay_log].
* Erreur : `Got fatal error 1236 from master when reading data from binary log: 'log event entry exceeded max_allowed_packet; Increase max_allowed_packet on master'`
On obtient apparemment cela dans différents cas. L'un d'eux serait si max_allowed_packet est inférieur à read_buffer_size ; voir http://www.mysqlperformanceblog.com/2012/06/06/read_buffer_size-can-break-your-replication/ ; dans d'autre cas, il faudra forcer la réplication à se poursuivre via `STOP SLAVE; CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.00XXXX' , MASTER_LOG_POS=XXXX; START SLAVE;` ; dans un autre cas, la position indiquée n'existe pas dans le binlog ; enfin voir http://dev.mysql.com/doc/refman/5.1/en/replication-features-max-allowed-packet.html
* Dans certains cas *exceptionnels*, une solution radicale est de réinitialiser la réplication avec un `STOP SLAVE; RESET SLAVE; START SLAVE;` Attention, cela doit être fait dans de très rares cas maîtrisés (attention notamment aux conflits `DUPLICATE ENTRY` que cela risque de provoquer).
* Si un `SHOW SLAVE STATUS` ne retourne pas d'erreur mais que la réplication ne se fait pas, les logs du slave peuvent contenir une erreur du type :
[Note] Slave I/O thread: Failed reading log event, reconnecting to retry, log 'mysql-bin.003357' at position 389449
[Note] Slave: received end packet from server, apparent master shutdown:
Il se peut que le master se réplique sur 2 slaves ayant un server-id identique !
## Changement de la position dans un `Relay_log`
À faire uniquement si en tentant de changer la position d'un Relay_log sur un slave, vous obtenez cette erreur :
Error initializing relay log position: Could not find target log during
relay log initialization
Il faut alors stopper le processus slave de réplication :
~~~
mysql> STOP SLAVE;
~~~
Puis éditer (en gardant une sauvegarde) le fichier `${datadir}/relay-log.info`. La première ligne correspond au Relay_Log_File, la seconde au `Relay_Log_Pos`.
Redémarrer MySQL.
## Contrôle de l'intégrité de la réplication
### pt-table-checksum
C'est un outil de [Percona](https://www.percona.com/downloads/percona-toolkit/) intégré dans son toolkit. (Package Debian [percona-toolkit](https://packages.debian.org/search?keywords=percona-toolkit) disponible à partir de Wheezy).
Manuel : https://www.percona.com/doc/percona-toolkit/2.1/pt-table-checksum.html
L'outil vérifie l'intégrité de la réplication en effectuant des requêtes de checksum (crc32 par défaut) sur le master, puis les requêtes sont joués sur les slaves permettant de trouver des différences.
La méthode la plus simple pour l'utiliser est d'autoriser le master à se connecter au slave (authentification MySQL). Ainsi, il s'occupe lui même de faire le nécessaire pour identifier les erreurs. Il suffira donc de lancer la commande sans argument pour qu'il identifie les incohérences. On pourra réaliser un cron avec l'argument `-q` qui ne fait remonter que les erreurs.
~~~
MAILTO=jdoe@exemple.com
42 9 * * 7 pt-table-checksum -q
~~~
### pt-table-sync
Si pt-table-checksum vous a remonté des incohérences, vous pouvez avec cet outil les corriger. Cela va identifier les différences et les corriger avec un REPLACE sur le master (qui sera donc répliqué sur le slave), garantissant la cohérence des données.
Exemple :
~~~
# pt-table-sync --print --replicate percona.checksums localhost
# pt-table-sync --execute --replicate percona.checksums localhost
~~~
En cas de `Can't make changes on the master because no unique index exists`. On peut synchroniser directement les différences sur le slave depuis le master.
~~~
# pt-table-sync --execute --no-check-slave localhost slave
~~~
# Diagnostic des erreurs
## Error 24
Si vous obtenez des erreurs de ce type, lors d'un mysqldump par exemple :
~~~
mysqldump: Got error: 1016: Can't open file: './db/table.frm' (errno: 24) when using LOCK TABLES
mysqldump: Got error: 23: Out of resources when opening file '.\db\table.MYD' (Errcode: 24) when using LOCK TABLES
~~~
C'est que votre serveur MySQL tente d'ouvrir trop de fichiers simultanément.
Pour augmenter le nombre maximal de fichiers pouvant être ouverts, vous pouvez ajuster le paramètre suivant dans la section `[mysqld]` du fichier `my.cnf`, dans la limite permise par votre système d'exploitation :
~~~
open_files_limit = 2048
~~~
La valeur par défaut étant de 1024.
Note : inutile de positionner une valeur pour `ulimit -n` dans les scripts de démarrage, mysqld_safe s'en charge tout seul.
## Error 2020
Si vous obtenez l'erreur suivante lors d'un mysqldump :
~~~
mysqldump: Error 2020: Got packet bigger than 'max_allowed_packet' bytes when dumping table `mytable` at row: 6542
~~~
Augmentez la valeur de `max_allowed_packet` dans la section `[mysqldump]` du fichier `my.cnf` :
~~~{.ini}
[mysqldump]
max_allowed_packet = 64M
~~~
## Error 1267
Si vous obtenez une erreur du type :
~~~
ERROR 1267 (HY000): Illegal mix of collations (binary,IMPLICIT) and (utf8_bin,NONE) for operation 'like'
~~~
C'est qu'il y a souci entre votre charset client (`character_set_client`, `collation_connection`) et votre requête.
Vous pouvez les ajuster avec des commandes du type :
~~~
mysql> set character_set_client=utf8;
Query OK, 0 rows affected (0.00 sec)
mysql> set collation_connection=utf8_unicode_ci;
Query OK, 0 rows affected (0.00 sec)
~~~
Dans certain cas (création d'une vue par exemple), cela peut venir d'une version de MySQL trop ancienne
(on a constaté des requêtes qui passaient en 5.1 mais pas en 5.0).
## Column count of mysql.proc is wrong
Si vous avez des erreurs de ce type :
~~~
[ERROR] Column count of mysql.db is wrong. Expected 22, found 20. The table is probably corrupted
[ERROR] mysql.user has no `Event_priv` column at position 29
[ERROR] Event Scheduler: An error occurred when initializing system tables. Disabling the Event Scheduler.
[ERROR] Column count of mysql.proc is wrong. Expected 20, found 16. The table is probably corrupted
~~~
Cela signifie que les tables de la base `mysql` ne correspondent pas à la version de MySQL en cours.
Vous avez sûrement mis à jour MySQL ou réinjecter des données d'une autre base.
Plusieurs solutions, réinjecter les tables incorrectes ou utilisez `mysql_upgrade` qui est sensé adapter les tables.
## Désactiver la complétion avec l'option `--skip-auto-rehash`
En cas de souci lors de la connexion en ligne de commande MySQL,
vous pouvez désactiver la complétion automatique. En effet, cette
complétion peut créer de soucis si certaines tables sont corrompues :
~~~
$ mysql --skip-auto-rehash
~~~
## InnoDB: ERROR: the age of the last checkpoint is NNNNN
Si vous avez une erreur du type :
~~~
mysqld: 120313 12:16:10 InnoDB: ERROR: the age of the last checkpoint is 9433587,
mysqld: InnoDB: which exceeds the log group capacity 9433498.
mysqld: InnoDB: If you are using big BLOB or TEXT rows, you must set the
mysqld: InnoDB: combined size of log files at least 10 times bigger than the
mysqld: InnoDB: largest such row.
~~~
Il faut augmenter le log InnoDB, notamment _innodb_log_file_size''. Attention, il faudra ensuite stopper MySQL et effacer les fichiers ''ib_logfile*_ !
Pour plus de détails, voir :
http://www.mysqlperformanceblog.com/2008/11/21/how-to-calculate-a-good-innodb-log-file-size/
et http://dev.mysql.com/doc/refman/5.1/en/innodb-parameters.html
## `InnoDB: Error: trying to load index PRIMARY for table […] but the index tree has been freed!`
Si vous obtenez une erreur du type :
~~~
InnoDB: Error: trying to load index PRIMARY for table foo/bar
InnoDB: but the index tree has been freed!
121222 11:28:48 - mysqld got signal 11;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
We will try our best to scrape up some info that will hopefully help diagnose
the problem, but since we have already crashed, something is definitely wrong
and this may fail.
key_buffer_size=713031680
read_buffer_size=131072
max_used_connections=31
max_connections=384
threads_connected=29
It is possible that mysqld could use up to
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_connections = 1531901 K
bytes of memory
Hope that's ok; if not, decrease some variables in the equation.
thd=0x84dc0a0
Attempting backtrace. You can use the following information to find out
where mysqld died. If you see no messages after this, something went
terribly wrong…
Cannot determine thread, fp=0x84dc0a0, backtrace may not be correct.
Bogus stack limit or frame pointer, fp=0x84dc0a0, stack_bottom=0x44660000, thread_stack=196608, aborting backtrace.
Trying to get some variables.
Some pointers may be invalid and cause the dump to abort…
thd->query at 0x7f0d9c284ba0 is invalid pointer
thd->thread_id=355
The manual page at http://www.mysql.com/doc/en/Crashing.html contains
information that should help you find out what is causing the crash.
~~~
Cela semble être une table corrompue, et a priori il faut réinstaurer la table concernée.
Se connecter en ligne de commande avec l'option `mysql --skip-auto-rehash` puis
supprimer la table concernée (voir ci-dessous si besoin) et réinjecter là.
Diverses astuces sont listées ici :
http://dba.stackexchange.com/questions/23296/mysql-innodb-index-in-swap
## InnoDB: Souci lors de la création/suppression de table InnoDB
On suppose que vous utilisez bien l'option `innodb_file_per_table` comme conseillé sur cette page.
Pour supprimer une table problématique, vous pouvez éteindre MySQL
et supprimer le fichier `.frm` correspondant ! Néanmoins la table sera
toujours référencée par InnoDB, et vous devez créer un `.frm` simple
pour tromper le moteur :
~~~
mysql> create table foo(foo int) ENGINE=InnoDB;
# /etc/init.d/mysql stop
# cp /var/lib/mysql/foo/foo.frm /var/lib/mysql/foo/TABLE.frm
~~~
Quelques informations supplémentaire sur :
http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting-datadict.html
## `Error 121 : InnoDB : ERROR 1005 (HY000): Can't create table './foo/bar.frm' (errno: 121)`
Il s'agit d'un problème avec les clés. Par exemple, les clés que vous crééez
sont déjà référencée par InnoDB. Cela peut ainsi se produire si vous avez du
supprimer une table InnoDB via son fichier .frm
Une astuce possible est simplement de créer la table sans ses clés.
Une fois créée, vous devez voir les clés avec un `SHOW CREATE TABLE`.
À vous de voir si vous devez les modifier/supprimer.
## `InnoDB: Failing assertion: addr.page == FIL_NULL || addr.boffset >= FIL_PAGE_DATA`
Si le service MySQL/InnoDB refuse de démarrer à cause d'une erreur du type :
~~~
mysqld: InnoDB: Starting in background the rollback of uncommitted transactions
mysqld: 140130 16:01:44 InnoDB: Rolling back trx with id 13B87781, 3 rows to undo
mysqld: 140130 16:01:44 InnoDB: Assertion failure in thread 140516188849920 in file fut0lst.ic line 83
mysqld: InnoDB: Failing assertion: addr.page == FIL_NULL || addr.boffset >= FIL_PAGE_DATA
~~~
C'est que la base est corrompue…
Il faut sauvegarder le datadir. Puis tenter de redémarrer avec l'option `innodb_force_recovery=1`
puis `innodb_force_recovery=2` puis `innodb_force_recovery=3` etc. jusqu'à ce que le service démarre.
Attention, à partir de `innodb_force_recovery=3` vous devrez sûrement ajouter `innodb_purge_threads=0` :
~~~
innodb_force_recovery = 3
innodb_purge_threads = 0
~~~
Dès que le service démarre (il sera peut être en read-only), faites un dump de toutes vos bases MySQL.
Vous devrez sûrement repartir de zéro en recréant un datadir tout neuf (`mysql_install_db --datadir=/var/lib/mysql.new`)
et en réinjectant votre dump.
Voir http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html
## `Error 13 : mysqld: #007/usr/sbin/mysqld: File '/var/log/mysql/mysql-bin.00NNNN' not found (Errcode: 13)`
Si vous obtenez des erreurs de ce type :
~~~
mysqld: #007/usr/sbin/mysqld: File '/var/log/mysql/mysql-bin.005655' not found (Errcode: 13)
mysqld: 130202 19:49:05 [ERROR] Failed to open log (file '/var/log/mysql/mysql-bin.005655', errno 13)
mysqld: 130202 19:49:05 [ERROR] Could not open log file
mysqld: 130202 19:49:05 [ERROR] Can't init tc log
mysqld: 130202 19:49:05 [ERROR] Aborting
~~~
C'est que MySQL n'arrive pas à accéder au dernier binlog. Cela peut par exemple
être une question de droits. Pour vérifier que tout est correct, faire :
~~~
# sudo -u mysql head /var/log/mysql/mysql-bin.00NNNN
~~~
Une erreur étrange mais classique est que le fichier binlog prenne les droits de root.
On le corrigera ainsi :
~~~
# chown mysql:adm /var/log/mysql/mysql-bin.00NNNN
~~~
## `ERROR 130 (HY000): Incorrect file format '[…]'`
Si vous obtenez l'erreur ci-dessus, lors d'un mysqldump par exemple, et que les fichier ${mysql_datadir}/base/table.{MYD,MYI} sont vides mais pas le .frm, il faut réparer la table comme ceci :
~~~
# mysqlcheck --auto-repair --check --use-frm <base> <table>
~~~
## `is blocked because of many connection errors.`
Blocage pour l'IP car nombreuses erreurs sur BD. Lié à la valeur de la variable `max_connect_errors`.
Pour résoudre la situation :
~~~
# mysqladmin flush-hosts
~~~
# Instances MySQL
## Installation
### Installation
Avant toute opération, il est nécessaire de commenter cette ligne dans le `/etc/mysql/my.cnf` :
@ -1534,14 +699,14 @@ Avant toute opération, il est nécessaire de commenter cette ligne dans le `/et
#user = mysql
~~~
et de rajouter ces lignes (à voir l'utilité en fait…)
et de rajouter ces lignes (_à voir l'utilité en fait…_)
~~~{.ini}
[mysqld_multi]
user = mysqladmin
user = mysqladmin
~~~
Créez un utilisateur pour l'instance (il doit avoir un shell valide comme /bin/sh) :
Créez un utilisateur pour l'instance (il doit avoir un shell valide comme `/bin/sh`) :
~~~
useradd mysql-INSTANCE_NAME
@ -1555,18 +720,18 @@ Créez ensuite le "datadir" :
# chmod 700 /home/mysql/INSTANCE_NAME
~~~
NOTE : Sous Debian Etch, l'option `--user` n'est pas présente.
**Note** : Sous Debian Etch, l'option `--user` n'est pas présente.
Ajoutez ces lignes dans `/etc/mysql/my.cnf` :
~~~{.ini}
[mysqld1]
mysqld = /usr/bin/mysqld_safe
user = mysql-INSTANCE_NAME
port = 3307
socket = /var/run/mysqld-INSTANCE_NAME/mysqld.sock
mysqld = /usr/bin/mysqld_safe
user = mysql-INSTANCE_NAME
port = 3307
socket = /var/run/mysqld-INSTANCE_NAME/mysqld.sock
pid-file = /var/run/mysqld-INSTANCE_NAME/mysqld.pid
datadir = /home/mysql-INSTANCE_NAME
datadir = /home/mysql-INSTANCE_NAME
~~~
**Note 1** : même si l'on ne prévoit pas de faire tourner le mysqld principal sur le port 3306,
@ -1613,7 +778,7 @@ On préfère passer la commande "shutdown" en interne :
**Note** : a priori cela revient à envoyer un signal SIGTERM (kill -15) au process mysqld
## Nettoyage
### Nettoyage
Si le mysqld principal n'est pas utilisé, on désactivera le script d'init.d /etc/init.d/mysql (en ajoutant `exit 0`
au début du script ) et on pourra se créer un script `/etc/init.d/mysql-instance` du type :
@ -1656,9 +821,7 @@ esac
exit 0
~~~
## Administration
### Administration
Pour voir le statut de l'instance n°1, logiquement nommée mysqld1 (GNR=1) et tournant sur le port 3307 :
@ -1668,7 +831,7 @@ Reporting MySQL servers
MySQL server from group: mysqld1 is running
~~~
Pour l'arrêter / redémarrer, même principe (attention, `mysqld_multi` est peu verbeux) :
Pour l'arrêter/redémarrer, même principe (attention, `mysqld_multi` est peu verbeux) :
~~~
# ps auwx | grep 3307
@ -1678,37 +841,10 @@ Pour l'arrêter / redémarrer, même principe (attention, `mysqld_multi` est peu
# ps auwx | grep 3307
~~~
# routines MySQL
## Réplication
~~~
mysql> select * from INFORMATION_SCHEMA.routines;
~~~
Voir [HowtoMySQLReplication]
# Changement mot de passe utilisateur
## FAQ et erreurs courantes
~~~
> SET PASSWORD FOR 'user'@'hostname' = PASSWORD('pwd');
~~~
Par rapport à un autre utilisateur:
~~~
mysql> use mysql;
mysql> UPDATE USER SET Password='XXXXX' WHERE User='XXXXX' and Host='XX.XX.XX.XX';
~~~
# Changer variables global d'environnement
~~~
mysql> SET GLOBAL max_connect_errors=50;
~~~
ou
~~~
mysql> SET @@max_connect_errors=50;
~~~
# FAQ
Voir [wiki:HowtoMySQLFAQ]
Voir [HowtoMySQLTroubeshooting]

434
HowtoMySQLOptimize.md Normal file
View File

@ -0,0 +1,434 @@
---
title: Howto MySQL : optimisation et configuration avancée.
---
Pour le guide d'installation et d'usage courant, consultez [HowtoMySQL]
## Configuration
Rappel : selon les ressources de la machine, il faut optimiser les directives dans `my.cnf` (par défaut, la configuration est adaptée… pour un petit serveur !). Sous Debian, on trouvera quelques exemples dans le répertoire `/usr/share/doc/mysql-server-5.0/examples/`
Voici les paramètres essentiels :
~~~{.ini}
[mysqld]
###### Connexions
# Maximum de connexions concurrentes (defaut = 100)… provoque un "Too many connections"
max_connections = 250
# Maximum de connexions en attente en cas de max_connections atteint (defaut = 50)
back_log = 100
# Maximum d'erreurs avant de blacklister un hote
max_connect_errors = 10
# Loguer les requetes trop longues
log_slow_queries = /var/log/mysql/mysql-slow.log
long_query_time = 2
###### Tailles
# Taille reservee au buffer des index MyIsam
# A ajuster selon les resultats
key_buffer = 512M
# Taille max des paquets envoyés/reçus … provoque un "Packet too large"
max_allowed_packet = 64M
# Taille de la memoire reserve pour un thread
thread_stack = 192K
# A mettre le nombre de CPU x2
thread_cache_size = 8
# Taille maximum des tables de type MEMORY
max_heap_table_size = 64M
###### Cache
# max_connections x nbre max de tables dans une jointure (defaut = 64)
table_cache = 1500
# Taille max des requetes cachees (defaut = 1M)
query_cache_limit = 8M
# Taille reservee pour le cache (defaut = 0)
query_cache_size = 256M
# Type de requetes a cacher (defaut tout peut etre cache)
query_cache_type = 1
###### InnoDB
# Si InnoDB n'est pas utilise… le desactiver
#skip-innodb
# En general, il est plus optimum d'avoir un fichier par table
innodb_file_per_table
# Taille memoire allouee pour le cache des datas et index
# A ajuster en fonction de sa RAM (si serveur dedie a MySQL, on peut aller jusqu'a 80%)
innodb_buffer_pool_size = 2G
# Taille buffer memoire pour structures internes InnoDB
innodb_additional_mem_pool_size = 16M
# Nombre maximum de threads systeme concurents
innodb_thread_concurrency = 16
# Ajuste la valeur des logs InnoDB
# (attention, il faut ensuite stopper MySQL et effacer les fichiers ib_logfile*)
innodb_log_file_size = 128M
innodb_log_files_in_group = 2
###### Misc
# charset utf8 par defaut
default-character-set=utf8
~~~
## mysqltuner
Après quelques temps d'utilisation, il est très intéressant de regarder les résultats du script `mysqltuner.pl` téléchargeable sur http://mysqltuner.pl/
~~~
perl mysqltuner.pl
>> MySQLTuner 1.0.1 - Major Hayden <major@mhtx.net>
>> Bug reports, feature requests, and downloads at http://mysqltuner.com/
>> Run with '--help' for additional options and output filtering
-------- General Statistics --------------------------------------------------
[--] Skipped version check for MySQLTuner script
[OK] Currently running supported MySQL version 5.0.51a-24+lenny3
[OK] Operating on 64-bit architecture
-------- Storage Engine Statistics -------------------------------------------
[--] Status: +Archive -BDB -Federated +InnoDB -ISAM -NDBCluster
[--] Data in MyISAM tables: 556M (Tables: 3831)
[--] Data in InnoDB tables: 34M (Tables: 968)
[--] Data in MEMORY tables: 3K (Tables: 9)
[!!] Total fragmented tables: 335
-------- Performance Metrics -------------------------------------------------
[--] Up for: 20h 59m 23s (2M q [29.301 qps], 31K conn, TX: 5B, RX: 300M)
[--] Reads / Writes: 23% / 77%
[--] Total buffers: 58.0M global + 2.6M per thread (100 max threads)
[OK] Maximum possible memory usage: 320.5M (10% of installed RAM)
[OK] Slow queries: 0% (1/2M)
[OK] Highest usage of available connections: 11% (11/100)
[OK] Key buffer size / total MyISAM indexes: 16.0M/216.0M
[OK] Key buffer hit rate: 97.0% (31M cached / 942K reads)
[OK] Query cache efficiency: 77.2% (921K cached / 1M selects)
[!!] Query cache prunes per day: 78490
[OK] Sorts requiring temporary tables: 0% (0 temp sorts / 59K sorts)
[!!] Joins performed without indexes: 29333
[OK] Temporary tables created on disk: 25% (22K on disk / 88K total)
[OK] Thread cache hit rate: 99% (26 created / 31K connections)
[!!] Table cache hit rate: 0% (64 open / 82K opened)
[OK] Open file limit used: 12% (126/1K)
[OK] Table locks acquired immediately: 99% (1M immediate / 1M locks)
[!!] InnoDB data size / buffer pool: 34.4M/8.0M
-------- Recommendations -----------------------------------------------------
General recommendations:
Run OPTIMIZE TABLE to defragment tables for better performance
MySQL started within last 24 hours - recommendations may be inaccurate
Enable the slow query log to troubleshoot bad queries
Adjust your join queries to always utilize indexes
Increase table_cache gradually to avoid file descriptor limits
Variables to adjust:
query_cache_size (> 16M)
join_buffer_size (> 128.0K, or always use indexes with joins)
table_cache (> 64)
innodb_buffer_pool_size (>= 34M)
~~~
## tunning-prime
Il y a aussi tunning-prime qui est plus verbeux, et il se complète bien avec mysql-tuner. Dispo [ici](https://launchpad.net/mysql-tuning-primer/trunk).
~~~
-- MYSQL PERFORMANCE TUNING PRIMER --
- By: Matthew Montgomery -
MySQL Version 5.1.49-3-log x86_64
Uptime = 102 days 22 hrs 6 min 25 sec
Avg. qps = 196
Total Questions = 1747455439
Threads Connected = 29
Server has been running for over 48hrs.
It should be safe to follow these recommendations
To find out more information on how each of these
runtime variables effects performance visit:
http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html
Visit http://www.mysql.com/products/enterprise/advisors.html
for info about MySQL's Enterprise Monitoring and Advisory Service
SLOW QUERIES
The slow query log is enabled.
Current long_query_time = 2.000000 sec.
You have 533228 out of 1747455460 that take longer than 2.000000 sec. to complete
Your long_query_time seems to be fine
BINARY UPDATE LOG
The binary update log is NOT enabled.
You will not be able to do point in time recovery
See http://dev.mysql.com/doc/refman/5.1/en/point-in-time-recovery.html
WORKER THREADS
Current thread_cache_size = 32
Current threads_cached = 12
Current threads_per_sec = 0
Historic threads_per_sec = 0
Your thread_cache_size is fine
MAX CONNECTIONS
Current max_connections = 250
Current threads_connected = 28
Historic max_used_connections = 250
The number of used connections is 100% of the configured maximum.
You should raise max_connections
INNODB STATUS
Current InnoDB index space = 34 M
Current InnoDB data space = 77 M
Current InnoDB buffer pool free = 98 %
Current innodb_buffer_pool_size = 6.00 G
Depending on how much space your innodb indexes take up it may be safe
to increase this value to up to 2 / 3 of total system memory
MEMORY USAGE
Max Memory Ever Allocated : 7.67 G
Configured Max Per-thread Buffers : 671 M
Configured Max Global Buffers : 7.01 G
Configured Max Memory Limit : 7.67 G
Physical Memory : 23.58 G
Max memory limit seem to be within acceptable norms
KEY BUFFER
Current MyISAM index space = 4.13 G
Current key_buffer_size = 512 M
Key cache miss rate is 1 : 4965
Key buffer free ratio = 8 %
You could increase key_buffer_size
It is safe to raise this up to 1/4 of total system memory;
assuming this is a dedicated database server.
QUERY CACHE
Query cache is enabled
Current query_cache_size = 512 M
Current query_cache_used = 52 M
Current query_cache_limit = 8 M
Current Query cache Memory fill ratio = 10.34 %
Current query_cache_min_res_unit = 4 K
Your query_cache_size seems to be too high.
Perhaps you can use these resources elsewhere
MySQL won't cache query results that are larger than query_cache_limit in size
SORT OPERATIONS
Current sort_buffer_size = 2 M
Current read_rnd_buffer_size = 256 K
Sort buffer seems to be fine
JOINS
Current join_buffer_size = 132.00 K
You have had 9527 queries where a join could not use an index properly
You should enable "log-queries-not-using-indexes"
Then look for non indexed joins in the slow query log.
If you are unable to optimize your queries you may want to increase your
join_buffer_size to accommodate larger joins in one pass.
Note! This script will still suggest raising the join_buffer_size when
ANY joins not using indexes are found.
OPEN FILES LIMIT
Current open_files_limit = 5260 files
The open_files_limit should typically be set to at least 2x-3x
that of table_cache if you have heavy MyISAM usage.
You currently have open more than 75% of your open_files_limit
You should set a higher value for open_files_limit in my.cnf
TABLE CACHE
Current table_open_cache = 2500 tables
Current table_definition_cache = 256 tables
You have a total of 5084 tables
You have 2500 open tables.
Current table_cache hit rate is 0%
, while 100% of your table cache is in use
You should probably increase your table_cache
You should probably increase your table_definition_cache value.
TEMP TABLES
Current max_heap_table_size = 64 M
Current tmp_table_size = 256 M
Of 8549110 temp tables, 35% were created on disk
Effective in-memory tmp_table_size is limited to max_heap_table_size.
Perhaps you should increase your tmp_table_size and/or max_heap_table_size
to reduce the number of disk-based temporary tables
Note! BLOB and TEXT columns are not allow in memory tables.
If you are using these columns raising these values might not impact your
ratio of on disk temp tables.
TABLE SCANS
Current read_buffer_size = 128 K
Current table scan ratio = 50592 : 1
You have a high ratio of sequential access requests to SELECTs
You may benefit from raising read_buffer_size and/or improving your use of indexes.
TABLE LOCKING
Current Lock Wait ratio = 1 : 115
You may benefit from selective use of InnoDB.
If you have long running SELECT's against MyISAM tables and perform
frequent updates consider setting 'low_priority_updates=1'
If you have a high concurrency of inserts on Dynamic row-length tables
consider setting 'concurrent_insert=2'.
~~~
## Support des _large pages_
Le support des _large pages_ permet d'avoir une gestion différente de la mémoire, notamment
utile pour les requêtes avec des besoins en mémoire important. Ceci n'est utile dans la plupart
des cas a priori.
Pour l'activer, il faudra vérifier que le kernel supporte les _large pages_ :
~~~
# cat /proc/meminfo | grep -i huge
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
~~~
Si cela ne renvoie rien c'est que le support des _large pages_ est désactivé.
(options `CONFIG_HUGETLBFS` et `CONFIG_HUGETLB_PAGE` du noyau).
Pour l'activer, on fera les opérations suivantes :
~~~
# Set the number of pages to be used.
# Each page is normally 2MB, so a value of 20 = 40MB.
# This command actually allocates memory, so this much
# memory must be available.
# echo 20 > /proc/sys/vm/nr_hugepages
# Set the group number that is permitted to access this
# memory (110 in this case). The mysql user must be a
# member of this group.
# echo 110 > /proc/sys/vm/hugetlb_shm_group
# Increase the amount of shmem permitted per segment
# (900M in this case).
# echo 943718400 > /proc/sys/kernel/shmmax
~~~
Ajouter dans le `/etc/security/limits.conf` :
~~~
ulimit -l unlimited
~~~
Enfin dans la partie `[mysqld]` de `my.cf` ajouter :
~~~
large-pages
~~~
Mais encore une fois, cela n'est pas nécessaire dans la plupart des cas.
### À propos de shmmax
> Since shm (shared memory) in Unix/Linux is only used to share memory between independent processes and mysql server is a single process (multi-threaded), I am convinced that shmmax value has no consequence on mysql. (OTOH Oracle has a multiprocess architecture and uses shared memory).
Il semblerait que contrairement à PostgreSQL, il ne soit pas nécessaire d'augmenter le `kernel.shmmax` dans la plupart des cas [Source](http://lists.mysql.com/mysql/135019).
## Benchmark avec sql-bench
Récupérer les fichiers de sql-bench avec bazar :
~~~
$ bzr branch lp:sql-bench
~~~
Installer quelques outils nécessaire pour le benchmark :
~~~
# aptitude install libclass-dbi-perl libdbd-mysql-perl mysql-client-5.1
~~~
Créer une base de données de test :
~~~
# mysqladmin create test
# mysql
mysql> GRANT ALL PRIVILEGES ON test.* TO 'test'@localhost IDENTIFIED BY 'test';
~~~
Résultat du benchmark :
~~~
# cd ~/mysql-5.5.12/sql-bench/sql-bench/
# perl run-all-tests --user='test' --password='test'
Benchmark DBD suite: 2.15
Date of test: 2011-05-19 14:01:18
Running tests on: Linux 2.6.32-5-amd64 x86_64
Arguments:
Comments:
Limits from:
Server version: MySQL 5.1.49 3
Optimization: None
Hardware:
Operation seconds usr sys cpu tests
alter-table: Total time: 16 wallclock secs ( 0.08 usr 0.00 sys + 0.00 cusr 0.00 csys = 0.08 CPU)
ATIS: Total time: 4 wallclock secs ( 2.34 usr 0.77 sys + 0.00 cusr 0.00 csys = 3.11 CPU)
big-tables: Total time: 5 wallclock secs ( 0.92 usr 2.47 sys + 0.00 cusr 0.00 csys = 3.39 CPU)
connect: Total time: 18 wallclock secs ( 4.85 usr 2.88 sys + 0.00 cusr 0.00 csys = 7.73 CPU)
create: Total time: 53 wallclock secs ( 3.86 usr 1.75 sys + 0.00 cusr 0.00 csys = 5.61 CPU)
insert: Total time: 618 wallclock secs (221.17 usr 14.05 sys + 0.00 cusr 0.00 csys = 235.22 CPU)
select: Total time: 49 wallclock secs (11.28 usr 8.95 sys + 0.00 cusr 0.00 csys = 20.23 CPU)
transactions: Test skipped because the database doesn't support transactions
wisconsin: Total time: 5 wallclock secs ( 1.93 usr 0.47 sys + 0.00 cusr 0.00 csys = 2.40 CPU)
TOTALS 788.00 244.02 30.97 274.99 2913950
~~~
## Cache
La première fois que vous faites un SELECT, son résultat sera conservé dans le _Query Cache_ de MySQL.
Si ce cache n'a pas été invalidé, la même requête retournera directement son résultat à partir de cache,
soit un résultat quasi-instantané.
On peut avoir des stats sur ce _Query Cache_ :
~~~
mysql> SHOW STATUS LIKE 'Qcache%';
+-------------------------+-----------+
| Variable_name | Value |
+-------------------------+-----------+
| Qcache_free_blocks | 28 |
| Qcache_free_memory | 242412400 |
| Qcache_hits | 226483735 |
| Qcache_inserts | 22334124 |
| Qcache_lowmem_prunes | 5337097 |
| Qcache_not_cached | 3323695 |
| Qcache_queries_in_cache | 4229 |
| Qcache_total_blocks | 8919 |
+-------------------------+-----------+
8 rows in set (0.00 sec)
~~~
On peut vider ce cache ainsi :
~~~
mysql> RESET QUERY CACHE;
Query OK, 0 rows affected (0.00 sec)
~~~
## tmpdir
Il peut être intéressant de changer le chemin du tmpdir pour le mettre sur un disque plus rapide ou plus gros par exemple.
~~~
# mkdir /ssd/mysqltmp
# chown mysql: /ssd/mysqltmp
# chmod 700 /ssd/mysqltmp
~~~
Puis dans /etc/mysql/my.cnf.
~~~{.diff}
-tmpdir = /tmp
+tmpdir = /ssd/mysqltmp
~~~

179
HowtoMySQLReplication.md Normal file
View File

@ -0,0 +1,179 @@
---
title: Howto MySQL : réplication.
---
Pour le guide d'installation et d'usage courant, consultez [HowtoMySQL].
## Préparation
**Prérequis** : disposer de deux serveurs MySQL avec un `datadir` identique
Dans le cas où le futur master est en production et ne peut être arrété :
~~~
# mysqldump --master-data --all-databases > mysql.dump
~~~
`--master-data` ajoute un `CHANGE MASTER TO` dans le dump contenant les informations nécessaires au slave sur les logs (nom de fichier et position). Cette option implique `--lock-all-tables` qui bloquera toutes les tables pendant le dump.
Il faut également :
* autoriser les connections MySQL distantes
* activer les logs binaires sur chaque serveur : `log_bin mixed`
* positionner un `server-id` différent sur chaque serveur (a priori, ne pas utiliser 0…)
* créer un utilisateur dédié pour la réplication sur chaque serveur avec le droit `REPLICATION SLAVE` : `grant replication slave on *.* to repl@'%' identified by 'XXX';`
## Activation
Récupérer les informations sur `MASTER_LOG_FILE` et `MASTER_LOG_POS` dans le dump avec master-data. (Avec head par exemple).
Sur le serveur B (le _slave_ en mode master-slave), exécuter :
~~~
CHANGE MASTER TO
MASTER_HOST='$MASTER_IP',
MASTER_USER='repl',
MASTER_PASSWORD='XXX',
MASTER_LOG_FILE='mysql-bin.NNNNNN',
MASTER_LOG_POS=NNN;
~~~
**Bien que non obligatoire, il est recommandé de toujours indiquer les directives `MASTER_LOG_FILE` et `MASTER_LOG_POS`**
Pour exclure une base de la réplication, dans `/etc/mysql/my.cnf` :
~~~
binlog-ignore-db = mysql
~~~
Puis démarrer la réplication sur le serveur B avec la commande : `START SLAVE`.
Enfin, exécuter `SHOW SLAVE STATUS` pour vérifier le bon fonctionnement.
## Désactivation
Pour supprimer toute trace de réplication (sauf si des infos sont en dur dans la configuration) :
~~~
mysql> RESET SLAVE;
Query OK, 0 rows affected (0.00 sec)
~~~
Pour éviter que la réplication démarre automatiquement au démarrage, on ajoutera dans la configuration :
~~~
skip-slave-start
~~~
## Trucs et astuces pour la réplication MySQL
**Astuce 1** : Une astuce parfois très utile est la possibilité d'exécuter des requêtes qui ne seront pas prises en compte
par le binlog (et donc non répliquée !). Cela nécessite le droit SUPER :
~~~
mysql> SET sql_log_bin = 0;
~~~
**Astuce 2** : Pour divers raisons (notamment la réplication de données déjà répliquées !), on devra activer l'option
suivante dans le my.cnf :
~~~
log-slave-updates
~~~
**Astuce 3** : Sauter une requête déjà présente dans les binlog sur le slave (à tester) :
https://stackoverflow.com/questions/17701524/mysql-replication-skip-statement-is-it-possible
## Etapes supplémentaires pour une réplication _master-master_
Positionner la directive `auto-increment-increment = 10` sur chaque serveur
Positionner la directive `auto-increment-offset` avec une valeur numérique différente sur chaque serveur
Exemple : `auto-increment-offset 2` sur le serveur B
Effectuer l'étape _Activation_ dans le sens A->B et B->A
## Résolution des erreurs lors de la réplication
On vérifie les erreurs avec les commandes `SHOW SLAVE STATUS` et `SHOW MASTER STATUS`.
En cas d'erreur, il faut "simplement" résoudre l'erreur, puis relancer la réplication
avec la commande `START SLAVE`. Voici quelques erreurs possibles
* Si l'on veut zapper l'erreur en cours : `SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;`
* `Incorrect key file for table './base/table.MYI'; try to repair it` : il faut réparer la table concernée
* `Duplicate entry 'NNNNNN' for key N` : une solution *peut* être de supprimer la ligne concernée (ou de zapper l'erreur)
* Si pour une raison ou un autre, on a plein de `DUPLICATE ENTRY` mais que l'est *sûr* de vouloir les ignorer, on peut faire cela en redémarrant MySQL avec le paramètre : `slave-skip-errors = 1062` ; on peut faire également cela avec d'autres types d'erreurs. Malheureusement, il faut forcément redémarrer MySQL car cette commande ne se fait pas à chaud : http://bugs.mysql.com/bug.php?id=35611
* En cas d'erreur du type `[ERROR] Error reading packet from server: Client requested master to start replication from impossible position (server_errno=1236)`, cela signifie que la position indiquée sur le binlog du master est impossible à récupérer. On peut le vérifier avec une commande du type `mysqlbinlog mysqld-bin.00123 --start-position=251` sur le master. Si l'on constate que le binlog est corrompu avec des erreurs du type `ERROR: Error in Log_event::read_log_event(): 'read error' # Warning: this binlog is either in use or was not closed properly.` ou `ERROR: Error in Log_event::read_log_event(): 'Event too small', data_len: 0, event_type: 0` l'idée sera d'identifier les requêtes non jouées sur le slave dans le binlog corrompu (le Relay_Master_Log_File via un SHOW SLAVE STATUS) et de les rejouer (cf [wiki:HowtoMySQL#Replay procédure décrite]) puis de passer au binlog suivant via une commande du type `CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000124' , MASTER_LOG_POS=106; START SLAVE;` (la position à indiquer est souvent `106`, cf `mysqlbinlog mysql-bin.000124`). Si l'on juge cela non nécessaire (données non critiques), on pourra bien sûr passer directement au binlog suivant en ignorant les requêtes du binlog corrompu. Bien sûr, suite à ces manipulations risquées, on vérifiera ensuite la cohérence de la base de données répliquée (`COUNT(*)` ou outils plus avancés).
* En cas d'erreur du type `Could not parse relay log event entry. The possible reasons are: the master's binary log is corrupted (you can check this by running 'mysqlbinlog' on the binary log), the slave's relay log is corrupted (you can check this by running 'mysqlbinlog' on the relay log), a network problem, or a bug in the master's or slave's MySQL code. If you want to check the master's binary log or slave's relay log, you will be able to know their names by issuing 'SHOW SLAVE STATUS' on this slave.` : souvent un binlog corrompu (le Relay_Master_Log_File via un SHOW SLAVE STATUS), ~~appliquer la même solution que ci-dessus !! ~~ Jusqu'à MySQL <= 5.1 au moins, changer la position dans un Relay_log avec un `CHANGE MASTER TO` ne marche pas. Voir [#ChangementdelapositiondansunRelay_log].
* Erreur : `Got fatal error 1236 from master when reading data from binary log: 'log event entry exceeded max_allowed_packet; Increase max_allowed_packet on master'`
On obtient apparemment cela dans différents cas. L'un d'eux serait si max_allowed_packet est inférieur à read_buffer_size ; voir <http://www.mysqlperformanceblog.com/2012/06/06/read_buffer_size-can-break-your-replication/> ; dans d'autre cas, il faudra forcer la réplication à se poursuivre via `STOP SLAVE; CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.00XXXX' , MASTER_LOG_POS=XXXX; START SLAVE;` ; dans un autre cas, la position indiquée n'existe pas dans le binlog ; enfin voir <http://dev.mysql.com/doc/refman/5.1/en/replication-features-max-allowed-packet.html>
* Dans certains cas **exceptionnels**, une solution radicale est de réinitialiser la réplication avec un `STOP SLAVE; RESET SLAVE; START SLAVE;` Attention, cela doit être fait dans de très rares cas maîtrisés (attention notamment aux conflits `DUPLICATE ENTRY` que cela risque de provoquer).
* Si un `SHOW SLAVE STATUS` ne retourne pas d'erreur mais que la réplication ne se fait pas, les logs du slave peuvent contenir une erreur du type :
~~~
[Note] Slave I/O thread: Failed reading log event, reconnecting to retry, log 'mysql-bin.003357' at position 389449
[Note] Slave: received end packet from server, apparent master shutdown:
~~~
Il se peut que le master se réplique sur 2 slaves ayant un server-id identique !
### Changement de la position dans un `Relay_log`
À faire uniquement si en tentant de changer la position d'un `Relay_log` sur un slave, vous obtenez cette erreur :
~~~
Error initializing relay log position: Could not find target log during
relay log initialization
~~~
Il faut alors stopper le processus slave de réplication :
~~~
mysql> STOP SLAVE;
~~~
Puis éditer (en gardant une sauvegarde) le fichier `${datadir}/relay-log.info`. La première ligne correspond au `Relay_Log_File`, la seconde au `Relay_Log_Pos`.
Redémarrer MySQL.
## Contrôle de l'intégrité de la réplication
### pt-table-checksum
C'est un outil de [Percona](https://www.percona.com/downloads/percona-toolkit/) intégré dans son toolkit. (Package Debian [percona-toolkit](https://packages.debian.org/search?keywords=percona-toolkit) disponible à partir de Wheezy).
Manuel : https://www.percona.com/doc/percona-toolkit/2.1/pt-table-checksum.html
L'outil vérifie l'intégrité de la réplication en effectuant des requêtes de checksum (crc32 par défaut) sur le master, puis les requêtes sont joués sur les slaves permettant de trouver des différences.
La méthode la plus simple pour l'utiliser est d'autoriser le master à se connecter au slave (authentification MySQL). Ainsi, il s'occupe lui même de faire le nécessaire pour identifier les erreurs. Il suffira donc de lancer la commande sans argument pour qu'il identifie les incohérences. On pourra réaliser un cron avec l'argument `-q` qui ne fait remonter que les erreurs.
~~~
MAILTO=jdoe@exemple.com
42 9 * * 7 pt-table-checksum -q
~~~
### pt-table-sync
Si `pt-table-checksum` vous a remonté des incohérences, vous pouvez avec cet outil les corriger. Cela va identifier les différences et les corriger avec un `REPLACE` sur le master (qui sera donc répliqué sur le slave), garantissant la cohérence des données.
Exemple :
~~~
# pt-table-sync --print --replicate percona.checksums localhost
# pt-table-sync --execute --replicate percona.checksums localhost
~~~
En cas de `Can't make changes on the master because no unique index exists`. On peut synchroniser directement les différences sur le slave depuis le master.
~~~
# pt-table-sync --execute --no-check-slave localhost slave
~~~

View File

@ -0,0 +1,411 @@
---
title: Howto MySQL : questions fréquentes et erreurs courantes.
---
Pour le guide d'installation et d'usage courant, consultez [HowtoMySQL].
## Problèmes d'encodage de caractères (alias "y'a un problème de charset")
Lors de migration de bases, ou tout simplement restauration de dump, un des problèmes les plus courants est d'avoir des problèmes d'encodage de caractères.
L'encodage de caractères est complexe sous MySQL car il peut être géré à plusieurs niveaux (serveur, base, table, etc.).
Voici donc quelques astuces qui peuvent servir (ou pas) :
* Mettre son client MySQL avec le bon encodage (dans votre `.my.cnf`) :
~~~{.ini}
[client]
default-character-set=utf8
~~~
* Vérifier le type d'encodage du dump :
~~~
$ file my.dymp
my.dump: UTF-8 Unicode text, with very long lines
~~~
Attention, l'outil *file* n'est pas fiable à 100%… notamment en cas de présence de caractères de plusieurs encodages différents.
* Modifier l'encodage d'un dump avec ICONV. Cela sera souvent dans ce sens là :
~~~
$ iconv -f iso -t utf8 my.dump > my.dump.utf8
~~~
Dans certains cas très tordus, *iconv -f utf8 -t utf8* peut avoir une utilité.
* Forcer l'encodage lors de la réinjection :
~~~
$ mysql --default-character-set utf8 < my.dump
~~~
* Forcer l'encodage directement dans le dump.
On créera ainsi les bases et les tables ainsi ainsi :
~~~
mysql> CREATE DATABASE foo DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
mysql> CREATE TABLE bar (coin int) DEFAULT CHARSET=utf8;
~~~
* Modifier l'encodage du dump avec VIM.
On peut forcer l'UTF8 avec `:set fileencoding=utf8`. Cela peut être utile en cas de présence de caractères de plusieurs encodages différents.
Bien sûr, il faut en général se taper des remplacements à la main.
Dans tous les cas, on vérifiera si les fichiers ont été modifiés en calculant la somme MD5 par exemple...
Note : si votre dump fait plusieurs Go, vérifier que vous avez assez de mémoire pour l'ouvrir entièrement avec Vim ;-)
* Si les données sont injectées avec `LOAD DATA INFILE` :
Il faut préciser `CHARACTER SET latin1` ou `CHARACTER SET utf8` notamment si des tables ont des encodages différents !
Par exemple, si l'on a des dumps .txt de tables au format latin1 et utf8, on fera ainsi :
~~~
$ grep CHARSET=latin1 *.sql
$ grep CHARSET=utf8 *.sql
~~~
En fonction des résultats, on réinjectera les données .txt ainsi :
~~~
$ echo "LOAD DATA INFILE '$PWD/<table>.txt' INTO TABLE <table>" CHARACTER SET utf8 | mysql <db>; done
$ echo "LOAD DATA INFILE '$PWD/<table>.txt' INTO TABLE <table>" CHARACTER SET latin1 | mysql <db>; done
~~~
## Purger une table InnoDB (fichier .ibd)
En utilisant l'option `innodb_file_per_table` cela crée des fichiers .ibd par table. Lors d'une suppression de lignes, l'espace
n'est pas libéré immédiatement, il faut ensuite faire un OPTIMIZE TABLE pour "purger" les fichiers .ibd
## Reset mot de passe MySQL
Si vous avez oublié le mot de passe MySQL admin, vous pouvez relancer le démon MySQL ainsi :
~~~
$ /usr/bin/mysqld_safe --skip-grant-tables
130314 16:47:10 mysqld_safe Logging to syslog.
130314 16:47:10 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
~~~
Vous pourrez ainsi accéder à MySQL sans mot de passe, et aller changer le mot de passe admin dans la table `mysql.user`.
## Indexes désactivés
Si des requêtes "normales" semblent très lentes, vérifier que les indexes ne sont pas désactivés !
En effet, on peut désactiver les indexes avec un requête : `ALTER TABLE ... DISABLE KEYS`
Pour le vérifier, on vérifiera via un `SHOW INDEXES FROM <table>`
Pour le réactiver (cela peut être long) : `ALTER TABLE ... ENABLE KEYS`
## Cache désactivé
MySQL dispose d'un cache, par exemple si vous faites deux fois un SELECT identique (et simple) sur une table qui n'a pas été modifiée, le 2ème SELECT devrait être renvoyé par le CACHE.
Pour vérifier que cela fonctionne, vous pouvez observer le compteur de cache hits :
~~~
mysql> show status like 'Qcache_hits';
+---------------+--------+
| Variable_name | Value |
+---------------+--------+
| Qcache_hits | 544667 |
+---------------+--------+
1 row in set (0.00 sec)
~~~
Un bug vicieux impacte MySQL 5.1 et 5.5 (corrigé à partir de 5.6.9) : si le nom de la base comporte des caractères spéciaux (le `-` par exemple) et que les tables sont en InnoDB… **le cache ne marche pas !!!!**
Voir http://bugs.mysql.com/bug.php?id=64821 et http://dev.mysql.com/doc/relnotes/mysql/5.6/en/news-5-6-9.html
## Erreur avec base InnoDB
~~~
InnoDB: A new raw disk partition was initialized or
InnoDB: innodb_force_recovery is on: we do not allow
InnoDB: database modifications by the user. Shut down
InnoDB: mysqld and edit my.cnf so that newraw is replaced
InnoDB: with raw, and innodb_force_... is removed.
~~~
The total number of locks exceeds the lock table size
## Erreur 'The total number of locks exceeds the lock table size' on query.
Il faut augmenter la valeur de `innodb_buffer_pool_size`.
## Error 24
Si vous obtenez des erreurs de ce type, lors d'un mysqldump par exemple :
~~~
mysqldump: Got error: 1016: Can't open file: './db/table.frm' (errno: 24) when using LOCK TABLES
mysqldump: Got error: 23: Out of resources when opening file '.\db\table.MYD' (Errcode: 24) when using LOCK TABLES
~~~
C'est que votre serveur MySQL tente d'ouvrir trop de fichiers simultanément.
Pour augmenter le nombre maximal de fichiers pouvant être ouverts, vous pouvez ajuster le paramètre suivant dans la section `[mysqld]` du fichier `my.cnf`, dans la limite permise par votre système d'exploitation :
~~~
open_files_limit = 2048
~~~
La valeur par défaut étant de 1024.
Note : inutile de positionner une valeur pour `ulimit -n` dans les scripts de démarrage, mysqld_safe s'en charge tout seul.
## Problème de _definer_ avec les vues
Un problème classique qui peut arriver suite à une migration d'une base de données contenant des vues. Si l'utilisateur qui a créé la vue (le _definer_) n'existe pas sur le nouveau serveur, la vue sera inutilisable :
~~~
mysql> SELECT * FROM myview;
MySQL ERROR 1449 (HY000) : The user specified as a definer ('root'@'localhost') does not exist
~~~
Il faut donc soit faire un sed dans le dump MySQL pour remplacer le definer si on est à l'étape de préparation de la migration.
Si la migration a déjà été faite et qu'il n'est plus possible de réinjecter un dump, il faut modifier la vue via `ALTER VIEW`, mais il est nécessaire d'indiquer la définition complète de la vue. Pour la connaitre :
~~~
mysql> SHOW CREATE VIEW myview;
~~~
Copier ensuite la définition complète, en remplaçant `CREATE` par `ALTER`, et bien sûr le definer de la vue par un utilisateur existant qui aura les droits sur la vue.
Pour lister les vues dans une base :
~~~
mysql> SHOW FULL TABLES IN mydb WHERE TABLE_TYPE LIKE 'VIEW';
~~~
## Error 2020
Si vous obtenez l'erreur suivante lors d'un mysqldump :
~~~
mysqldump: Error 2020: Got packet bigger than 'max_allowed_packet' bytes when dumping table `mytable` at row: 6542
~~~
Augmentez la valeur de `max_allowed_packet` dans la section `[mysqldump]` du fichier `my.cnf` :
~~~{.ini}
[mysqldump]
max_allowed_packet = 64M
~~~
## Error 1267
Si vous obtenez une erreur du type :
~~~
ERROR 1267 (HY000): Illegal mix of collations (binary,IMPLICIT) and (utf8_bin,NONE) for operation 'like'
~~~
C'est qu'il y a souci entre votre charset client (`character_set_client`, `collation_connection`) et votre requête.
Vous pouvez les ajuster avec des commandes du type :
~~~
mysql> set character_set_client=utf8;
Query OK, 0 rows affected (0.00 sec)
mysql> set collation_connection=utf8_unicode_ci;
Query OK, 0 rows affected (0.00 sec)
~~~
Dans certain cas (création d'une vue par exemple), cela peut venir d'une version de MySQL trop ancienne
(on a constaté des requêtes qui passaient en 5.1 mais pas en 5.0).
## Column count of mysql.proc is wrong
Si vous avez des erreurs de ce type :
~~~
[ERROR] Column count of mysql.db is wrong. Expected 22, found 20. The table is probably corrupted
[ERROR] mysql.user has no `Event_priv` column at position 29
[ERROR] Event Scheduler: An error occurred when initializing system tables. Disabling the Event Scheduler.
[ERROR] Column count of mysql.proc is wrong. Expected 20, found 16. The table is probably corrupted
~~~
Cela signifie que les tables de la base `mysql` ne correspondent pas à la version de MySQL en cours.
Vous avez sûrement mis à jour MySQL ou réinjecter des données d'une autre base.
Plusieurs solutions, réinjecter les tables incorrectes ou utilisez `mysql_upgrade` qui est sensé adapter les tables.
## Désactiver la complétion avec l'option `--skip-auto-rehash`
En cas de souci lors de la connexion en ligne de commande MySQL,
vous pouvez désactiver la complétion automatique. En effet, cette
complétion peut créer de soucis si certaines tables sont corrompues :
~~~
$ mysql --skip-auto-rehash
~~~
## InnoDB: ERROR: the age of the last checkpoint is NNNNN
Si vous avez une erreur du type :
~~~
mysqld: 120313 12:16:10 InnoDB: ERROR: the age of the last checkpoint is 9433587,
mysqld: InnoDB: which exceeds the log group capacity 9433498.
mysqld: InnoDB: If you are using big BLOB or TEXT rows, you must set the
mysqld: InnoDB: combined size of log files at least 10 times bigger than the
mysqld: InnoDB: largest such row.
~~~
Il faut augmenter le log InnoDB, notamment _innodb_log_file_size''. Attention, il faudra ensuite stopper MySQL et effacer les fichiers ''ib_logfile*_ !
Pour plus de détails, voir :
http://www.mysqlperformanceblog.com/2008/11/21/how-to-calculate-a-good-innodb-log-file-size/
et http://dev.mysql.com/doc/refman/5.1/en/innodb-parameters.html
## `InnoDB: Error: trying to load index PRIMARY for table […] but the index tree has been freed!`
Si vous obtenez une erreur du type :
~~~
InnoDB: Error: trying to load index PRIMARY for table foo/bar
InnoDB: but the index tree has been freed!
121222 11:28:48 - mysqld got signal 11;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
We will try our best to scrape up some info that will hopefully help diagnose
the problem, but since we have already crashed, something is definitely wrong
and this may fail.
key_buffer_size=713031680
read_buffer_size=131072
max_used_connections=31
max_connections=384
threads_connected=29
It is possible that mysqld could use up to
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_connections = 1531901 K
bytes of memory
Hope that's ok; if not, decrease some variables in the equation.
thd=0x84dc0a0
Attempting backtrace. You can use the following information to find out
where mysqld died. If you see no messages after this, something went
terribly wrong…
Cannot determine thread, fp=0x84dc0a0, backtrace may not be correct.
Bogus stack limit or frame pointer, fp=0x84dc0a0, stack_bottom=0x44660000, thread_stack=196608, aborting backtrace.
Trying to get some variables.
Some pointers may be invalid and cause the dump to abort…
thd->query at 0x7f0d9c284ba0 is invalid pointer
thd->thread_id=355
The manual page at http://www.mysql.com/doc/en/Crashing.html contains
information that should help you find out what is causing the crash.
~~~
Cela semble être une table corrompue, et a priori il faut réinstaurer la table concernée.
Se connecter en ligne de commande avec l'option `mysql --skip-auto-rehash` puis
supprimer la table concernée (voir ci-dessous si besoin) et réinjecter là.
Diverses astuces sont listées ici :
http://dba.stackexchange.com/questions/23296/mysql-innodb-index-in-swap
## InnoDB: Souci lors de la création/suppression de table InnoDB
On suppose que vous utilisez bien l'option `innodb_file_per_table` comme conseillé sur cette page.
Pour supprimer une table problématique, vous pouvez éteindre MySQL
et supprimer le fichier `.frm` correspondant ! Néanmoins la table sera
toujours référencée par InnoDB, et vous devez créer un `.frm` simple
pour tromper le moteur :
~~~
mysql> create table foo(foo int) ENGINE=InnoDB;
# /etc/init.d/mysql stop
# cp /var/lib/mysql/foo/foo.frm /var/lib/mysql/foo/TABLE.frm
~~~
Quelques informations supplémentaire sur :
http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting-datadict.html
## `Error 121 : InnoDB : ERROR 1005 (HY000): Can't create table './foo/bar.frm' (errno: 121)`
Il s'agit d'un problème avec les clés. Par exemple, les clés que vous crééez
sont déjà référencée par InnoDB. Cela peut ainsi se produire si vous avez du
supprimer une table InnoDB via son fichier .frm
Une astuce possible est simplement de créer la table sans ses clés.
Une fois créée, vous devez voir les clés avec un `SHOW CREATE TABLE`.
À vous de voir si vous devez les modifier/supprimer.
## `InnoDB: Failing assertion: addr.page == FIL_NULL || addr.boffset >= FIL_PAGE_DATA`
Si le service MySQL/InnoDB refuse de démarrer à cause d'une erreur du type :
~~~
mysqld: InnoDB: Starting in background the rollback of uncommitted transactions
mysqld: 140130 16:01:44 InnoDB: Rolling back trx with id 13B87781, 3 rows to undo
mysqld: 140130 16:01:44 InnoDB: Assertion failure in thread 140516188849920 in file fut0lst.ic line 83
mysqld: InnoDB: Failing assertion: addr.page == FIL_NULL || addr.boffset >= FIL_PAGE_DATA
~~~
C'est que la base est corrompue…
Il faut sauvegarder le datadir. Puis tenter de redémarrer avec l'option `innodb_force_recovery=1`
puis `innodb_force_recovery=2` puis `innodb_force_recovery=3` etc. jusqu'à ce que le service démarre.
Attention, à partir de `innodb_force_recovery=3` vous devrez sûrement ajouter `innodb_purge_threads=0` :
~~~
innodb_force_recovery = 3
innodb_purge_threads = 0
~~~
Dès que le service démarre (il sera peut être en read-only), faites un dump de toutes vos bases MySQL.
Vous devrez sûrement repartir de zéro en recréant un datadir tout neuf (`mysql_install_db --datadir=/var/lib/mysql.new`)
et en réinjectant votre dump.
Voir http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html
## `Error 13 : mysqld: #007/usr/sbin/mysqld: File '/var/log/mysql/mysql-bin.00NNNN' not found (Errcode: 13)`
Si vous obtenez des erreurs de ce type :
~~~
mysqld: #007/usr/sbin/mysqld: File '/var/log/mysql/mysql-bin.005655' not found (Errcode: 13)
mysqld: 130202 19:49:05 [ERROR] Failed to open log (file '/var/log/mysql/mysql-bin.005655', errno 13)
mysqld: 130202 19:49:05 [ERROR] Could not open log file
mysqld: 130202 19:49:05 [ERROR] Can't init tc log
mysqld: 130202 19:49:05 [ERROR] Aborting
~~~
C'est que MySQL n'arrive pas à accéder au dernier binlog. Cela peut par exemple
être une question de droits. Pour vérifier que tout est correct, faire :
~~~
# sudo -u mysql head /var/log/mysql/mysql-bin.00NNNN
~~~
Une erreur étrange mais classique est que le fichier binlog prenne les droits de root.
On le corrigera ainsi :
~~~
# chown mysql:adm /var/log/mysql/mysql-bin.00NNNN
~~~
## `ERROR 130 (HY000): Incorrect file format '[…]'`
Si vous obtenez l'erreur ci-dessus, lors d'un mysqldump par exemple, et que les fichier `${mysql_datadir}/base/table.{MYD,MYI}` sont vides mais pas le `.frm`, il faut réparer la table comme ceci :
~~~
# mysqlcheck --auto-repair --check --use-frm <base> <table>
~~~
## `is blocked because of many connection errors.`
Blocage pour l'IP car nombreuses erreurs sur BD. Lié à la valeur de la variable `max_connect_errors`.
Pour résoudre la situation :
~~~
# mysqladmin flush-hosts
~~~