22
0
Fork 0
wiki/HowtoMySQL.md

2269 lines
73 KiB
Markdown
Raw Permalink Normal View History

2017-11-09 15:31:26 +01:00
---
categories: databases
2016-11-02 12:12:48 +01:00
title: Howto MySQL : installation et utilisation courante.
2017-01-13 15:45:18 +01:00
---
2016-11-01 23:37:14 +01:00
2017-09-13 01:19:24 +02:00
* Documentation MariaDB : <https://mariadb.com/kb/en/library/documentation/>
* Documentation MySQL 5.5 : <http://dev.mysql.com/doc/refman/5.5/en/>
2016-12-16 02:33:28 +01:00
* Rôle Ansible : <https://forge.evolix.org/projects/ansible-roles/repository/show/mysql>
2023-07-28 17:56:06 +02:00
* Statut de cette page : prod / bullseye
2016-12-16 00:41:21 +01:00
[MySQL](https://www.mysql.com/) est une base de données très populaire au sein des infrastructures web. Nous utilisons au choix la version libre de MySQL distribuée par Oracle, et [MariaDB](https://mariadb.org/) un fork créé en 2009 par le créateur initial de MySQL.
2016-11-01 22:50:26 +01:00
2016-11-02 12:12:48 +01:00
## Installation
2016-11-01 22:50:26 +01:00
2023-11-20 17:39:40 +01:00
Depuis Debian 9, nous installons MariaDB qui est distribué par Debian à place de MySQL.
Exemple en Debian 11 :
2023-11-20 17:40:00 +01:00
2016-11-01 23:37:14 +01:00
~~~
# apt install mariadb-server mariadb-client libconfig-inifiles-perl
2016-12-16 00:41:21 +01:00
$ mysql --version
mysql Ver 15.1 Distrib 10.5.15-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper
2016-12-16 00:41:21 +01:00
~~~
> *Note* : Sous Debian 8, nous installons la version libre de MySQL distribuée par Oracle :
2017-09-13 01:19:24 +02:00
>
> ~~~
> # apt install mysql-server
>
> $ mysql --version
> mysql Ver 14.14 Distrib 5.5.53, for debian-linux-gnu (x86_64) using readline 6.3
> ~~~
>
> ou MariaDB :
>
> ~~~
> # apt install mariadb-server-10.0
>
> $ mysql --version
> mysql Ver 15.1 Distrib 10.0.28-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2
> ~~~
2016-11-01 22:50:26 +01:00
L'installation sous Debian 11 ne permet plus a l'utilisateur SQL root de se connecter avec un mot de passe, mais seulement via la socket
2016-12-16 00:41:21 +01:00
On mets donc en place le `.my.cnf` suivant dans /root/ :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
[client]
user = root
socket = /run/mysqld/mysqld.sock
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
On peut créer un utilisateur root comme ceci :
2016-11-01 22:50:26 +01:00
~~~{ .sql }
mysql> CREATE USER root@localhost IDENTIFIED VIA unix_socket;
~~~
Si dans `SQL_MODE` la valeur `NO_AUTO_CREATE_USER` n'est pas activé, on peut créé l'utilisateur root comme ceci :
~~~{ .sql }
2023-02-15 11:57:41 +01:00
mysql> GRANT ALL PRIVILEGES ON *.* TO root@localhost IDENTIFIED VIA unix_socket WITH GRANT OPTION;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
En Debian 11, il n'y a plus d'utilisateur SQL `debian-sys-main`, le fichier `/etc/mysql/debian.cnf` est toujours créé mais est obsolète et ne doit plus être utilisé.
Sous Debian 8, avec MariaDB 10.0, l'utilisateur *debian-sys-maint* **n'a pas le privilège GRANT**. Il est donc impossible de créer d'autres utilisateurs en étant connecté avec ce compte.
2023-06-22 11:46:26 +02:00
2023-11-20 17:39:40 +01:00
### Bibliothèque client
2018-06-09 13:07:19 +02:00
Certains logiciels tiers nécessitent la "bibliothèque client".
Sur Debian 8, c'est le paquet `libmysqlclient-dev`.
Sur Debian 9 (et Debian 8 avec backports), c'est le meta-paquet `default-libmysqlclient-dev` qui permet de facilement installer la paquet adapté à votre base de données.
2022-01-25 11:30:11 +01:00
2023-06-22 11:46:26 +02:00
### MySQL 5.7 sur Debian 9
2017-09-18 19:52:47 +02:00
<https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/>
Pour installer MySQL 5.7 distribuée par Oracle sous Debian 9, on ajoute le dépôt *repo.mysql.com* :
~~~
2017-09-18 19:52:47 +02:00
# echo "deb http://repo.mysql.com/apt/debian stretch mysql-5.7" > /etc/apt/sources.list.d/mysql57.list
2019-12-28 23:22:14 +01:00
# wget 'http://keys.gnupg.net/pks/lookup?op=get&search=0x8C718D3B5072E1F5' -O /etc/apt/trusted.gpg.d/mysql57.asc
~~~
2019-12-28 23:22:14 +01:00
> *Note:* la clé GPG peut aussi être récupérée via https://dev.mysql.com/doc/refman/en/checking-gpg-signature.html
2017-09-18 19:52:47 +02:00
On peut ensuite installer les paquets :
~~~
# apt install mysql-server mysql-client
~~~
2017-09-18 19:52:47 +02:00
> *Note* : il faudra alors installer *mytop* en récupérant le package de Debian 8
2022-01-25 11:30:11 +01:00
2023-06-22 11:46:26 +02:00
### MySQL 8.0 sur Debian 9
Pour installer MySQL 8.0 distribué par Oracle sous Debian 9, on ajoute le dépôt `repo.mysql.com` :
~~~
# echo "deb http://repo.mysql.com/apt/debian stretch mysql-8.0" > /etc/apt/sources.list.d/mysql80.list
~~~
Malheureusement, la clé du dépôt est expirée, il faut forcer `apt update` avec `--allow-unauthenticated` :
2023-06-22 11:46:26 +02:00
~~~
# apt-get update --allow-unauthenticated
~~~
On peut ensuite mettre-à-jour MySQL :
~~~
# apt install mysql-server
~~~
Enfin, désactiver le dépôt pour ne pas rencontrer des erreurs aux prochains updates :
~~~
mv /etc/apt/sources.list.d/mysql80.list /etc/apt/sources.list.d/mysql80.list~
~~~
2023-11-20 17:37:46 +01:00
### MySQL 8.0 sur Debian 11/12
Pour installer MySQL 8.0 distribué par Oracle sous Debian 11, on utilise le paquet mysql-apt-config.
Celui-ci va s'occuper de la configuration apt (dépôt & clé GPG)
~~~
# wget https://repo.mysql.com/apt/debian/pool/mysql-apt-config/m/mysql-apt-config/mysql-apt-config_0.8.24-1_all.deb
# apt install ./mysql-apt-config_0.8.24-1_all.deb
# apt update
# apt install mysql-server
$ mysql --version
mysql Ver 8.0.35 for Linux on x86_64 (MySQL Community Server - GPL)
2023-11-20 17:37:46 +01:00
~~~
2023-06-22 11:46:26 +02:00
2016-11-02 12:12:48 +01:00
## Configuration
2016-11-01 22:50:26 +01:00
Le fichier de configuration principal est `/etc/mysql/my.cnf` qui inclue notamment les fichiers `.cnf` présents dans les sous-répertoires `conf.d/` et `mariadb.conf.d/`.
2016-11-01 22:50:26 +01:00
> *Note* : attention, si vous avez une configuration MySQL/MariaDB issue d'une Debian 8 (suite upgrade par exemple), le sous-répertoire `mariadb.conf.d/` ne sera **pas** pris en compte.
2017-09-18 21:15:04 +02:00
Le fichier `/etc/mysql/mariadb.conf.d/z-evolinux-defaults.cnf` contient nos optimisations basiques :
2016-11-01 22:50:26 +01:00
2016-12-16 00:41:21 +01:00
~~~{.ini}
[mysqld]
###### Connexions
2022-01-25 11:30:11 +01:00
# Maximum de connexions concurrentes (défaut = 100)... provoque un "Too many connections"
2016-12-16 00:41:21 +01:00
max_connections = 250
2022-01-25 11:30:11 +01:00
# Maximum de connexions en attente en cas de max_connections atteint (défaut = 50)
2016-12-16 00:41:21 +01:00
back_log = 100
# Maximum d'erreurs avant de blacklister un hote
max_connect_errors = 10
# Loguer les requetes trop longues
2018-10-11 10:42:59 +02:00
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
2016-12-16 00:41:21 +01:00
long_query_time = 10
###### Tailles
2022-01-25 11:30:11 +01:00
# Taille réservée au buffer des index MyIsam
# A ajuster selon les résultats utilisateurs
2016-12-16 00:41:21 +01:00
key_buffer_size = 512M
# Taille max des paquets envoyés/reçus … provoque un "Packet too large"
max_allowed_packet = 64M
2022-01-25 11:30:11 +01:00
# Taille de la mémoire réservée pour un thread
2016-12-16 00:41:21 +01:00
thread_stack = 192K
# A mettre le nombre de threads CPU alloues pour MySQL
thread_cache_size = 1
# Taille maximum des tables de type MEMORY
max_heap_table_size = 64M
###### Cache
2022-01-25 11:30:11 +01:00
# max_connections x nombre max de tables dans une jointure (défaut = 64)
2016-12-16 00:41:21 +01:00
table_open_cache = 4096
table_definition_cache = 4096
2022-01-25 11:30:11 +01:00
# Taille max des requêtes cachées (défaut = 1M)
2016-12-16 00:41:21 +01:00
query_cache_limit = 8M
2022-01-25 11:30:11 +01:00
# Taille réservée pour le cache (défaut = 0)
2016-12-16 00:41:21 +01:00
query_cache_size = 256M
2022-01-25 11:30:11 +01:00
# Type de requêtes à cacher (défaut = 1 : tout peut être caché)
2016-12-16 00:41:21 +01:00
query_cache_type = 1
# Cache tables
max_heap_table_size = 128M
tmp_table_size = 128M
###### InnoDB
2022-01-25 11:30:11 +01:00
# Si InnoDB n'est pas utilisé... le désactiver
2016-12-16 00:41:21 +01:00
#skip-innodb
2022-01-25 11:30:11 +01:00
# En général, il est plus optimum d'avoir un fichier par table
2016-12-16 00:41:21 +01:00
innodb_file_per_table
2022-01-25 11:30:11 +01:00
# Taille mémoire allouée pour le cache des datas et index
# A ajuster en fonction de sa RAM (si serveur dédié à MySQL, on peut aller jusqu'à 70%)
2016-12-16 00:41:21 +01:00
innodb_buffer_pool_size = 512M
2022-01-25 11:30:11 +01:00
# Nombre maximum de threads système concurrents
2016-12-16 00:41:21 +01:00
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
2022-01-25 11:30:11 +01:00
# Charset utf8 par défaut
2018-10-11 10:44:07 +02:00
character-set-server=utf8
collation-server=utf8_general_ci
2018-10-11 10:42:59 +02:00
# Patch MySQL 5.5.53
2017-09-14 15:18:23 +02:00
secure-file-priv = ""
2016-12-16 00:41:21 +01:00
~~~
**Note** : MariaDB 10.1 avec Debian 9 est par défaut en `utf8mb4` + collation `utf8mb4_general_ci`.
2017-09-18 21:15:04 +02:00
Le fichier `/etc/mysql/mariadb.conf.d/zzz-evolinux-custom.cnf` contient nos éventuelles optimisations spécifiques.
2016-12-16 00:41:21 +01:00
2017-09-18 21:15:04 +02:00
Nous désactivons également une protection mise en place par l'unité [systemd](HowtoSystemd).
Cela permet d'utiliser la partition `/home` ou `/srv` pour des sauvegardes ou autres opérations :
~~~
# cat /etc/systemd/system/mariadb.service.d/evolinux.conf
[Service]
ProtectHome=false
~~~
2016-12-16 00:41:21 +01:00
Par défaut, MySQL écoute en réseau sur `127.0.0.1` (port TCP/3306) et sur la socket Unix `/var/run/mysqld/mysqld.sock`
2017-09-18 21:15:04 +02:00
Pour activer les connexions réseau à distance, il faut ajouter la configuration suivante dans `zzz-evolinux-custom.cnf` :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~{.ini}
2016-12-16 00:41:21 +01:00
[mysqld]
2016-11-01 23:37:14 +01:00
bind-address = 0.0.0.0
~~~
2016-11-01 22:50:26 +01:00
2024-04-24 10:33:36 +02:00
Note : depuis MariaDB 10.11, il est possible de mettre plusieurs adresses IP séparées par des virgules :
~~~{.ini}
[mysqld]
bind-address=127.0.0.1,172.17.0.1
~~~
2023-09-12 17:27:05 +02:00
Puis, mettre les permissions :
~~~
# chmod o+r /etc/mysql/mariadb.conf.d/zzz-evolinux-custom.cnf
~~~
2022-01-25 11:30:11 +01:00
Pour plus d'informations sur l'optimisation avancée de MySQL, consultez le guide [/HowtoMySQL/Optimize]().
> *Note* : Sous Debian 8, nous mettons notre configuration dans `/etc/mysql/conf.d/evolinux.cnf`
### Configuration de la mémoire (InnoDB)
2017-09-18 21:15:04 +02:00
Selon les ressources de la machine, il faut optimiser davantage les options (par défaut, la configuration est adaptée pour une machine avec très peu de mémoire vive !).
2016-12-16 00:41:21 +01:00
2022-01-25 11:30:11 +01:00
On conseille au minimum d'ajuster `thread_cache_size` et `innodb_buffer_pool_size` :
2016-12-16 00:41:21 +01:00
~~~{.ini}
[mysqld]
# Nombre de threads CPU alloués pour MySQL
thread_cache_size = 2
2022-01-25 11:30:11 +01:00
# Mémoire allouée pour le cache InnoDB (si serveur dédié à MySQL, on peut aller jusqu'à 70% de la RAM)
2016-12-16 00:41:21 +01:00
innodb_buffer_pool_size = 2G
~~~
2022-01-25 11:30:11 +01:00
**Attention :** MySQL consomme plus de mémoire que ce qui est indiqué dans la variable `innodb_buffer_pool_size`. Celle-ci n'est que la taille totale du tampon mémoire utilisable par les instances des « workers » de InnoDB.
2016-12-16 00:41:21 +01:00
2022-01-25 11:30:11 +01:00
Si MySQL utilise trop de mémoire, le système va swapper, et on peut observer une importante baisse des performances. Pour vérifier que MySQL ne swappe pas, il faut que les valeurs `si` et `so` de la commande suivante soient égales à zéro :
~~~
# vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 976884 3301840 200564 1540540 0 0 6 53 0 0 2 1 96 1 0
~~~
2022-01-25 11:37:53 +01:00
A partir de MariaDB 10.2 (Debian 10 - Buster), il est possible d'ajuster dynamiquement (= sans redémarrer MySQL) la variable `innodb_buffer_pool_size`. Elle doit être un multiple de `innodb_buffer_pool_chunk_size` x `innodb_buffer_pool_instances` (par défaut 128M x 8), en octets (1 Go = 1073741824 octets).
2022-01-25 11:30:11 +01:00
~~~
# Afficher la valeur de innodb_buffer_pool_size :
mysql > SELECT @@innodb_buffer_pool_size;
# Redimensionner dynamiquement innodb_buffer_pool_size :
mysql > SET GLOBAL innodb_buffer_pool_size=...; # en octets
# Afficher l'avancement du redimensionnement dynamique :
mysql > SHOW STATUS WHERE Variable_name='InnoDB_buffer_pool_resize_status';
+----------------------------------+----------------------------------------------------+
| Variable_name | Value |
+----------------------------------+----------------------------------------------------+
| Innodb_buffer_pool_resize_status | Completed resizing buffer pool at ... |
+----------------------------------+----------------------------------------------------+
~~~
Il faut ensuite penser à ajuster aussi la valeur de `innodb_buffer_pool_size` dans `/etc/mysql/mariadb.conf.d/zzz-evolinux-custom.cnf` comme indiqué ci-dessus.
2023-12-15 09:49:14 +01:00
Voici la commande pour calculer la mémoire théorique qui sera utilisée (théorique car ça ne prend pas en compte les fuites mémoire) :
~~~
2023-12-15 09:58:08 +01:00
MariaDB [(none)]> SELECT ( @@key_buffer_size + @@query_cache_size + @@tmp_table_size + @@innodb_buffer_pool_size
+ @@innodb_log_buffer_size + @@max_connections * ( @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size
+ @@join_buffer_size + @@binlog_cache_size + @@thread_stack ) ) / (1024 * 1024 * 1024) AS MAX_MEMORY_GB;
2023-12-15 09:49:14 +01:00
~~~
2022-01-25 11:30:11 +01:00
Pour aller plus loin : <https://mariadb.com/kb/en/mariadb-memory-allocation/>
2017-09-18 21:15:04 +02:00
2016-12-16 00:41:21 +01:00
## datadir / tmpdir
Par défaut, le *datadir* (le répertoire où sont stockées les données brutes) est `/var/lib/mysql/`.
Pour diverses raisons il peut être intéressant de le déplacer (partition ou disque dédié etc.).
Pour des raisons de compatibilité, on conseille de conserver un lien symbolique :
~~~
# systemctl stop mysql
# mv /var/lib/mysql /srv/mysql-datadir
# ln -s /srv/mysql-datadir /var/lib/mysql
# systemctl start mysql
# systemctl status mysql
2016-12-16 00:41:21 +01:00
~~~
Pour certaines opérations lourdes, MySQL a besoin d'un *tmpdir* (répertoire où il va écrire des tables temporaires).
Par défaut il utilise `/tmp` mais vu qu'il est parfois nécessaire d'avoir plusieurs Go de libre, on pourra utiliser un répertoire différents :
~~~{.ini}
[mysqld]
tmpdir = /srv/mysql-tmpdir
~~~
2022-01-25 11:30:11 +01:00
2020-04-06 16:32:07 +02:00
### Liste toutes les permissions utilisateurs
2023-11-27 19:53:09 +01:00
Déplacé sur la page de [Percona Toolkit](HowtoPerconaToolkit#pt-show-grants)
2017-09-18 21:15:04 +02:00
2016-12-16 00:41:21 +01:00
## Logs
Sous Debian, les journaux de MySQL (démarrage, arrêt, erreurs, informations) sont envoyés via *syslog*.
Par défaut, ils seront donc visibles dans `/var/log/syslog`.
2016-11-01 22:50:26 +01:00
2016-12-16 00:41:21 +01:00
Le répertoire `/var/log/mysql/` contient les *binlogs* (trace de toutes les requêtes INSERT/UPDATE/DELETE exécutées).
2016-11-01 23:37:14 +01:00
## Utilisation courante (TL;DR)
Se connecter :
~~~
mysql [--defaults-group-suffix=1]
~~~
**Attention :**
* Les noms contenant des traits d'union ou un wildcard `%` doivent être mis entre des backquotes.
### Variables de configuration (dynamique)
Voir la valeur d'une variable de configuration :
~~~{.sql}
> SHOW VARIABLES LIKE '<VAR>';
~~~
On peut aussi utiliser un wildcard `%` :
~~~{.sql}
> SHOW VARIABLES LIKE '%<KEYWORD>%';
~~~
Afficher plusieurs variables :
~~~{.sql}
> SHOW VARIABLES WHERE Variable_name = '<VAR1>' OR Variable_name = '<VAR2>';
~~~
Changer dynamiquement la valeur d'une variable de configuration :
~~~{.sql}
> SET GLOBAL <VAR>=<VALUE>;
~~~
### Bases de données
**Lister** les bases :
~~~{.sql}
> SHOW DATABASES;
~~~
**Ajouter** une base :
~~~{.sql}
> CREATE DATABASE <DB_NAME>;
~~~
**Note :** si on veut créer un utilisateur et une table du même nom, on préférera utiliser la commande shell `# mysqladmin create <NAME>`.
**Supprimer** une base :
~~~{.sql}
> DROP DATABASE <DB_NAME>;
~~~
**Utiliser** (ou entrer dans) une base :
~~~{.sql}
> USE <DB_NAME>;
~~~
**Lister** les tables de la base :
~~~{.sql}
> SHOW TABLES;
~~~
### Utilisateurs
**Attention :**
* **Les permissions et mots de passe ne sont pas spécifiques à un utilisateur, mais à un couple utilisateur/hôte.**
* Par défaut, utiliser l'hôte `localhost`. Pour autoriser l'utilisateur à se connecter à partir de n'importe où, utiliser le wildcard `%`.
**Lister** les utilisateurs :
~~~{.sql}
> SELECT host, user, password FROM mysql.user [WHERE db=<BD_NAME> ORDER BY user];
~~~
**Lister** les privilèges dun utilisateur/hôte :
~~~{.sql}
> SHOW GRANTS FOR <USER>@<HOST>;
~~~
**Créer** un utilisateur/hôte :
~~~{.sql}
CREATE USER <USER>@<HOST> IDENTIFIED BY '<PASSWORD>';
~~~
**Note :** si on veut créer un utilisateur et une base du même nom, on préférera la commande shell `mysqladmin create <NAME>`.
Pour autoriser un utilisateur existant à se connecter à partir d'un autre hôte (ou de `%`), réutiliser le hash de son mot de passe :
~~~{.sql}
> SELECT host, user, password FROM mysql.user WHERE user LIKE '<USER>';
> CREATE USER <USER>@<HOST2> IDENTIFIED BY PASSWORD '<HASH>';
~~~
Donner **tous les privilèges** sur une base à un utilisateur/hôte :
~~~{.sql}
> GRANT ALL PRIVILEGES ON <DB_NAME>.* TO <USER>@<HOST>;
~~~
Changer le **mot de passe** d'un utilisateur :
~~~{.sql}
> SET PASSWORD FOR <USER>@<HOST> = PASSWORD('<PASSWORD>');
~~~
**Révoquer** les accès d'un utilisateur/hôte à une base (attention un utilisateur peut avoir des permissions avec plusieurs hôtes !) :
~~~{.sql}
> REVOKE ALL PRIVILEGES ON <DB_NAME>.* FROM <USER>@<HOST>;
~~~
**Supprimer** un couple utilisateur/hôte :
~~~{.sql}
> DROP USER <USER>@<HOST>;
~~~
2016-11-02 12:12:48 +01:00
## Utilisation courante
2016-11-01 23:37:14 +01:00
2017-06-22 10:34:30 +02:00
### Créer
2016-12-16 00:41:21 +01:00
Créer une nouvelle base de données nommée _foo_ :
2016-11-01 23:37:14 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-12-16 00:41:21 +01:00
mysql> CREATE DATABASE foo;
2016-11-02 12:12:48 +01:00
~~~
2016-11-01 23:37:14 +01:00
2017-06-22 10:34:30 +02:00
Créer une table nommée _bar_ avec différents champs :
2016-11-01 23:37:14 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-06-22 10:34:30 +02:00
mysql> CREATE TABLE bar (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));
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
Ajouter un champ à une table :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-06-22 10:34:30 +02:00
mysql> ALTER TABLE bar ADD another VARCHAR(100) DEFAULT NULL;
2016-11-02 12:12:48 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
Ajouter un champ à une table en précisant sa place :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-06-22 10:34:30 +02:00
mysql> ALTER TABLE bar ADD another VARCHAR(100) DEFAULT NULL AFTER prenom;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
### Lister
Voir les bases de données créées :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-06-22 10:34:30 +02:00
mysql> SHOW DATABASES;
2016-11-02 12:12:48 +01:00
~~~
2016-11-01 22:50:26 +01:00
Lister les utilisateurs :
2018-02-09 10:42:37 +01:00
~~~{.sql}
mysql> select Host,user from mysql.user;
~~~
Lister les utilisateurs d'une base :
~~~{.sql}
select User,Host from mysql.db where Db="DATABASE" order by User;
~~~
Connaître les privilèges d'un utilisateur :
2018-02-09 10:42:37 +01:00
~~~{.sql}
mysql> show grants for USER@'HOST';
~~~
2017-06-22 10:34:30 +02:00
Utiliser la base de données _foo_ :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-06-22 10:34:30 +02:00
mysql> USE foo
2016-11-02 12:12:48 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
Voir les tables créées :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-06-22 10:34:30 +02:00
mysql> SHOW TABLES;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
Décrire une table :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-06-22 10:34:30 +02:00
mysql> DESC bar;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
Sélectionner tous les champs d'une table :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-06-22 10:34:30 +02:00
mysql> SELECT * FROM bar;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
Lancer des commandes bash depuis l'invite de commande :
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
~~~{ .sql }
mysql> \! ls -l
-rw-r----- 1 user user 208774 Jan 11 14:31 dump_base.sql
2016-11-02 12:12:48 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
Lister les droits pour tous les accès MySQL créés :
2016-11-01 22:50:26 +01:00
2016-11-02 12:12:48 +01:00
~~~
2017-06-22 10:34:30 +02:00
# mysql -e "select concat('\'',User,'\'@\'',Host,'\'') as '' from mysql.user" | sort | \
( while read user; do [ -z "$user" ] && continue; echo "-- $user :"; mysql -e "show grants for $user"; done )
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
-- 'accesbase'@'localhost' :
Grants for accesbase@localhost
GRANT USAGE ON *.* TO 'accesbase'@'localhost' IDENTIFIED BY PASSWORD '*XXXX'
GRANT ALL PRIVILEGES ON `base`.* TO 'accesbase'@'localhost'
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
~~~
# mysql -e "select * from information_schema.user_privileges;"
2016-11-02 12:12:48 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-06-22 10:34:30 +02:00
### Supprimer
Supprimer un champ à une table :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-06-22 10:34:30 +02:00
mysql> ALTER TABLE bar DROP another;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-11-02 12:12:48 +01:00
Effacer des données d'une table :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-12-16 00:41:21 +01:00
mysql> DELETE FROM bar WHERE nom='waddle';
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-11-02 12:12:48 +01:00
Effacer TOUTES les données d'une table :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-12-16 00:41:21 +01:00
mysql> DELETE FROM bar;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-11-02 12:12:48 +01:00
Supprimer une table :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-12-16 00:41:21 +01:00
mysql> DROP TABLE bar;
2016-11-02 12:12:48 +01:00
~~~
Supprimer une base de données :
2016-11-01 23:37:14 +01:00
~~~{ .sql }
mysql> DROP DATABASE foo; # Mettre le nom de la base entre backquotes s'il contient un trait d'union : `foo-bar`
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-05-23 10:19:49 +02:00
Supprimer les différents privilèges pour un utilisateur mysql :
~~~{ .sql }
mysql> REVOKE ALL PRIVILEGES ON base.* FROM 'user'@'localhost';
~~~
2017-08-01 09:37:51 +02:00
Lister les bases de données dont l'utilisateur à le droit d'accès:
~~~{ .sql }
SELECT * FROM mysql.db WHERE User="nvmpubli";
~~~
2021-12-01 15:12:59 +01:00
Supprimer un utilisateur (supprime aussi les entrées dans mysql.db):
2017-05-23 10:19:49 +02:00
~~~{ .sql }
mysql> DROP USER 'user'@'localhost';
~~~
2017-06-22 10:34:30 +02:00
### Renommer
2017-06-22 10:34:30 +02:00
#### Base
2017-06-22 10:34:30 +02:00
- Créer une base vide
- Renommer toutes les tables vers cette dernière (utilisation script ci-dessous)
2017-01-31 10:42:11 +01:00
2017-06-22 14:00:41 +02:00
~~~{.bash}
BASE_FROM=db1; BASE_TO=db2
2017-06-22 10:34:30 +02:00
for table in $(mysql -e "use $BASE_FROM; show tables\G;" | grep -v '^\*\*\*' | cut -d':' -f2 | sed 's/^ //'); do echo $table; mysql -e "RENAME TABLE ${BASE_FROM}.${table} TO ${BASE_TO}.${table};"; done
2017-01-31 10:42:11 +01:00
~~~
2017-08-01 16:10:53 +02:00
- Appliquer les bons droits à la bdd
2017-06-22 10:34:30 +02:00
#### Table
Renommer un champ :
~~~{.sql}
mysql> ALTER TABLE bar CHANGE COLUMN another anotherone TEXT;
2017-04-12 11:13:07 +02:00
~~~
2017-06-22 10:34:30 +02:00
Changer le type d'un champ :
~~~{.sql}
mysql> ALTER TABLE bar CHANGE another another enum('foo',bar');
2017-04-12 11:13:07 +02:00
~~~
2017-06-22 10:34:30 +02:00
### Insérer
2017-06-22 10:26:54 +02:00
2017-06-22 10:34:30 +02:00
Insertion de données dans une table :
2017-06-22 10:26:54 +02:00
2017-06-22 10:34:30 +02:00
~~~{.sql}
mysql> INSERT INTO bar VALUES (1,'jp','papin','2005-06-12','Marseille',2);
INSERT INTO test (id,prenom,nom,ne_le) VALUES (2,'c','waddle','2004-06-17');
2017-06-22 10:26:54 +02:00
~~~
2017-10-16 21:12:29 +02:00
### Vues
Lister les vues de la base foo :
~~~{.sql}
mysql> SHOW FULL TABLES IN foo WHERE TABLE_TYPE LIKE 'VIEW';
~~~
Créer une vue baz à partir du contenu de la table bar :
~~~{.sql}
mysql> CREATE VIEW baz AS SELECT * FROM bar;
~~~
Voir la vue :
~~~{.sql}
mysql> SHOW CREATE VIEW `baz`;
~~~
Supprimer une vue baz :
~~~{.sql}
mysql> DROP VIEW `baz`;
~~~
2016-11-02 12:12:48 +01:00
## Administration
2016-11-01 22:50:26 +01:00
### Créer une base de données et un utilisateur associé
2016-11-01 22:50:26 +01:00
On crée une base de données et un utilisateur associé :
2016-11-01 23:37:14 +01:00
~~~
# mysqladmin create $db_name
2016-11-01 23:37:14 +01:00
# mysql
mysql> GRANT ALL PRIVILEGES ON $db_name.* TO '$user'@'localhost' IDENTIFIED BY '$password';
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
Cette opération revient à insérer des lignes suivante dans les tables `mysql.user` et `mysql.db` :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-12-16 01:49:46 +01:00
mysql> INSERT INTO mysql.user VALUES ('localhost','jdoe',password('PASSWORD'),'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','N','N','','','','',0,0,0,0,'',NULL);
mysql> INSERT INTO mysql.db VALUES ('localhost','foo','jdoe','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
2016-11-01 23:37:14 +01:00
mysql> FLUSH PRIVILEGES;
~~~
2016-11-01 22:50:26 +01:00
Si l'utilisateur existe déjà, on peut récupérer le hash de son mot de passe et lui créer l'accès de la manière suivante :
~~~
2023-03-14 10:18:22 +01:00
mysql> SHOW GRANTS FOR '$user'@'host';
mysql> GRANT ALL PRIVILEGES ON $db_name.* TO '$user'@'localhost' IDENTIFIED BY PASSWORD '$hash';
~~~
2017-01-12 00:04:04 +01:00
*À savoir* :
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
Pour migrer de Debian 6 à 7 :
- 3 colonnes ont été ajoutées dans `mysql.user` : Create_tablespace_priv, plugin et authentication_string (pour migrer il faut ajouter 'N' en 32ème position + '' et NULL à la fin)
- 2 colonnes ont été ajoutées dans `mysql.db` : Event_priv et Trigger_priv (pour migrer il faut ajouter 'Y' et 'Y' à la fin)
2016-11-01 22:50:26 +01:00
### Créer des utilisateurs et gérer leurs droits
2016-11-15 10:42:16 +01:00
On pourra ainsi régler finement les droits d'un utilisateur en connaissant la signification de chaque colonne :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-01 23:37:14 +01:00
mysql> desc user;
+------------------------+-----------------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+-----------------------------------+------+-----+---------+-------+
| Host | char(60) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Password | char(41) | NO | | | |
| Select_priv | enum('N','Y') | NO | | N | |
| Insert_priv | enum('N','Y') | NO | | N | |
| Update_priv | enum('N','Y') | NO | | N | |
| Delete_priv | enum('N','Y') | NO | | N | |
| Create_priv | enum('N','Y') | NO | | N | |
| Drop_priv | enum('N','Y') | NO | | N | |
| Reload_priv | enum('N','Y') | NO | | N | |
| Shutdown_priv | enum('N','Y') | NO | | N | |
| Process_priv | enum('N','Y') | NO | | N | |
| File_priv | enum('N','Y') | NO | | N | |
| Grant_priv | enum('N','Y') | NO | | N | |
| References_priv | enum('N','Y') | NO | | N | |
| Index_priv | enum('N','Y') | NO | | N | |
| Alter_priv | enum('N','Y') | NO | | N | |
| Show_db_priv | enum('N','Y') | NO | | N | |
| Super_priv | enum('N','Y') | NO | | N | |
| Create_tmp_table_priv | enum('N','Y') | NO | | N | |
| Lock_tables_priv | enum('N','Y') | NO | | N | |
| Execute_priv | enum('N','Y') | NO | | N | |
| Repl_slave_priv | enum('N','Y') | NO | | N | |
| Repl_client_priv | enum('N','Y') | NO | | N | |
| Create_view_priv | enum('N','Y') | NO | | N | |
| Show_view_priv | enum('N','Y') | NO | | N | |
| Create_routine_priv | enum('N','Y') | NO | | N | |
| Alter_routine_priv | enum('N','Y') | NO | | N | |
| Create_user_priv | enum('N','Y') | NO | | N | |
| Event_priv | enum('N','Y') | NO | | N | |
| Trigger_priv | enum('N','Y') | NO | | N | |
| Create_tablespace_priv | enum('N','Y') | NO | | N | |
| ssl_type | enum('','ANY','X509','SPECIFIED') | NO | | | |
| ssl_cipher | blob | NO | | NULL | |
| x509_issuer | blob | NO | | NULL | |
| x509_subject | blob | NO | | NULL | |
| max_questions | int(11) unsigned | NO | | 0 | |
| max_updates | int(11) unsigned | NO | | 0 | |
| max_connections | int(11) unsigned | NO | | 0 | |
| max_user_connections | int(11) unsigned | NO | | 0 | |
| plugin | char(64) | YES | | | |
| authentication_string | text | YES | | NULL | |
+------------------------+-----------------------------------+------+-----+---------+-------+
42 rows in set (0.00 sec)
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
Par exemple, pour permettre à un utilisateur (ici *debian-sys-maint*) de faire des `SHOW VIEW` :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-01 23:37:14 +01:00
mysql> UPDATE user SET Show_view_priv='Y' WHERE User='debian-sys-maint';
mysql> FLUSH PRIVILEGES;
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
…que l'on peut aussi faire via :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-01 23:37:14 +01:00
mysql> GRANT SHOW VIEW on *.* to `debian-sys-maint`@localhost;
~~~
2016-11-01 22:50:26 +01:00
Pour créer un utilisateur sans droit particulier, par exemple pour du monitoring :
2017-01-31 09:59:12 +01:00
~~~{.sql}
2018-08-10 14:48:17 +02:00
mysql> create user nrpe@localhost identified by 'PASSWORD';
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
On peut aussi gérer des droits sur les tables :
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-12-16 01:49:46 +01:00
mysql> GRANT Select,Insert,Update ON foo.bar TO 'jdoe'@localhost;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-11-02 12:12:48 +01:00
Pour révoquer des droits sur une table, on utilisera `REVOKE` :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-12-16 01:49:46 +01:00
mysql> REVOKE ALL PRIVILEGES ON foo.bar FROM 'jdoe'@localhost;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
/!\\ 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) !
2016-11-01 22:50:26 +01:00
On le positionnera ainsi :
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-12-16 01:49:46 +01:00
mysql> GRANT FILE ON *.* TO 'jdoe'@localhost;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-10-31 13:11:48 +01:00
Si l'on veux autoriser laccès a une base depuis un utilisateur MySQL, depuis lextérieur :
2022-04-04 17:39:00 +02:00
**/!\\ Il faut s'assurer que MySQL écoute bien sur toutes les IPs (bind-address = 0.0.0.0 dans la configuration MySQL)**
2023-12-14 14:49:01 +01:00
Depuis une IP particulière :
~~~{.sql}
2023-12-14 14:49:01 +01:00
mysql> GRANT ALL PRIVILEGES ON foo.* TO 'jdoe'@'<IP_ADDRESS>' IDENTIFIED BY '<PASSWORD>';
~~~
Depuis un sous-réseau (attention, `IP/NETMASK` ne fonctionne pas !), par exemple `172.16.0.0/24` :
~~~{.sql}
mysql> GRANT ALL PRIVILEGES ON foo.* TO 'jdoe'@'172.16.%' IDENTIFIED BY '<PASSWORD>';
~~~
Depuis toutes les IPs :
~~~{.sql}
2023-12-14 14:49:01 +01:00
mysql> GRANT ALL PRIVILEGES ON foo.* TO 'jdoe'@'%' IDENTIFIED BY '<PASSWORD>';
~~~
2022-09-26 17:23:34 +02:00
Si l'on ne connait pas le mot de passe, on peut utiliser le hash du mot de passe de l'utilisateur mysql comme ceci
~~~{.sql}
mysql> GRANT ALL PRIVILEGES ON foo.* TO 'jdoe'@'%' IDENTIFIED BY PASSWORD '*E355A1AB8251C0B7E02ED8483696B2F3954C05CC';
~~~
Pour créer un nouvel administrateur qui puisse également gérer les droits :
~~~{.sql}
GRANT ALL PRIVILEGES ON *.* TO '<ADMIN>'@'localhost' IDENTIFIED BY '<PASSWORD>' WITH GRANT OPTION;
~~~
2016-11-02 12:12:48 +01:00
### Vérifications et réparations
2016-12-16 01:49:46 +01:00
Pour vérifier et réparer toutes les tables (une sorte de *fsck* pour les tables), on lancera :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
# mysqlcheck --auto-repair --check --all-databases
~~~
2016-11-01 22:50:26 +01:00
On peut aussi réparer qu'une base en particulier :
2016-11-01 23:37:14 +01:00
~~~
2016-12-16 01:49:46 +01:00
# mysqlcheck --auto-repair --check foo
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-01-12 00:04:04 +01:00
> *Note* : ceci peut être à faire en cas d'arrêt inopiné du service.
2016-11-01 22:50:26 +01:00
Pour réparer une seule table :
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-12-16 01:49:46 +01:00
mysql> CHECK TABLE foo.bar;
mysql> REPAIR TABLE foo.bar;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
Dans le cas des tables MyISAM, si le REPAIR échoue, une réparation est aussi possible via `myisamchk`… à faire avec
le service MySQL arrêté :
2016-11-01 23:37:14 +01:00
~~~
2016-12-16 01:49:46 +01:00
# myisamchk -r -q /var/lib/mysql/foo/bar.MYD
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
En cas d'échec (*segfault* par exemple), on peut tenter :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
# 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
~~~
2016-11-01 22:50:26 +01:00
2019-03-05 00:51:47 +01:00
### OPTIMIZE
Il est conseillé de lancer régulièrement la commande `OPTIMIZE TABLE` sur ses tables.
Cela va réaliser une sorte de défragmentation des tables (`*.idb`), notamment sur les indexes.
2019-03-05 00:51:47 +01:00
C'est particulièrement recommandé sur les tables qui subissent beaucoup de changement, notamment des modifications/suppressions de lignes.
Voici comment on lance l'opération sur une table :
~~~{.sql}
mysql> OPTIMIZE TABLE foo.bar;
~~~
On peut également lancer cela sur l'ensemble des tables d'une base (mais cela peut être très long) :
~~~
# mysqlcheck --optimize --all-databases
~~~
> *Note* : lors d'un OPTIMIZE TABLE, la table est lockée.
Suivant le moteur utilisé pour une table, les opérations vont être différentes. Notamment pour le moteur *InnoDB* vous aurez un message du type `Table does not support optimize, doing recreate + analyze instead` et une table temporaire sera complètement recréée et remplacera l'ancienne (attention à l'espace disque !).
Par défaut les OPTIMIZE sont répliqué sur les autres serveurs, pour éviter cette réplication, il y a l'option NO_WRITE_TO_BINLOG :
~~~{.sql}
mysql> OPTIMIZE NO_WRITE_TO_BINLOG TABLE foo.bar
~~~
~~~
mysqlcheck --optimize --all-databases --skip-write-binlog
~~~
2016-11-02 12:12:48 +01:00
### routines MySQL
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-02 12:12:48 +01:00
mysql> select * from INFORMATION_SCHEMA.routines;
~~~
### Changement mot de passe utilisateur
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2018-06-29 14:49:34 +02:00
mysql> SET PASSWORD FOR 'jdoe'@'localhost' = PASSWORD('my_password');
2017-08-02 15:55:38 +02:00
MariaDB [(none)]> GRANT USAGE ON *.* TO 'jdoe'@'%' IDENTIFIED BY "passwd";
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
Par rapport à un autre utilisateur :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-02 12:12:48 +01:00
mysql> use mysql;
2018-06-29 14:49:34 +02:00
mysql> UPDATE mysql.user SET Password=PASSWORD('my_new_password') WHERE User='jdoe' and Host='localhost';
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
Ou encore plus simple en se connectant avec le compte utilisateur puis lancer les commandes suivantes sans oublier de modifier le fichier de configuration :
~~~
$ mysql
> set password = password("mon-mot-de-passe-en-clair");
$ vim ~/my.cnf
~~~
2017-08-03 16:08:19 +02:00
### Changer variables globales d'environnement
2016-11-01 22:50:26 +01:00
2017-08-03 16:08:19 +02:00
On peut changer à chaud certaines variables globales d'environnement :
2017-10-11 21:13:47 +02:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-02 12:12:48 +01:00
mysql> SET GLOBAL max_connect_errors=50;
~~~
ou
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-02 12:12:48 +01:00
mysql> SET @@max_connect_errors=50;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-08-03 16:08:19 +02:00
Voici les changements utiles :
~~~{ .sql }
mysql> set global max_connections = 350;
~~~
> *Note* : on prendra garde à modifier aussi en dur dans la configuration si le changement doit être persistent.
### Lister les variables de configuration
Pour voir la liste de toutes les variables :
~~~{.sql}
mysql> show variables;
~~~
Pour ne voir qu'un sous-ensemble de variables :
~~~{.sql}
mysql> show variables like 'read_only';
mysql> show variables like '%thread%';
~~~
2022-06-15 10:30:16 +02:00
Pour dumper la configuration MySQL dans un fichier (à adapter s'il y a plusieurs instances) :
~~~{.bash}
mysql -A -e"SHOW GLOBAL VARIABLES;" > mysql_settings
~~~
### Lecture seule
Si on veut qu'une instance de MySQL soit démarrée en lecture seule, on peut ajouter la variable `read_only = 1` dans la section `[mysqld]`. Seules les requêtes faites par des utilisateurs ayant le privilège SUPER seront alors exécutées.
C'est par exemple utile dans une situation où une instance "slave" doit pouvoir être utilisée en lecture seule, tout en utilisant les mêmes comptes utilisateurs qui ont accès en écriture sur le "master". La réplication en elle-même n'est pas impactée.
2024-04-18 14:49:00 +02:00
### Tailles de bases et de tables
Pour afficher la **taille d'une base* (remplacer `<DB_NAME>`) :
~~~{.sql}
mysql> SELECT table_schema "DB Name",
Round(Sum(data_length + index_length) / 1024 / 1024, 1) "DB Size in MB"
FROM information_schema.tables
2024-04-18 14:49:00 +02:00
WHERE table_schema = '<DB_NAME>';
~~~
Pour lister la **taille de chaque base** :
~~~{.sql}
mysql> SELECT table_schema "DB Name",
Round(Sum(data_length + index_length) / 1024 / 1024, 1) "DB Size in MB"
FROM information_schema.tables
2024-04-18 14:49:00 +02:00
GROUP BY table_schema
ORDER BY 2 DESC;
2020-06-11 09:55:10 +02:00
~~~
2019-11-07 16:28:34 +01:00
2020-06-11 09:55:10 +02:00
Si on préfère avoir le détail entre taille de données et d'index :
2019-11-07 16:28:34 +01:00
2020-06-11 09:55:10 +02:00
~~~{.sql}
2019-11-07 16:28:34 +01:00
mysql> SELECT table_schema "DB Name",
Round(Sum(data_length) / 1024 / 1024, 1) "Data (in MB)",
Round(Sum(index_length) / 1024 / 1024, 1) "Index (in MB)",
Round(Sum(data_length + index_length) / 1024 / 1024, 1) "Total size in MB"
FROM information_schema.tables
GROUP BY table_schema;
~~~
Pour lister la **taille de toutes les tables d'une base** (remplacer `<DB_NAME>`) :
~~~{.sql}
mysql> SELECT
table_schema as `Database`,
table_name AS `Table`,
round(((data_length + index_length) / 1024 / 1024), 2) `Size in MB`
FROM information_schema.TABLES
WHERE table_schema = '<DB_NAME>'
ORDER BY (data_length + index_length) DESC;
~~~
Pour lister la **taille de toutes les tables de toutes les bases** :
~~~{.sql}
mysql> SELECT
table_schema as `Database`,
table_name AS `Table`,
round(((data_length + index_length) / 1024 / 1024), 2) `Size in MB`
FROM information_schema.TABLES
ORDER BY (data_length + index_length) DESC;
~~~
2023-01-12 14:47:17 +01:00
### Lister les Indexes de toutes les tables
Pour lister tous les Indexes de toutes les tables et de toutes les bases :
~~~
select index_schema, index_name, group_concat(column_name order by seq_in_index) as index_columns, index_type, case non_unique when 1 then 'Not Unique' else 'Unique' end as is_unique, table_name from information_schema.statistics where table_schema not in ('information_schema', 'mysql', 'performance_schema', 'sys') group by index_schema, index_name, index_type, non_unique, table_name order by index_schema, index_name;
~~~
En version plus "lisible" pour mettre dans un script ou autre :
~~~
select index_schema,
index_name,
group_concat(column_name order by seq_in_index) as index_columns,
index_type,
case non_unique
when 1 then 'Not Unique'
else 'Unique'
end as is_unique,
table_name
from information_schema.statistics
where table_schema not in ('information_schema', 'mysql',
'performance_schema', 'sys')
group by index_schema,
index_name,
index_type,
non_unique,
table_name
order by index_schema,
index_name;
~~~
Lister la taille des tables, des indexs, et l'espace libre (vide) de chaques tables :
~~~
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;
~~~
## Logs
### Log de toutes les requêtes
On peut activer à chaud le logging de l'ensemble des requêtes :
~~~{.sql}
mysql> SET GLOBAL GENERAL_LOG=ON;
~~~
Toutes les requêtes seront immédiatemment écrites dans le fichier `DATADIR/HOSTNAME.log`.
Pour désactiver à chaud :
~~~{.sql}
mysql> SET GLOBAL GENERAL_LOG=OFF;
~~~
> *Note* : évidemment, sur un serveur de production il faut éviter d'activer cela plus de quelques secondes car cela impacte fortement la performance et cela va rapidement remplir l'espace disque.
### Log des requêtes lentes (slow queries)
Pour débugger les applications lentes, c'est une fonctionnalité intéressante de trouver quelle requête est longue.
Pour cela on peut spécifier par exemple que l'on veut avoir toutes les requêtes qui durent plus de 5 secondes :
~~~{.ini}
[mysqld]
slow_query_log = 1
long_query_time = 5
slow_query_log_file = /var/log/mysql/mysql-slow.log
~~~
Ou à chaud :
~~~{.sql}
mysql> SET GLOBAL slow_query_log=ON;
mysql> SET GLOBAL long_query_time=5;
mysql> SET GLOBAL slow_query_log_file= "/var/log/mysql/mysql-slow.log";
~~~
Il est également possible de remonter les requêtes n'utilisant pas d'index (qu'importe le temps qu'elle prennent pour s'exécuter), avec le paramètre booléen `log_queries_not_using_indexes` mais nous déconseillons de le faire car cela ajoute de nombreuses requêtes inintéressantes en général.
#### long_query_time=0
Une astuce pour analyser l'ensemble des requêtes et de positionner `long_query_time=0` mais il faut évidemment faire très attention car la taille du fichier de logs peut grossier très vite. C'est intéressant à faire pendant quelques secondes, ou exceptionnellement pendant quelques heures pour une analyse avec `pt-query-digest` par exemple.
#### mysqldumpslow
Pour avoir une meilleure lecture des slow queries, on peut utiliser la commande [mysqldumpslow](https://dev.mysql.com/doc/refman/5.7/en/mysqldumpslow.html) :
~~~
# mysqldumpslow /var/log/mysql/mysql-slow.log
~~~
#### pt-query-digest
2023-11-27 19:53:09 +01:00
On peut aussi utilise la commande `pt-query-digest` disponible dans le paquet `percona-toolkit` afin d'avoir une analyse plus poussée des _slow queries_. Plus d'information sur la page de [Percona Toolkit](HowtoPerconaToolkit#pt-query-digest).
2017-01-11 23:29:31 +01:00
### Log des dead lock
> *Note* : Seulement possible depuis MySQL 5.6 ou MariaDB 10.
~~~{.ini}
[mysqld]
innodb_print_all_deadlocks = on
~~~
À chaud :
2017-01-31 09:59:12 +01:00
~~~{.sql}
mysql> SET GLOBAL innodb_print_all_deadlocks=on;
2017-01-11 23:29:31 +01:00
~~~
2016-11-02 12:12:48 +01:00
## Sauvegarde
2016-11-01 22:50:26 +01:00
2023-10-04 09:02:13 +02:00
> *Note*: Avec MariaDB 10.4.6 `mariadb-dump` est un lien symbolique vers `mysqldump`, avec MariaDB 10.5.2 `mysqldump` est un lien symbolique vers `mariadb-dump` et depuis MariaDB 11.0.1 `mysqldump` n'existe plus.
Pour sauvegarder une base de données dans un seul fichier (sans et avec compression) :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
$ mysqldump --hex-blob foo > foo.sql
$ mysqldump --hex-blob foo | gzip > foo.sql.gz
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2023-10-04 09:02:29 +02:00
> *Note* : l'option `--hex-blob` est importante pour ne pas risquer de perdre certains caractères dans les colonnes de type BINARY/BLOB/BIT
2018-06-04 10:54:10 +02:00
Il est aussi possible de sauvegarder une seule table avec mysqldump.
2023-10-04 09:02:29 +02:00
Exemple avec la table `bar` de la base `foo` : `$ mysqldump --hex-blob foo bar`
2018-06-04 10:54:10 +02:00
2022-12-06 18:01:00 +01:00
Pour sauvegarder une base de données au format *tab-separated data files*, avec - pour chaque table - un fichier .sql contenant la structure de la table (CREATE TABLE) et un fichier .txt contenant les données brutes (réinjectable avec `LOAD DATA INFILE`) :
~~~
# mkdir /tmp/foo && chown mysql:mysql /tmp/foo
2018-02-08 01:50:23 +01:00
$ mysqldump -T foo > /tmp/foo
~~~
2023-10-04 09:02:29 +02:00
> *Note* : le répertoire de destination doit exister et `mysqld` doit avoir les droits d'écrire dedans.
2018-02-08 01:50:23 +01:00
On peut utiliser les options `--fields-xxx` pour obtenir des fichiers au format CSV :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
2018-02-08 01:50:23 +01:00
$ mysqldump --hex-blob --fields-enclosed-by='\"' --fields-terminated-by=',' -T /tmp/foo foo
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2018-02-08 01:50:23 +01:00
Pour sauvegarder toutes les bases (exemples en mode classique et en mode `-T`) :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
$ mysqldump --opt --all-databases --events --hex-blob > all.sql
2019-10-01 15:39:17 +02:00
$ for db in $(mysql -P3308 -e "SHOW DATABASES" | egrep -v "^(Database|information_schema|performance_schema)"); do \
mkdir /backupmysql/$db && chown mysql:mysql /backupmysql/$db && \
mysqldump --events --hex-blob -T /backupmysql/$db $db; done
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
Pour sauvegarder uniquement certaines tables :
2016-11-01 23:37:14 +01:00
~~~
$ mysqldump --hex-blob foo TABLE0 [TABLE1…] > foo_tables.sql
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 12:21:26 +01:00
Pour presque faire un `--exclude` (qui manque cruellement à *mysqldump*):
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
$ mysql -B -N -e 'show databases' | \
perl -ne 'print unless /\b(?:phpmyadmin|mysql|information_schema)\b/' | \
xargs echo mysqldump --hex-blob -B
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
Et pour sauvegarder des tables correspondant à un motif (préfixe le plus souvent) :
2016-11-01 23:37:14 +01:00
~~~
$ mysqldump --hex-blob foo $(mysql foo -B --column-names=False -e "show tables like 'exemple_%'") > foo_motif.sql
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2019-02-08 10:59:35 +01:00
Pour exclure les VIEWS d'un mysqldump :
~~~
2023-10-04 07:46:18 +02:00
EXCLUDE_VIEWS=$(echo "SELECT CONCAT_WS('.', TABLE_SCHEMA, TABLE_NAME) from INFORMATION_SCHEMA.VIEWS" | mysql --skip-column-names | tr '\n' ',')
2019-02-08 10:59:35 +01:00
2023-10-04 07:46:18 +02:00
mysqldump [OPTIONS] --ignore-table=$EXCLUDE_VIEWS
~~~
2023-10-04 09:02:29 +02:00
**Attention**: la syntaxe `--ignore-table=DB.TABLE1,DB.TABLE2` ne semble plus fonctionner, il faudrait utiliser `--ignore-table=DB.TABLE1 --ignore-table=DB.TABLE2`
2023-10-04 07:46:18 +02:00
Pour exclure des tables d'un mysqldump :
~~~
2023-10-04 09:02:29 +02:00
mysqldump [OPTIONS] --ignore-table=DB.TABLE1 --ignore-table=DB.TABLE2
2023-10-04 07:46:18 +02:00
~~~
Pour exclure seulement les données de tables mais en garder la structure (à partir de MariaDB 10.1) :
~~~
2023-10-04 09:02:29 +02:00
mysqldump [OPTIONS] --ignore-table-data=DB.TABLE1 --ignore-table-data=DB.TABLE2
2019-02-08 10:59:35 +01:00
~~~
2016-11-01 22:50:26 +01:00
Pour dumper avec une condition particulière :
2016-11-01 23:37:14 +01:00
~~~
$ mysqldump -t foo bar --hex-blob --where="id='66666666'"
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
Ce qui permet de réinjecter des données résultantes d'un `SELECT * FROM foo.bar WHERE id='66666666'`.
2016-11-01 22:50:26 +01:00
2016-12-14 14:23:53 +01:00
Il est évidement possible de faire toutes ces opérations sur une instance en
2023-10-04 09:02:29 +02:00
précisant son port avec l'option `--port` (valable pour `mysqldump` et `mysql`).
2016-11-01 22:50:26 +01:00
Pour obtenir une liste des utilisateurs mysql, on peut utiliser cette fonction (glanée sur [serverfault](http://serverfault.com/questions/8860/how-can-i-export-the-privileges-from-mysql-and-then-import-to-a-new-server/)) :
2016-11-01 23:37:14 +01:00
~~~{.bash}
mygrants()
{
mysql -B -N -e "SELECT DISTINCT CONCAT(
'SHOW GRANTS FOR *, user, _'@'_, host, *;'
) AS query FROM mysql.user" | \
mysql | \
sed 's/\(GRANT .*\)/\1;/;s/^\(Grants for .*\)/## \1 ##/;/##/{x;p;x;}'
}
~~~
2016-11-01 22:50:26 +01:00
2018-02-08 01:50:23 +01:00
Pour avoir un dump avec un seul insert par ligne, pratique pour restaurer partiellement les bases `mysql.user` et `mysql.db` par exemple :
~~~
2018-02-08 01:50:23 +01:00
$ mysqldump --skip-extended-insert --events --hex-blob mysql > mysql.sql
~~~
Pour sauvegarder une grosse base de donnée en **InnoDB** et **NDB**, on peut ajouter l'argument [`--single-transaction`](https://dev.mysql.com/doc/refman/5.5/en/mysqldump.html#option_mysqldump_single-transaction) et la combiner avec `--skip-lock-tables` qui permet d'effectuer le dump dans une transaction et ainsi ne pas verrouiller les tables.
[Voir plus d'infomation ici sur ces deux options](https://wiki.evolix.org/HowtoMySQL/Troubleshooting#r%C3%A9duire-limpact-de-mysqldump-lors-du-process-de-sauvegarde-sur-la-production)
2020-02-13 14:43:11 +01:00
Pour sauvegarder uniquement la structure (pour toutes les bases d'un coup)
2020-02-13 14:43:11 +01:00
~~~
2020-02-13 14:44:05 +01:00
$ mysqldump --no-data --all-databases > schema.sql
2020-02-13 14:43:11 +01:00
~~~
Idem pour une seule base en particulier
~~~
2020-02-13 14:44:05 +01:00
$ mysqldump --no-data --databases DATABASE > DATABASE.schema.sql
2020-02-13 14:43:11 +01:00
~~~
Il existe aussi une autre manière de sauvegarder une instance MariaBD avec Mariabackup voir [/HowtoMySQL/mariabackup]().
2018-02-08 01:50:23 +01:00
### Restauration
2021-01-28 12:04:14 +01:00
- Pour restaurer une base de données (sans et avec compression) :
~~~
2018-02-08 01:50:23 +01:00
$ mysqladmin create foo
2018-03-08 15:04:28 +01:00
$ mysql -o foo < foo.sql
2022-09-07 17:13:48 +02:00
$ gunzip < foo.sql.gz | mysql -o foo
~~~
2021-01-28 12:04:14 +01:00
- Pour restaurer uniquement une base inclus dans un dump complet de MySQL :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
2018-02-08 01:50:23 +01:00
$ mysql -o foo < all.sql
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2018-03-20 16:29:10 +01:00
Mais si venant d'un dump complet voir aussi : [Restaurer une base depuis un dump complet](HowtoMySQL/Troubleshooting#restauration-dune-base-depuis-un-dump-complet)
2021-01-28 12:04:14 +01:00
- Pour restaurer une table de type "tab-separated data files" (`mysqldump -T`) dans une base _foo_ (exemples par défaut et avec des options `--fields-xxx`) :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
2018-02-08 01:50:23 +01:00
$ mysqlimport --default-character-set=utf8 foo /tmp/foo/bar.txt
$ mysqlimport --default-character-set=utf8 --fields-enclosed-by='\"' --fields-terminated-by=',' foo /tmp/foo/bar.txt
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2021-01-28 12:04:14 +01:00
- Pour restaurer une base entière avec un dump de type "tab-separated data files" (exemples par défaut et avec des options `--fields-xxx`) :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~{.bash}
db=test1
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
for file in *.sql; do
mysql $db <$file
done
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
grep CHARSET= *txt
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
for file in *.txt; do
2018-02-05 15:05:41 +01:00
tablename=$(basename $file .txt)
echo "LOAD DATA INFILE '$PWD/$file' INTO TABLE \`$tablename\`" CHARACTER SET utf8 | mysql $db
2018-02-08 01:50:23 +01:00
#echo "LOAD DATA INFILE '$PWD/$file' INTO TABLE \`$tablename\`" CHARACTER SET utf8 FIELDS TERMINATED BY ',' ENCLOSED BY '"' | mysql $db
2016-11-01 23:37:14 +01:00
done
~~~
2016-11-01 22:50:26 +01:00
2018-02-08 01:50:23 +01:00
> *Note 1* : Attention, l'utilisateur MySQL doit avoir le droit de lecture sur les fichiers .txt
Se positionner dans un répertoire où mysql a les droits (mysqltmp - /home/mysqltmp par ex).
2017-03-02 11:30:31 +01:00
2018-02-08 01:50:23 +01:00
> *Note 2* : Si vous n'avez pas toutes vos tables en utf8 (par exemple du `CHARSET=LATIN1`), ce n'est pas bien… et vous devrez pour la peine adapter le script (en détectant le charset utilisé avec « file » si nécessaire)
2017-03-02 11:30:31 +01:00
2018-02-08 01:50:23 +01:00
> *Note 3* : Si erreur 150 : «Can't create table» voir du côté des foreign keys :
>
>~~~{.bash}
>$ mysql -e 'SHOW ENGINE INNODB STATUS\G;' | grep LATEST\ FOREIGN -A3
>~~~
>
>et ignorer les erreurs pour pouvoir recréer les tables :
>
>~~~{.bash}
>$ mysql -e "set GLOBAL foreign_key_checks=OFF;"
>~~~
2017-03-02 11:30:31 +01:00
2021-01-28 12:04:14 +01:00
- Si cela concerne plusieurs bases réparties dans différents sous-répertoires :
2017-03-01 09:50:00 +01:00
~~~{.bash}
2018-02-08 01:50:23 +01:00
cd /home/mysqldump
2017-03-01 09:50:00 +01:00
for dir in *
do
2017-03-01 10:00:09 +01:00
echo "=======base $dir========="
2017-03-01 09:50:00 +01:00
db=$dir
mysql -e "create database ${dir};"
2017-03-01 10:00:09 +01:00
ls $dir/*.sql || continue
2017-03-01 09:50:00 +01:00
for file in $dir/*.sql
do
mysql $db <$file
done
2017-03-01 10:00:09 +01:00
grep CHARSET= $dir/*txt
ls $dir/*.txt || continue
2017-03-01 09:50:00 +01:00
for file in $dir/*.txt
do
tablename=$(basename $file .txt)
echo "LOAD DATA INFILE '$PWD/$file' INTO TABLE $tablename" CHARACTER SET utf8 | mysql $db
done
done
~~~
2017-01-12 00:04:04 +01:00
On peut également restaurer un dump _foo.sql_ (ou tout script au format SQL) de façon interactive via la commande `source` :
2017-01-12 00:02:01 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-01-12 00:02:01 +01:00
mysql> source foo.sql
~~~
2017-01-12 14:20:38 +01:00
> *Note 1* : il est nécessaire que MySQL ait les droits de lecture sur le fichier _foo.sql_
> *Note 2* : les sorties des requêtes sont renvoyées sur la sortie standard (au contraire de la restauration avec `mysql < foo.sql`)
2017-01-12 00:02:01 +01:00
2019-06-24 16:08:12 +02:00
Pour [extraire une table précise d'un dump complet pour ensuite la restaurer](https://wiki.evolix.org/HowtoMySQL/Troubleshooting#restauration-dune-table-depuis-un-dump-complet)
2021-05-12 14:12:09 +02:00
#### Exclure des tables de la restauration
Si vous disposez d'un dump complet dont vous souhaitez exclure des tables lors de la restauration, vous pouvez utilisez `sed` pour retirer les lignes correspondantes (pour autant que les INSERT soient en mode mono-ligne) :
~~~
# sed '/INSERT INTO `\(table1\|table2\)`/d' dump-full.sql > dump-light.sql
~~~
Attention que ça ne supprime pas les instructions `DROP TABLE` et `CREATE TABLE` qui sont en général en multi-lignes donc plus difficiles à supprimer.
2016-11-01 22:50:26 +01:00
2016-11-02 12:12:48 +01:00
## Monitoring
2016-11-01 22:50:26 +01:00
Pour surveiller un service MySQL en production, on pourra faire :
2016-11-01 23:37:14 +01:00
~~~
# mysqladmin status
# mysqladmin extended-status
# mysqladmin processlist
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
### mytop
Pour avoir une version conviviale et dynamique des process en cours, on utilisera l'outil *mytop*.
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
2017-08-31 04:36:13 +02:00
# apt install mariadb-client-10.1 libconfig-inifiles-perl libterm-readkey-perl
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2017-08-31 04:36:13 +02:00
> *Note* : Pour Debian 7 et 8, c'était dans un package indépendant :
>
> ~~~
> # aptitude install mytop
> ~~~
2016-11-01 22:50:26 +01:00
On édite le fichier `/root/.mytop` ainsi :
2016-11-01 23:37:14 +01:00
~~~{.ini}
user = debian-sys-maint
2016-12-16 01:49:46 +01:00
pass = PASSWORD
2016-11-01 23:37:14 +01:00
db = mysql
~~~
2016-11-01 22:50:26 +01:00
Reste plus qu'à lancer la commande `mytop -s1` (pour un rafraichissement toutes les secondes) : on appréciera les raccourcis `p` (mettre en pause l'affichage), `o` (pour voir en 1er les requêtes les plus longues / inverse l'ordre de tri sur le temps des requêtes), `k` (pour killer une requête… par exemple celle qui bloque toutes les autres), `i` (pour masquer les requêtes en status *sleep*) et `?` (pour voir les autres raccourcis possibles).
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
L'outil *mytop* se sert principalement de la requête `SHOW PROCESSLIST` que l'on pourra bien sûr lancer manuellement. Tout comme `KILL` :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-01 23:37:14 +01:00
mysql> SHOW PROCESSLIST;
2022-04-28 15:58:56 +02:00
mysql> SHOW FULL PROCESSLIST;
2016-12-16 01:49:46 +01:00
mysql> KILL <id_requête>;
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
Pour lister les requêtes qui durent plus de 30 secondes pour pouvoir les tuer facilement :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
2017-01-12 14:20:38 +01:00
# mysql -e 'select group_concat(concat("kill ",ID) separator ";") from information_schema.processlist where TIME>=30;'
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
Puis exécuter le résultat avec la commande *mysql*, exemple :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
2017-01-12 14:20:38 +01:00
# mysql -e 'kill 1854;kill 1853;kill 1852;kill 1851;kill 1850;kill 1848'
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
Pour surveiller le moteur InnoDB, on utilisera la commande suivante :
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-01 23:37:14 +01:00
mysql> SHOW ENGINE INNODB STATUS;
~~~
2016-11-01 22:50:26 +01:00
Pour lister tous les utilisateurs connectés, groupés par host (copié de [blog.shlomoid.com](http://blog.shlomoid.com/2011/08/how-to-easily-see-whos-connected-to.html)) :
~~~{.sql}
mysql> SELECT SUBSTRING_INDEX(host, ':', 1) AS host_short, GROUP_CONCAT(DISTINCT USER) AS users, COUNT(*)
FROM information_schema.processlist
GROUP BY host_short
ORDER BY COUNT(*), host_short;
~~~
### Installation de mytop sur Debian9 avec MySQL 5.7 (Oracle)
Sur debian9 mytop fait partie du paquet mariadb-client, et n'est plus disponible en stand alone, cela pose un problème si on installe MySQL depuis les dépôts d'oracle.
Il faut prendre le paquet mytop de jessie et l'installer sur stretch :
~~~
# wget http://ftp.de.debian.org/debian/pool/main/m/mytop/mytop_1.9.1-2_all.deb
# dpkg -i mytop_1.9.1-2_all.deb
~~~
Il se peut qu'il y est des dépendances manquante dans ce cas il faut les installer :
~~~
# apt install libdbi-perl libdbd-mysql-perl libconfig-inifiles-perl
# apt --fix-broken install
~~~
### log2mail
2016-12-16 01:49:46 +01:00
Afin d'être alerté en cas de souci, il est conseillé d'ajouter la configuration suivante au logiciel `log2mail` :
~~~
file = /var/log/syslog
pattern = "is marked as crashed and should be repaired"
mailto = monitoring@example.com
template = /etc/log2mail/template.mysql
2016-11-01 23:37:14 +01:00
file = /var/log/syslog
2016-12-16 01:49:46 +01:00
pattern = "init function returned error"
mailto = monitoring@example.com
template = /etc/log2mail/template.mysql
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
file = /var/log/syslog
2016-12-16 01:49:46 +01:00
pattern = "try to repair it"
mailto = monitoring@example.com
template = /etc/log2mail/template.mysql
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
file = /var/log/syslog
2016-12-16 01:49:46 +01:00
pattern = "InnoDB: Fatal error"
mailto = monitoring@example.com
template = /etc/log2mail/template.mysql
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
file = /var/log/syslog
2016-12-16 01:49:46 +01:00
pattern = "as a STORAGE ENGINE failed"
mailto = monitoring@example.com
template = /etc/log2mail/template.mysql
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
Le fichier `/etc/log2mail/template.mysql` contenant :
2016-11-01 23:37:14 +01:00
~~~
From: %f
To: %t
Subject: MySQL problem
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
Hello!
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
We have matched your pattern "%m" in "%F" %n times:
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
%l
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
Yours,
log2mail.
~~~
2016-11-01 22:50:26 +01:00
2017-01-12 00:04:04 +01:00
> *Note* : il faut ajouter l'utilisateur `log2mail` dans le groupe `adm`.
2016-11-01 22:50:26 +01:00
2017-01-11 23:29:31 +01:00
## binlogs
2016-11-01 22:50:26 +01:00
Par défaut, MySQL stocke chaque requête en écriture dans des fichiers appelés *binlogs*.
2016-11-02 12:12:48 +01:00
### Configuration
2016-11-01 22:50:26 +01:00
2020-02-14 10:39:33 +01:00
Par défaut les binlogs sont conservés sur une durée de 10 jours, avec des fichiers n'excédant pas 100 Mo :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~{.ini}
2016-12-16 01:49:46 +01:00
[mysqld]
2018-07-03 11:43:46 +02:00
log_bin = /var/log/mysql/mysql-bin.log
2016-11-01 23:37:14 +01:00
expire_logs_days = 10
max_binlog_size = 100M
#binlog_do_db = include_database_name
#binlog_ignore_db = include_database_name
binlog_format = mixed
~~~
2016-11-01 22:50:26 +01:00
2018-07-03 11:43:46 +02:00
> *Note :* depuis Debian 9, il faut explicitement préciser la directive `log_bin` pour activer les binlogs.
On peux modifier la valeur de la variable `expire_logs_days` _à chaud_ avec la commande suivante :
~~~{.sql}
SET GLOBAL expire_logs_days=5;
~~~
2023-06-16 15:07:54 +02:00
Il ne faut pas oublié également de mettre à jour la valeur dans le fichier de configuration.
2016-11-02 12:12:48 +01:00
### Format
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
<http://dev.mysql.com/doc/refman/5.5/en/binary-log-setting.html>
2016-11-01 22:50:26 +01:00
On peut choisir 3 types de format pour les binlogs :
2016-11-01 23:37:14 +01:00
* **statement** : les requêtes INSERT / UPDATE sont conservées
2016-12-14 14:23:53 +01:00
* **row** : les modifications de chaque ligne sont conservées (via une sorte de code « binaire » propre à MySQL)
2016-11-01 23:37:14 +01:00
* **mixed** : en mode statement… sauf dans certains cas où cela passe en mode row
2016-11-01 22:50:26 +01:00
Avantages et inconvénients :
2016-11-01 23:37:14 +01:00
Le mode **statement** est utile pour conserver en clair toutes les requêtes. Il permet aussi de meilleures performances quand des UPDATE contiennent des clauses WHERE qui modifient de nombreuses lignes.
2016-11-01 22:50:26 +01:00
Pour de la réplication, il peut être non fiable car le résultat d'un UPDATE peut donner des résultats différents sur un serveur SLAVE. Cela peut aussi poser des soucis avec les transactions InnoDB.
2016-11-01 23:37:14 +01:00
Le mode **row** a l'inconvénient de rendre illisibles toutes les requêtes. Dans certains cas particuliers (UPDATE contiennent des clauses WHERE qui modifient de nombreuses lignes), il peut être moins performant.
2016-11-01 22:50:26 +01:00
Il a l'avantage d'être plus fiable pour de la réplication.
2016-11-01 23:37:14 +01:00
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.
2016-11-01 22:50:26 +01:00
2018-12-09 01:25:06 +01:00
### Informations
On peut savoir le dernier binlog écrit :
~~~
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.020505 | 17736280 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
~~~
On peut lister l'ensemble des binlogs vus par MySQL :
~~~
mysql> show binary logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.020437 | 18697341 |
| mysql-bin.020438 | 104858013 |
| mysql-bin.020439 | 104858274 |
| mysql-bin.020440 | 104863158 |
[…]
~~~
Et les visualiser d'un point de vue filesystem :
~~~
# ls -l /var/log/mysql/
-rw-rw---- 1 mysql adm 18697341 Nov 29 00:01 mysql-bin.020437
-rw-rw---- 1 mysql adm 104858013 Nov 29 09:41 mysql-bin.020438
-rw-rw---- 1 mysql adm 104858274 Nov 29 13:10 mysql-bin.020439
-rw-rw---- 1 mysql adm 104863158 Nov 29 17:28 mysql-bin.020440
[…]
-rw-rw---- 1 mysql adm 17863542 Dec 9 01:24 mysql-bin.020505
-rw-rw---- 1 mysql adm 2208 Dec 9 00:01 mysql-bin.index
~~~
2016-11-02 12:12:48 +01:00
### Suppression
2016-11-01 22:50:26 +01:00
2022-09-23 15:49:28 +02:00
Si une réplication est en place, il faut vérifier sur le slave quel est le dernier binlog qu'il a récupéré/traité. Pour cela on fait `SHOW SLAVE STATUS\G`, puis on note la ligne `Relay_Master_Log_File:`. Elle indique le dernier binlog récupéré/traité. On peut donc supprimer sur le master, tout ceux d'avant. Par précaution, on peut en garder 10.
2017-11-09 12:05:21 +01:00
Exemple : `Relay_Master_Log_File: mysql-bin.009628` → sur le master `BINARY LOGS TO 'mysql-bin.009618';`
2017-11-01 19:21:02 +01:00
2024-01-11 21:20:27 +01:00
Note : On peut s'assurer qu'on a récupéré la valeur de `Relay_Master_Log_File` sur le bon serveur en la comparant avec les plus récents fichiers `mysql-bin.00NNNN` présents dans le répertoire `/var/log/mysql` que l'on souhaite nettoyer.
2016-11-01 23:37:14 +01:00
Pour supprimer les binlogs antérieurs à `mysql-bin.00NNNN` :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-01-01 12:53:13 +01:00
mysql> PURGE BINARY LOGS TO 'mysql-bin.00NNNN';
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
ou par rapport à une date :
2017-01-31 09:59:12 +01:00
~~~{.sql}
2016-11-01 23:37:14 +01:00
mysql> PURGE BINARY LOGS BEFORE "2011-12-07 00:00:00";
~~~
2016-11-01 22:50:26 +01:00
2017-11-01 19:21:02 +01:00
Si cela vient à se reproduire régulièrement, on peut baisser le nombre de jour durant lesquels les binlogs sont gardés avec la directive *expire_logs_days*.
2020-07-24 10:51:42 +02:00
On peut automatiser la tâche avec l'outil mysqlbinlogpurge dispo via [mysql-utils](https://downloads.mysql.com/archives/utilities/). Il faudra aussi mysql-connector-python.
2017-12-18 16:18:53 +01:00
Exemple :
```
2020-07-24 10:51:42 +02:00
mysqlbinlogpurge --master=mysqladmin:PASSWORD@192.0.2.1:3306 --slaves=mysqladmin:PASSWORD@192.0.2.2:3306 --dry-run
2017-12-18 16:18:53 +01:00
```
2017-11-09 15:18:39 +01:00
**Note** : Il est nécessaire que le slave s'annonce avec --report-host et --report-port.
2020-07-24 10:51:42 +02:00
**Note** : Il est nécessaire de ne pas mettre localhost ni 127.0.0.1 pour --master, mais l'adresse IP principale.
~~~
[mysqld]
report-host = 192.0.2.1
report-port = 3306
~~~
2016-11-02 12:12:48 +01:00
### Désactivation
2016-11-01 22:50:26 +01:00
Ce procédure est à faire à froid (besoin d'un restart), on ajoutera l'option suivante dans la configuration :
2016-11-01 22:50:26 +01:00
2016-12-16 01:49:46 +01:00
~~~{.ini}
[mysqld]
2016-11-01 23:37:14 +01:00
disable-log-bin
~~~
2016-11-01 22:50:26 +01:00
Puis on videra les binlogs manuellement depuis mysql :
~~~
RESET MASTER;
~~~
2016-11-02 12:12:48 +01:00
### Lecture
2016-11-01 22:50:26 +01:00
On pourra lire en ligne de commande le contenu d'un binlog via la commande :
2016-11-01 23:37:14 +01:00
~~~
# mysqlbinlog /var/log/mysql/mysql-bin.001789 | less
~~~
2016-11-01 22:50:26 +01:00
2018-10-29 15:48:00 +01:00
> * 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]`). On peut aussi lancer la commande avec l'option `--no-defaults`.
2016-11-01 22:50:26 +01:00
2016-11-02 12:12:48 +01:00
### Replay
2016-11-01 22:50:26 +01:00
2019-12-06 09:59:23 +01:00
/!\\ **CES MANIPULATIONS SONT DANGEREUSES POUR VOS DONNÉES, BIEN SAVOIR CE QUE L'ON FAIT !**
2016-11-01 22:50:26 +01:00
On pourra ainsi injecter le contenu d'un binlog dans une base… tout simplement avec une commande du type :
2016-11-01 23:37:14 +01:00
~~~
# mysqlbinlog /var/log/mysql/mysql-bin.001789 | mysql -P3307
~~~
2016-11-01 22:50:26 +01:00
À noter que si une partie des données étaient déjà présentes (cas d'un binlog corrompu lors d'incident lors d'une réplication), on pourra procéder ainsi :
2016-11-01 23:37:14 +01:00
~~~
# mysqlbinlog /var/log/mysql/mysql-bin.001789 > mysql-bin.001789.txt
# sed -i 's/INSERT INTO/INSERT IGNORE INTO/gi' mysql-bin.001789.txt
# cat mysql-bin.001789.txt | mysql -P3307
~~~
2016-11-01 22:50:26 +01:00
2018-12-28 10:28:21 +01:00
On peut aussi injecter les binlogs, sur une intervale de date et heure précise, en précisant le début et la fin comme ceci :
~~~
# mysqlbinlog --database foo --start-datetime='2018-12-27 16:44:57' --stop-datetime='2018-12-27 16:58:00' binlog.*|mysql -u user -p password
~~~
2017-01-11 23:29:31 +01:00
### SET sql_log_bin = 0
2016-11-01 22:50:26 +01:00
2017-01-11 23:29:31 +01:00
On peut effectuer des requêtes SQL qui ne seront pas écrites dans le binlog. Pour cela on positionne la variable _sql_log_bin_ à 0 et les requêtes interactives suivantes ne seront pas prises en compte dans le binlog (bien sûr, si l'on quitte le CLI MySQL, cela ne sera plus valable) :
2016-11-01 22:50:26 +01:00
2017-01-31 09:59:12 +01:00
~~~{.sql}
2017-01-11 23:29:31 +01:00
mysql> SET sql_log_bin = 0;
2017-01-04 15:23:40 +01:00
~~~
2017-01-10 17:24:40 +01:00
2017-01-11 23:29:31 +01:00
> *Note* : cela nécessite le droit MySQL _SUPER_
2017-01-10 17:24:40 +01:00
2016-11-02 12:12:48 +01:00
## Multiples instances MySQL
2016-11-01 22:50:26 +01:00
Il est possible de faire fonctionner plusieurs instances de MySQL sur un serveur où chacune à ses propres données, sa propre configuration et son utilisateur dédié.
2016-11-01 22:50:26 +01:00
> On préfère utiliser le port 3307 pour la première instance afin de ne pas confondre une configuration avec et sans instance.
2016-11-02 12:12:48 +01:00
### Installation
2016-11-01 22:50:26 +01:00
Pour éviter l'héritage de paramètres issue de l'instance principale, **il est nécessaire de commenter cette ligne** dans le `/etc/mysql/mariadb.conf.d/50-server.cnf` :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~{.ini}
#user = mysql
~~~
2016-11-01 22:50:26 +01:00
Ainsi que rajouter ces lignes dans `/etc/mysql/mariadb.conf.d/50-multi.cnf` :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~{.ini}
[mysqld_multi]
2016-11-02 12:12:48 +01:00
user = mysqladmin
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
Désactiver l'instance par défaut (qui écoute sur le port 3306) si elle n'est pas nécessaire :
~~~
systemctl disable --now mariadb
~~~
Créer un utilisateur dédié pour l'instance :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
instance=1
useradd mysqld${instance}
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
Créer les dossiers qui vont accueillir le *datadir* et les données temporaire avec les bons droits :
~~~
mkdir -vp /srv/mysqld_instances/mysqld${instance} /home/mysqld${instance}-tmp
touch /var/log/mysqld${instance}.log
chmod 755 /srv /srv/mysqld_instances
chown -R mysqld${instance}:mysqld${instance} /srv/mysqld_instances/mysqld${instance} /home/mysqld${instance}-tmp /var/log/mysqld${instance}.log
2017-12-21 11:23:24 +01:00
~~~
Ajouter ces lignes dans `/etc/mysql/mariadb.conf.d/zzz_mysqld${instance}.cnf` :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~{.ini}
echo "[mysqld${instance}]
user = mysqld${instance}
port = $((3306+${instance}))
tmpdir = /home/mysqld${instance}-tmp/
socket = /var/run/mysqld${instance}/mysqld${instance}.sock
pid-file = /var/run/mysqld${instance}/mysqld${instance}.pid
datadir = /srv/mysqld_instances/mysqld${instance}
log_error = /var/log/mysqld${instance}.log" \
> /etc/mysql/mariadb.conf.d/zzz_mysqld${instance}.cnf
2016-11-01 23:37:14 +01:00
~~~
L'ajout de ces directives permet de désactiver l'héritage des valeurs issue de l'instance principale :
~~~{.ini}
skip-slave-start
log_bin = 0
slow_query_log = 0
~~~
Ajuster les permissions du fichier de configuration:
~~~
chmod 644 /etc/mysql/mariadb.conf.d/zzz_mysqld${instance}.cnf
~~~
Créer manuellement le *datadir* :
~~~
mysql_install_db --user=mysqld${instance} --datadir=/srv/mysqld_instances/mysqld${instance} --defaults-file=/etc/mysql/mariadb.conf.d/zzz_mysqld${instance}.cnf
chmod 700 /srv/mysqld_instances/mysqld${instance}
~~~
### Démarrer une instance avec systemd
2016-11-01 22:50:26 +01:00
On créé une unité systemd multi-instances pour mysql dans `/etc/systemd/system/mysqld@.service`
~~~
[Unit]
Description=MySQL Multi Server for instance %i
After=syslog.target
After=network.target
[Service]
PIDFile=/var/run/mysqld%i/mysqld%i.pid
User=mysqld%i
Group=mysqld%i
Type=forking
ExecStart=/usr/bin/mysqld_multi start %i
ExecStop=/usr/bin/mysqld_multi stop %i
Restart=always
PrivateTmp=true
RuntimeDirectory=mysqld%i
[Install]
WantedBy=multi-user.target
~~~
On recharge le daemon systemd pour que l'unité soit prise en compte et on lance la nouvelle instance dont le "1" est une sorte d'alias de mysqld**1** :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
# systemctl daemon-reload
# systemctl start mysqld@1
~~~
2016-11-01 22:50:26 +01:00
On s'assure qu'elle fonctionne bien :
~~~
# systemctl status mysqld@1
● mysqld@1.service - MySQL Multi Server for instance 1
Loaded: loaded (/etc/systemd/system/mysqld@.service; disabled; vendor preset: enabled)
Active: active (running) since Tue 2020-08-18 15:49:27 CEST; 6min ago
Process: 16403 ExecStart=/usr/bin/mysqld_multi start 1 (code=exited, status=0/SUCCESS)
Main PID: 16408 (mysqld)
Tasks: 30 (limit: 4915)
Memory: 70.7M
CGroup: /system.slice/system-mysqld.slice/mysqld@1.service
└─16408 /usr/sbin/mysqld --defaults-group-suffix=1 --user=mysqld1 --port=3307 --tmpdir=/localhome/emorino/mysqld1-tmp --socket=/var/run/mysqld1/mysq
août 18 15:49:27 huit systemd[1]: Starting MySQL Multi Server for instance 1...
août 18 15:49:27 huit mysqld_multi[16403]: WARNING: Log file disabled. Maybe directory or file isn't writable?
août 18 15:49:27 huit mysqld_multi[16403]: [67B blob data]
août 18 15:49:27 huit mysqld_multi[16403]: Starting MariaDB servers
août 18 15:49:27 huit mysqld_multi[16403]: 2020-08-18 15:49:27 0 [Note] /usr/sbin/mysqld (mysqld 10.3.23-MariaDB-0+deb10u1) starting as process 16408 ...
août 18 15:49:27 huit systemd[1]: Started MySQL Multi Server for instance 1.
~~~
> Pour stopper une instance, on évite la commande « mysqld_multi stop 1 » qui n'est pas assez fiable et peut laisser l'instance dans un état incorrect, difficile à récupérer.
On préfère passer la commande « shutdown » en interne qui devrait envoyer un signal SIGTERM (kill -15) au process mysqld :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
# mysqladmin -P3307 shutdown
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
2022-09-26 17:23:34 +02:00
Avec l'unité systemd on peut stopper l'unité systemd en faisant :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
# systemctl stop mysqld@1
2016-11-01 23:37:14 +01:00
~~~
2016-11-01 22:50:26 +01:00
### Connexion aux instances et création du fichier *~/.my.cnf*
Lors de la création de plusieurs instances MySQL, on préférera donner à chacune un mot de passe différents à lutilisateur `mysqladmin`
2016-11-01 22:50:26 +01:00
On sécurise la nouvelle instance en définissant un mot de passe au compte mysqladmin :
~~~
# mysql -P3307 --socket=/var/run/mysqld1/mysqld1.sock -u root -p
~~~
On supprime l'utilisateur root pour le remplacer par l'utilisateur mysqladmin dont [les requêtes sont identiques à une installation mono-instances](HowtoMySQL#installation).
Pour utiliser le multi client, on configurera le fichier ~/.my.cnf comme ceci :
~~~
[client1]
host = 127.0.0.1
port = 3307
user = mysqladmin
password = foo
[client2]
host = 127.0.0.1
port = 3308
user = mysqladmin
password = bar
~~~
Cette configuration permettra de se connecter à l'instance mysql avec l'option `--defaults-group-suffix=$NUM` par exemple :
~~~
mysql --defaults-group-suffix=1
~~~
> **Attention** : Si on utilise cela, il faut modifier vos scripts de mysqldump ou de création de base en conséquence.
2018-10-02 16:39:15 +02:00
### Nettoyage
2018-10-02 16:38:17 +02:00
Si le *mysqld* principal n'est pas utilisé, on désactivera l'unité systemd comme ceci :
2018-10-02 16:39:15 +02:00
~~~
# systemctl stop mysql.service
# systemctl disable mysql.service
~~~
Pour géré les instances créer on utilisera l'unité systemd `mysqld@.service` :
~~~
# systemctl status mysqld@1
# systemctl stop mysqld@1
# systemctl start mysqld@1
# systemctl enable mysqld@1
2018-10-02 16:38:17 +02:00
~~~
2016-11-02 12:12:48 +01:00
### Administration
2016-11-01 22:50:26 +01:00
Pour voir le statut de l'instance n°1, logiquement nommée mysqld1 (GNR=1) et tournant sur le port 3307 :
2016-11-01 23:37:14 +01:00
~~~
# mysqld_multi report 1
Reporting MySQL servers
MySQL server from group: mysqld1 is running
~~~
2016-11-01 22:50:26 +01:00
2016-11-02 12:12:48 +01:00
Pour l'arrêter/redémarrer, même principe (attention, `mysqld_multi` est peu verbeux) :
2016-11-01 22:50:26 +01:00
2016-11-01 23:37:14 +01:00
~~~
# ps auwx | grep 3307
# mysqladmin -P 3307 shutdown
# ps auwx | grep 3307
# mysqld_multi start 1
# ps auwx | grep 3307
~~~
2016-11-01 22:50:26 +01:00
2022-09-26 17:23:34 +02:00
Si on a configuré plusieurs instance dans le fichier *.my.cnf*, on peut choisir l'instance sur laquelle on veux se connecter en utilisant *mysql --defaults-group-suffix=1* par exemple.
2021-07-05 18:39:11 +02:00
2022-01-04 13:24:21 +01:00
## Plugins
### MariaDB Audit Plugin
Documentation : <https://mariadb.com/kb/en/mariadb-audit-plugin/>
Ce plugin est fourni par MariaDB. Il permet d'avoir un log d'audit des connexions et opérations réalisées au niveau de MySQL/MariaDB.
Pour l'installer, le mieux est de le faire via le fichier de configuration (par exemple `/etc/mysql/mariadb.conf.d/zzz-evolinux-custom.cnf`), dans la section adaptée :
2022-01-04 16:43:28 +01:00
~~~{.ini}
2022-01-04 13:24:21 +01:00
[mariadb]
[…]
plugin_load_add = server_audit
~~~
On peut aussi le faire dynamiquement, mais cela ne sera pas persistent au prochain redémarrage :
~~~
mariadb> INSTALL SONAME 'server_audit';
~~~
Par défaut le plugin est inactif, on peut l'activer avec la variable globale `server_audit_logging` (dans la config ou bien dynamiquement) :
2022-01-04 16:43:28 +01:00
~~~{.ini}
2022-01-04 13:24:21 +01:00
[mariadb]
plugin_load_add = server_audit
server_audit_logging = ON
~~~
Par défaut il écrit ses logs dans un fichier `DATA_DIR/server_audit.log`, mais il est conseillé de le placer avec les autres logs :
2022-01-04 16:43:28 +01:00
~~~{.ini}
2022-01-04 13:24:21 +01:00
[mariadb]
plugin_load_add = server_audit
server_audit_file_path = /var/log/mysql/server_audit.log
server_audit_logging = ON
~~~
Il est possible de filtrer le type d'événements avec la variable `server_audit_events` et de filtrer les utilisateurs avec les variables `server_audit_incl_users` et `server_audit_excl_users`. La documentation indique [toutes les valeurs possibles](https://mariadb.com/kb/en/mariadb-audit-plugin-log-settings/).
La rotation des logs est automatique, mais personnalisable.
2022-12-06 18:01:00 +01:00
## Activer la connexion via SSL/TLS à la base de données
### Principe de fonctionnement
On génère une CA (clé privée et certificat) pour pouvoir signé une certificat serveur et client.
On ajoute le certificat serveur dans la configuration du serveur MySQL.
Le certificat client sert a se connecter au instances MySQL en SSL, soit avec PhpMyAdmin soit avec mysql-client, ou autres.
On par du principe qu'on l'on créer les clés / certificats dans _/etc/mysql/ssl_
### Création de la CA
On créé la CA comme ceci :
~~~
# openssl genrsa 4096 > mysql-ca-key.pem
# openssl req -new -x509 -nodes -days 365000 -key mysql-ca-key.pem -out mysql-ca-cert.pem
~~~
### Création du certificat SSL serveur
On créé le certificat serveur comme ceci, ne général on mets comme Common Name le hostname du serveur :
~~~
# openssl req -newkey rsa:2048 -days 365 -nodes -keyout mysql-server-key.pem -out mysql-server-req.pem
# openssl rsa -in mysql-server-key.pem -out mysql-server-key.pem
# openssl x509 -req -in mysql-server-req.pem -days 365 -CA mysql-ca-cert.pem -CAkey mysql-ca-key.pem -set_serial 01 -out mysql-server-cert.pem
~~~
### Création du certificat SSL client
On créé le certificat client comme ceci, le Common Name est identique à celui du serveur :
~~~
# openssl req -newkey rsa:2048 -days 365 -nodes -keyout mysql-client-key.pem -out mysql-client-req.pem
# openssl rsa -in mysql-client-key.pem -out mysql-client-key.pem
# openssl x509 -req -in mysql-client-req.pem -days 365 -CA mysql-ca-cert.pem -CAkey mysql-ca-key.pem -set_serial 01 -out mysql-client-cert.pem
~~~
On vérifie la correspondance des certificats entre le certificat serveur et client comme ceci :
~~~
# openssl verify -CAfile mysql-ca-cert.pem mysql-server-cert.pem mysql-client-cert.pem
mysql-server-cert.pem: OK
mysql-client-cert.pem: OK
~~~
### Installation du certificat SSL serveur sur une instance MySQL
Il faut possitionner les bons droits sur les certificats comme ceci :
~~~
# chown -Rv mysql:root /etc/mysql/ssl/
~~~
Puis on mets cette configuration dans _/etc/mysql/mariadb.conf.d/zzz-evolinux-custom.cnf_
~~~
# SSL
ssl = on
ssl-ca = /etc/mysql/ssl/mysql-ca-cert.pem
ssl-cert = /etc/mysql/ssl/mysql-server-cert.pem
ssl-key = /etc/mysql/ssl/mysql-server-key.pem
ssl-cipher = AES256-SHA
~~~
Un redémarrage de mysql / mariadb est necessaire :
~~~
# systemctl restart mysql.service
~~~
2022-09-26 17:23:34 +02:00
On peut vérifier que c'est bien pris en compte sur l'instance :
~~~
mysql> show variables like '%ssl%';
+--------------------+--------------------------------------+
| Variable_name | Value |
+--------------------+--------------------------------------+
| have_openssl | YES |
| have_ssl | YES |
| mysqlx_ssl_ca | |
| mysqlx_ssl_capath | |
| mysqlx_ssl_cert | |
| mysqlx_ssl_cipher | |
| mysqlx_ssl_crl | |
| mysqlx_ssl_crlpath | |
| mysqlx_ssl_key | |
| ssl_ca | /etc/mysql/ssl/mysql-ca-cert.pem |
| ssl_capath | |
| ssl_cert | /etc/mysql/ssl/mysql-server-cert.pem |
| ssl_cipher | AES256-SHA |
| ssl_crl | |
| ssl_crlpath | |
| ssl_fips_mode | OFF |
| ssl_key | /etc/mysql/ssl/mysql-server-key.pem |
+--------------------+--------------------------------------+
17 rows in set (0.00 sec)
~~~
## Chiffrement de la base de données Transparent Data Encryption (TDE)
2022-09-26 17:23:34 +02:00
On peut chiffrer les bases et les tables avec la méthode Transparent Data Encryption (TDE), cela ne nécessite aucun changement au niveau applicatif pour se connecter à la base de donnée, d'où le terme « Transparent », il existe plusieurs plugins pour faire cela :
2022-02-16 11:33:58 +01:00
- File Key Management Encryption
2022-02-16 11:46:50 +01:00
- Le fichier de clé est stocké dans MariaDB.
- La bonne pratique est de la stocké dans un montage séparé.
2022-02-16 11:33:58 +01:00
- AWS Key Management Encryption Plugin
2022-02-16 11:46:50 +01:00
- La clé est créé et stocké sur AWS, c'est un bon choix si on a une base de données managé chez AWS.
### Mise en place du chiffrement avec File Key Management Encryption
Dans une premier temps on doit créer un dossier où le fichier de clé sera stocké, puis on genère la clé :
~~~
$ mkdir -p /etc/mysql/encryption
$ echo "1;"$(openssl rand -hex 32) > /etc/mysql/encryption/keyfile
$ openssl rand -hex 128 > /etc/mysql/encryption/keyfile.key
$ openssl enc -aes-256-cbc -md sha1 -pass file:/etc/mysql/encryption/keyfile.key -in /etc/mysql/encryption/keyfile -out /etc/mysql/encryption/keyfile.enc
~~~
Puis on supprime la clé originale non chiffré :
~~~
rm -f /etc/mysql/encryption/keyfile
~~~
et on donne les bon droits au dossier / fichiers, on présume ici que l'instance tourne avec l'utilisateur `mysql` :
~~~
$ chown -R mysql:mysql /etc/mysql
$ chmod -R 500 /etc/mysql
$ ls -lRt /etc/mysql
/etc/mysql:
total 0
dr-x------. 2 mysql mysql 44 Mar 22 17:45 encryption
/etc/mysql/encryption:
total 8
-r-x------. 1 mysql mysql 96 Mar 22 17:43 keyfile.enc
-r-x------. 1 mysql mysql 257 Mar 22 17:43 keyfile.key
~~~
Puis on rajoute ceci dans le fichier de configuration, soit dans la section `[mariadb]`, soit dans la section de notre instance, par exemple, `[mysqld1]` :
~~~
plugin_load_add = file_key_management
file_key_management_filename = /etc/mysql/encryption/keyfile.enc
file_key_management_filekey = FILE:/etc/mysql/encryption/keyfile.key
file_key_management_encryption_algorithm = AES_CBC
innodb_encrypt_tables = FORCE
innodb_encrypt_log = ON
innodb_encrypt_temporary_tables = ON
encrypt_tmp_disk_tables = ON
encrypt_tmp_files = ON
encrypt_binlog = ON
aria_encrypt_tables = ON
innodb_encryption_threads = 4
innodb_encryption_rotation_iops = 2000
~~~
2022-02-16 11:33:58 +01:00
- Dans la première section de la configuration :
2022-02-16 11:51:09 +01:00
- `plugin_load_add` : on charge le plugin `file_key_management`
- `file_key_management_*` : on indique le chemin des clés
- `file_key_management_encryption_algorithm` : on defini l'algorithme de chiffrement, soit `AES_CBC` si MariaDB utilise le binaire `yaSSL` ou `wolfSSL`, soit `AES_CTR` mais ce dernier est disponible seulement si MariaDB utilise le binaire `OpenSSL`
2022-02-16 11:33:58 +01:00
- Dans la seconde section de la configuration :
2022-02-16 11:51:09 +01:00
- `innodb_encrypt_tables` : on force le chiffrement des bases / tables existantes.
- `innodb_encrypt_log` : on chiffre les redo logs
- `innodb_encrypt_temporary_tables` : on chiffre les tables temporaires.
2022-02-16 11:33:58 +01:00
- Autres paramètres intérréssants :
2022-02-16 11:51:09 +01:00
- `encrypt_binlog` : on chiffre les logs binaires.
- `encrypt_tmp_files` : on chiffre les fichiers temporaires.
Une fois la configuration en place, on redémarre MariaDB :
~~~
# systemctl restart mariadb
~~~
2022-09-26 17:23:34 +02:00
Puis on peut vérifier le chiffrement des bases / tables avec cette requête :
~~~
MariaDB [none]> SELECT CASE WHEN INSTR(NAME, '/') = 0
THEN '01-SYSTEM TABLESPACES'
ELSE CONCAT('02-', SUBSTR(NAME, 1, INSTR(NAME, '/')-1)) END
AS "Schema Name",
SUM(CASE WHEN ENCRYPTION_SCHEME > 0 THEN 1 ELSE 0 END) "Tables Encrypted",
SUM(CASE WHEN ENCRYPTION_SCHEME = 0 THEN 1 ELSE 0 END) "Tables Not Encrypted"
FROM information_schema.INNODB_TABLESPACES_ENCRYPTION
GROUP BY CASE WHEN INSTR(NAME, '/') = 0
THEN '01-SYSTEM TABLESPACES'
ELSE CONCAT('02-', SUBSTR(NAME, 1, INSTR(NAME, '/')-1)) END
ORDER BY 1;
;
+-----------------------+------------------+----------------------+
| Schema Name | Tables Encrypted | Tables Not Encrypted |
+-----------------------+------------------+----------------------+
| 01-SYSTEM TABLESPACES | 1 | 0 |
| 02-mysql | 4 | 0 |
+-----------------------+------------------+----------------------+
2 rows in set (0.002 sec)
~~~
Si on tente de lire un fichier .idb d'une table, voici ce qu'on obtient :
~~~
# cd /var/lib/mysql/mysql/
# strings transaction_registry.ibd | head -20
mPQ&
P.S|y
"h_S2$
m,uQ
I0$Y
AhZV
,tp"
Jb4\
TT v
))ok
hByc
- aG
GQQM
nLUZ$
Jb9q
)72B
`dDF'
f=Fb
4 PR
b?;(
~~~
2016-12-16 01:52:54 +01:00
## Optimisation avancée
2016-11-02 12:20:02 +01:00
Voir [/HowtoMySQL/Optimize]().
2016-11-02 12:20:02 +01:00
2016-12-16 01:52:54 +01:00
## Réplication MySQL
2016-11-01 22:50:26 +01:00
Voir [/HowtoMySQL/Replication]().
2016-11-01 22:50:26 +01:00
2019-05-06 14:55:28 +02:00
## Mariabackup / Percona XtraBackup
Voir [/HowtoMySQL/mariabackup]().
2016-11-02 12:12:48 +01:00
## FAQ et erreurs courantes
2016-11-01 22:50:26 +01:00
Voir [/HowtoMySQL/Troubleshooting]().
2018-02-08 01:50:23 +01:00