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) :
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ᵉ 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 !!!!**
Pour augmenter le nombre maximal de fichiers pouvant être ouverts, vous pouvez ajuster le paramètre suivant(dans la limite permise par votre système d'exploitation) :
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.
Si vous obtenez l'erreur ci-dessus, lors d'un mysqldump par exemple, et que les fichiers `${mysql_datadir}/foo/bar.{MYD,MYI}` sont vides mais pas le `.frm`, il faut réparer la table comme ceci:
Got error: 1290: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement when executing 'SELECT INTO OUTFILE'
Lors du passage à la version 5.5.53, la valeur par défaut est passée de _vide_ à `/var/lib/mysql-files` ce qui casse les *mysqldump* qui écrivent leurs fichiers ailleurs (malgré des droits systèmes adaptés).
Sur Debian, la nouvelle valeur par défaut est `/var/lib/mysql-files`. L'utilisation d'un lien symbolique de cet emplacement vers le dossier réel (par exemple `/home/mysqldump`) ne suffit pas et MySQL continue de refuser le dump.
Lorsqu'on souhaite que certains utilisateurs soient restreints en fonction de leur origine de connexion, on utilise le champ `Host` dans lequel on met autre chose que les valeurs classiques telles que `%` ou `localhost`.
Pour une restriction sur une seule IP, c'est simple on l'indique dans ce champ.
Pour une restriction par plage IP il faut donner une valeur de la forme `192.168.2.0/255.255.255.0`.
La notation CIDR (`192.168.2.0/24`) n'est pas supportée.
## Restauration d'une table depuis un dump complet
Pour extraire une table depuis un dump complet `dump.sql`, on détermine la liste des tables via `grep -n "Table structure"`, par exemple :
~~~
$ grep -n "Table structure" dump.sql
19:-- Table structure for table `wp-cgp`
43:-- Table structure for table `wp_WP_SEO_404_links`
73:-- Table structure for table `wp_WP_SEO_Redirection`
109:-- Table structure for table `wp_WP_SEO_Redirection_LOG`
143:-- Table structure for table `wp_acccess`
170:-- Table structure for table `wp_avant_premiere`
196:-- Table structure for table `wp_commentmeta`
223:-- Table structure for table `wp_comments`
265:-- Table structure for table `wp_icl_string_translations`
294:-- Table structure for table `wp_icl_strings`
322:-- Table structure for table `wp_investments`
396:-- Table structure for table `wp_links`
431:-- Table structure for table `wp_login_redirects`
458:-- Table structure for table `wp_mtouchquiz_answer`
486:-- Table structure for table `wp_mtouchquiz_question`
~~~
Si l'on veut extraire la table `wp_investments`, on détermine où commence et finit la table, ici elle commence à la ligne 322 et finit à la ligne 395 (juste avant la table suivante).
On peut ainsi utiliser `sed -n` pour extraire la table :
~~~
$ sed -n '[numéro_du_début_de_la_ligne],[numéro_de_fin_de_la_ligne] p' dump.sql > table.sql
~~~
Exemple avec la table *wp_investments* :
~~~
$ sed -n '322,395 p' dump.sql > wp_investments.sql
## Binary logging not possible. Message: Transaction level 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'
En cas d'erreur du type :
~~~
Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging. InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED.
~~~
cela signifie que votre BINLOG_FORMAT doit être en ROW car il l'était précédemment.
Vous pouvez vérifier que c'est bien le cas en faisant :
~~~
MariaDB [(none)]> show variables LIKE 'BINLOG_FORMAT';
## ERROR 1148: The used command is not allowed with this MySQL version
Si cette erreur survient lors d'une requête avec un LOAD DATA LOCAL INFILE il faut vérifié sur la variable *local_infile* est bien activé :
~~~
mysql> SHOW GLOBAL VARIABLES LIKE 'local_infile';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| local_infile | ON |
+---------------+-------+
~~~
Dans la configuration de mysql mettre :
~~~
local-infile=1
~~~
Et pour l'activer à chaud :
~~~
SET GLOBAL local_infile=ON;
~~~
Si une requête LOAD DATA LOCAL INFILE est faite par un script ou un applicatif qui lit la variable global [client], comme le .my.cnf d'un utilisateur, il faut ajouter la variable *loose-local-infile* dans le .my.cnf :
## Colonne Create Function à nulle lors de l'appel de procédures stockées
Avec Mysql <8.x,lorsdel'utilisationdeSHOWCREATEPROCEDUREouSHOWCREATEFUNCTION,silacolonneCreateFunctionrenvoieNULL,ilfautaccorderàl'utilisateurlesdroitsSELECTsurlatablemysql.proc:
~~~
GRANT SELECT ON mysql.proc TO user;
~~~
Pour MySQL >= 8.x, les droits SELECT globaux sont nécessaires.
Si une action ou une réplication est bloquée avec le message `incorrect key file` et incitant à réparer la table, c'est une fausse piste (il n'y a pas de REPAIR sur les tables InnoDB). Il se peut que ça soit en fait un tmpdir insuffisant en espace disponible pour accueillir les données temporaires. Il faut alors passer sur un tmpdir plus important (`/home/mysql-tmpdir` par exemple). Attention, cela nécessite un redémarrage du service.
## Lister les tables fragmentés
Souvent on execute un mysqltuner et celui ci indique que mysql contient des tables fragmentés, mais sans indiqués lequelles.
Si on veux lister toutes les tables fragmenté d'une instance MySQL, on peux executer cette requête :
~~~
SELECT ENGINE, TABLE_NAME,Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free from information_schema.tables where DATA_FREE > 0;
~~~
Cela liste les tables qui ont un différentiel entre al longeur des données et la longeur de l'index, et le nombre de données non utilisé dans chaque table (data_free)
On peux égelement calculer le ratio d'espace non utilisé sur une table, exemple avec cette table `champ_supp_contact` :
C'est à dire que la table contient 50% d'espace vide.
Voici une requêtes SQL qui indique également le ratio de fragmentation de chaque tables :
~~~
SELECT ENGINE, concat(TABLE_SCHEMA, '.', TABLE_NAME) as table_name, round(DATA_LENGTH/1024/1024, 2) as data_length, round(INDEX_LENGTH/1024/1024, 2) as index_length, round(DATA_FREE/1024/1024, 2) as data_free, (data_free/(index_length+data_length)) as frag_ratio FROM information_schema.tables WHERE DATA_FREE > 0 ORDER BY frag_ratio DESC;
## Problème pour créer ou modifié des TRIGGER d'une base
Si un utilisateur mysql veut créer ou modifié un TRIGGER, par default il ne peut pas le faire, et mysql retourne l'erreur suivante :
~~~
You do not have the SUPER privilege and binary logging is enabled
~~~
Pour qu'un utilisateur puisse créer / modifié des TRIGGER, sans avoir le privilège SUPER, et lorsque les binlogs sont activé, il faut activer la variable *log_bin_trust_function_creators* de manière globale :
## Calculer la taille adéquate pour innodb_buffer_pool_size
On peux calculer la taille adéquate pour la variable innodb_buffer_pool_size sur un serveur mysql, avec la requête suivante :
~~~
SELECT CEILING(Total_InnoDB_Bytes*1.6/POWER(1024,3)) RIBPS FROM ( SELECT SUM(data_length+index_length) Total_InnoDB_Bytes FROM information_schema.tables WHERE engine='InnoDB') A;
~~~
Il ne faut pas oublié d'augmenté également le innodb_buffer_pool_instances si innodb_buffer_pool_size et suppérieur ou égal à 4Gio.
On mets une instances par Gigaoctet pour le innodb_buffer_pool_instances.
## Le plugin Munin "mysql2" ne graph rien
Dans les logs de Munin /var/log/munin/munin-node.log on voit des erreurs du style `Unknown section: INDIVIDUAL BUFFER POOL INFO at /etc/munin/plugins/mysql_tmp_tables line 1098`.
Il faut modifier le plugin `/usr/share/munin/plugins/mysql_`, dans la foncion `parse_innodb_status` et ajouter dans la `%section_map` la ligne suivante :
~~~
'INDIVIDUAL BUFFER POOL INFO' => \&skip,
~~~
On peut vérifier tout de suite le bon fonctionnement avec la commande `munin-run mysql_connections` ou n'importe quel autre module géré par ce plugin.
Après une relance de `munin-node` et attente de 10 minutes (2 passage de munin-node en cron), on devrait avoir des valeurs.
## Erreur Row Size Too Large avec InnoDB
Si lors de requêtes SQL il y a des erreurs du type, avec les bases / tables qui utilise le moteur InnoDB :
~~~
Row size too large. The maximum row size for the used table type, not counting BLOBs, is 8126. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
~~~
C'est qu'il faut convertir la table en format de ligne dynamique (DYNAMIC rox format) comme ceci :
Puis faire une recherche sur le definer dans le fichier généré.
## Extraire des logs dans les binlogs par rapport a une date de début et de fin avec mysqlbinlog
Si l'on veux extraire des requêtes des binlogs par rapport a une date de début et de fin, utile pour voir toutes les écritures qui ont été faite sur un serveur a un moment donner, on peux utilisé mysqldump comme ceci :
## Listé les Indexs non utilisé, avec la base information_schema
Voici une requêtes SQL qui se base sur la base `information_schema` et les tables `STATISTICS` et `INDEX_STATISTICS` qui liste les Indexes non utilisés.
Attention, le résultat de cette requête n'est pas fiable à 100%, car ça suppose qu'un index est inutilisé que s'il n'a jamais provoqué IO wait.
Les clés primaire et les indexs UNIQUE sont exclus :
## Réduire l'impact de mysqldump lors du process de sauvegarde sur la production
Il existe 2 options dans mysqldump qui peuvent réduire l'impact de process de la sauvegarde sur l'instance en production.
* La première option est `--single-transaction` :
Elle est recommandée pour les bases de données fréquemment mises à jour ou contenant de grandes quantités de données. En effet, cela garantit que la base de données reste dans un état cohérent tout au long de la sauvegarde.
Cette option est compatible seulement avec les moteurs `InnoDB` et `NDB`, elle n'est pas compatible avec le moteur `MyISAM`.
Mais le fait d'utilisé cette option sur des tables avec un moteur non compatible, n'auras aucun effet.
* La deuxième option est de combiner `--single-transaction` avec `--skip-lock-tables` :
L’utilisation conjointe des options `--single-transaction` et `--skip-lock-tables` peut offrir plusieurs avantages :
* L'option `--single-transaction` garantit une sauvegarde cohérente de votre base de données en encapsulant l'intégralité de l'opération mysqldump dans une seule transaction. Cela garantit que la sauvegarde reflète un état cohérent de votre base de données au moment du début de la transaction.
* L'option `--skip-lock-tables` évite le verrouillage des tables pendant le processus de sauvegarde, ce qui peut éviter les temps d'arrêt et l'interruption des opérations actives.
Donc, l'utilisation conjointe des deux options fournit une sauvegarde cohérente et à faible impact de l'instance MySQL, ce qui en fait un choix idéal pour les environnements où la réduction des temps d'arrêt et des interruptions est une priorité.