🛠 Outils IT
🛠 78 outils 📚 32 docs
🤖 Assistant

MySQL / MariaDB

1. Installation

1.1 Debian / Ubuntu

Option A : MariaDB (recommande pour Debian)

# Mettre a jour les paquets
sudo apt update && sudo apt upgrade -y

# Installer MariaDB
sudo apt install mariadb-server mariadb-client -y

# Demarrer et activer au boot
sudo systemctl start mariadb
sudo systemctl enable mariadb

# Verifier le statut
sudo systemctl status mariadb

# Securiser l'installation
sudo mysql_secure_installation

Option B : MySQL Server

# Installer MySQL
sudo apt install mysql-server mysql-client -y

# Demarrer et activer au boot
sudo systemctl start mysql
sudo systemctl enable mysql

# Verifier le statut
sudo systemctl status mysql

# Securiser l'installation
sudo mysql_secure_installation
Astuce : Pour installer une version specifique de MariaDB (ex: 11.x) non disponible dans les depots par defaut, utiliser le depot officiel :
# Ajouter la cle GPG et le depot MariaDB
sudo apt install software-properties-common -y
sudo curl -o /etc/apt/keyrings/mariadb-keyring.pgp 'https://mariadb.org/mariadb_release_signing_key.pgp'

# Ajouter le depot (adapter la version et la distribution)
sudo add-apt-repository 'deb [signed-by=/etc/apt/keyrings/mariadb-keyring.pgp] https://mirror.mariadb.org/repo/11.4/ubuntu jammy main'
sudo apt update
sudo apt install mariadb-server mariadb-client -y

1.2 CentOS / RHEL / AlmaLinux

# Installer MariaDB
sudo dnf install mariadb-server mariadb -y

# Demarrer et activer au boot
sudo systemctl start mariadb
sudo systemctl enable mariadb

# Securiser l'installation
sudo mysql_secure_installation

MySQL 8.x via le depot officiel

# Telecharger et installer le depot MySQL
sudo dnf install https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm -y

# Installer MySQL Server
sudo dnf install mysql-community-server -y

# Demarrer et activer
sudo systemctl start mysqld
sudo systemctl enable mysqld

# Recuperer le mot de passe root temporaire (MySQL 8)
sudo grep 'temporary password' /var/log/mysqld.log

# Se connecter avec le mot de passe temporaire et le changer
mysql -u root -p
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NouveauMotDePasse!Complexe2024';
Attention : MySQL 8.x impose une politique de mot de passe stricte par defaut (plugin validate_password). Le mot de passe root doit contenir au moins 8 caracteres avec majuscules, minuscules, chiffres et caracteres speciaux.

1.3 Ouvrir le pare-feu (si acces distant necessaire)

# Debian / Ubuntu (UFW)
sudo ufw allow 3306/tcp

# CentOS / RHEL (firewalld)
sudo firewall-cmd --permanent --add-service=mysql
sudo firewall-cmd --reload

1.4 Verifier l'installation

# Version installee
mysql -V

# Se connecter en tant que root
sudo mysql -u root -p

# Depuis le prompt MySQL, verifier le statut
STATUS;
SELECT VERSION();

2. Connexion et utilisateurs

2.1 Methodes de connexion

# Connexion locale en tant que root
sudo mysql -u root -p

# Connexion avec un utilisateur specifique
mysql -u mon_utilisateur -p

# Connexion directe a une base
mysql -u mon_utilisateur -p ma_base

# Connexion a un serveur distant
mysql -u mon_utilisateur -p -h 192.168.1.50 -P 3306

# Executer une commande sans entrer dans le prompt
mysql -u root -p -e "SHOW DATABASES;"

# Connexion via socket Unix (Linux)
mysql -u root --socket=/var/run/mysqld/mysqld.sock

2.2 Creer un utilisateur

# Utilisateur local uniquement
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'M0tDeP@sse!Fort2024';

# Utilisateur accessible depuis une IP specifique
CREATE USER 'appuser'@'192.168.1.%' IDENTIFIED BY 'M0tDeP@sse!Fort2024';

# Utilisateur accessible depuis n'importe ou (deconseille en production)
CREATE USER 'appuser'@'%' IDENTIFIED BY 'M0tDeP@sse!Fort2024';

# Creer un utilisateur avec authentification par plugin (MariaDB / MySQL 8)
CREATE USER 'appuser'@'localhost' IDENTIFIED WITH mysql_native_password BY 'M0tDeP@sse!Fort2024';

# Modifier le mot de passe d'un utilisateur
ALTER USER 'appuser'@'localhost' IDENTIFIED BY 'NouveauMotDePasse!2024';

# Renommer un utilisateur
RENAME USER 'ancien_nom'@'localhost' TO 'nouveau_nom'@'localhost';

# Supprimer un utilisateur
DROP USER 'appuser'@'localhost';

2.3 Privileges (GRANT / REVOKE)

# Accorder tous les privileges sur une base specifique
GRANT ALL PRIVILEGES ON ma_base.* TO 'appuser'@'localhost';

# Accorder des privileges specifiques
GRANT SELECT, INSERT, UPDATE, DELETE ON ma_base.* TO 'appuser'@'localhost';

# Privileges en lecture seule
GRANT SELECT ON ma_base.* TO 'lecteur'@'localhost';

# Privileges sur une table specifique
GRANT SELECT, INSERT ON ma_base.ma_table TO 'appuser'@'localhost';

# Privileges globaux (toutes les bases) - a utiliser avec precaution
GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost' WITH GRANT OPTION;

# Privileges specifiques utiles
GRANT CREATE, ALTER, DROP, INDEX ON ma_base.* TO 'dev'@'localhost';
GRANT PROCESS, RELOAD, SHOW DATABASES ON *.* TO 'monitoring'@'localhost';
GRANT REPLICATION SLAVE ON *.* TO 'replicateur'@'192.168.1.%';

# Revoquer des privileges
REVOKE INSERT, UPDATE, DELETE ON ma_base.* FROM 'lecteur'@'localhost';
REVOKE ALL PRIVILEGES ON ma_base.* FROM 'appuser'@'localhost';

# Appliquer les changements de privileges
FLUSH PRIVILEGES;

2.4 Verifier les privileges

# Voir les privileges d'un utilisateur
SHOW GRANTS FOR 'appuser'@'localhost';

# Voir les privileges de l'utilisateur courant
SHOW GRANTS;
SHOW GRANTS FOR CURRENT_USER();

# Lister tous les utilisateurs
SELECT user, host, plugin FROM mysql.user ORDER BY user;
Astuce : En MySQL 8 / MariaDB 10.4+, l'authentification par defaut utilise le plugin auth_socket (ou unix_socket) pour root. Cela signifie que sudo mysql fonctionne sans mot de passe, mais mysql -u root -p peut echouer. Pour revenir a l'authentification par mot de passe :
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'VotreMotDePasse';
FLUSH PRIVILEGES;

3. Bases de donnees

3.1 Creer une base de donnees

# Creer une base avec le jeu de caracteres par defaut
CREATE DATABASE ma_base;

# Creer une base avec un jeu de caracteres specifique (recommande)
CREATE DATABASE ma_base CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

# Creer uniquement si elle n'existe pas deja
CREATE DATABASE IF NOT EXISTS ma_base CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

3.2 Gerer les bases

# Lister toutes les bases
SHOW DATABASES;

# Selectionner une base de travail
USE ma_base;

# Voir la base courante
SELECT DATABASE();

# Voir le jeu de caracteres d'une base
SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = 'ma_base';

# Modifier le jeu de caracteres d'une base existante
ALTER DATABASE ma_base CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

# Supprimer une base
DROP DATABASE ma_base;

# Supprimer uniquement si elle existe
DROP DATABASE IF EXISTS ma_base;

3.3 Taille des bases

# Taille de chaque base en Mo
SELECT table_schema AS 'Base de donnees',
       ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Taille (Mo)',
       COUNT(*) AS 'Nombre de tables'
FROM information_schema.tables
GROUP BY table_schema
ORDER BY SUM(data_length + index_length) DESC;

# Taille detaillee d'une base (par table)
SELECT table_name AS 'Table',
       ROUND(data_length / 1024 / 1024, 2) AS 'Donnees (Mo)',
       ROUND(index_length / 1024 / 1024, 2) AS 'Index (Mo)',
       ROUND((data_length + index_length) / 1024 / 1024, 2) AS 'Total (Mo)',
       table_rows AS 'Lignes estimees'
FROM information_schema.tables
WHERE table_schema = 'ma_base'
ORDER BY (data_length + index_length) DESC;
Attention : DROP DATABASE supprime definitivement la base et toutes ses tables. Toujours faire une sauvegarde avant de supprimer une base en production.

4. Tables

4.1 Creer une table

# Exemple complet de creation de table
CREATE TABLE clients (
    id          INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    nom         VARCHAR(100) NOT NULL,
    prenom      VARCHAR(100) NOT NULL,
    email       VARCHAR(255) NOT NULL UNIQUE,
    telephone   VARCHAR(20) DEFAULT NULL,
    adresse     TEXT,
    date_naissance DATE DEFAULT NULL,
    actif       TINYINT(1) NOT NULL DEFAULT 1,
    solde       DECIMAL(10,2) DEFAULT 0.00,
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    INDEX idx_nom (nom),
    INDEX idx_actif (actif)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

# Table avec cle etrangere
CREATE TABLE commandes (
    id          INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    client_id   INT UNSIGNED NOT NULL,
    montant     DECIMAL(10,2) NOT NULL,
    statut      ENUM('en_attente','validee','expediee','annulee') NOT NULL DEFAULT 'en_attente',
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,

    FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE ON UPDATE CASCADE,
    INDEX idx_client (client_id),
    INDEX idx_statut (statut)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

# Creer uniquement si la table n'existe pas
CREATE TABLE IF NOT EXISTS logs (
    id      BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    message TEXT NOT NULL,
    niveau  ENUM('info','warning','error','critical') NOT NULL DEFAULT 'info',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

4.2 Types de colonnes courants

Type Description Plage / Taille
TINYINTEntier tres petit-128 a 127 (UNSIGNED: 0 a 255)
INTEntier standard-2.1 milliards a 2.1 milliards
BIGINTGrand entierJusqu'a 9.2 x 10^18
DECIMAL(M,D)Nombre exact (monnaie)M chiffres dont D decimales
FLOAT / DOUBLENombre a virgule flottanteApproximatif, calculs scientifiques
VARCHAR(N)Chaine variableJusqu'a 65535 caracteres
CHAR(N)Chaine fixeExactement N caracteres
TEXTTexte longJusqu'a 65535 caracteres
MEDIUMTEXTTexte moyenJusqu'a 16 Mo
LONGTEXTTexte tres longJusqu'a 4 Go
DATEDateAAAA-MM-JJ
DATETIMEDate et heureAAAA-MM-JJ HH:MM:SS
TIMESTAMPHorodatage UTC1970-01-01 a 2038-01-19
ENUM('a','b','c')Liste de valeursUne seule valeur parmi la liste
JSONDonnees JSONMySQL 5.7+ / MariaDB 10.2+
BLOBDonnees binairesJusqu'a 65535 octets

4.3 Modifier une table (ALTER TABLE)

# Ajouter une colonne
ALTER TABLE clients ADD COLUMN ville VARCHAR(100) DEFAULT NULL AFTER adresse;

# Ajouter plusieurs colonnes
ALTER TABLE clients
    ADD COLUMN code_postal VARCHAR(10) DEFAULT NULL AFTER ville,
    ADD COLUMN pays VARCHAR(50) DEFAULT 'Canada' AFTER code_postal;

# Modifier le type d'une colonne
ALTER TABLE clients MODIFY COLUMN telephone VARCHAR(30);

# Renommer une colonne
ALTER TABLE clients CHANGE COLUMN nom nom_famille VARCHAR(100) NOT NULL;

# Supprimer une colonne
ALTER TABLE clients DROP COLUMN adresse;

# Ajouter un index
ALTER TABLE clients ADD INDEX idx_ville (ville);

# Ajouter un index unique
ALTER TABLE clients ADD UNIQUE INDEX idx_email_unique (email);

# Ajouter un index composite
ALTER TABLE commandes ADD INDEX idx_client_statut (client_id, statut);

# Supprimer un index
ALTER TABLE clients DROP INDEX idx_ville;

# Ajouter une cle etrangere
ALTER TABLE commandes ADD CONSTRAINT fk_client
    FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE;

# Supprimer une cle etrangere
ALTER TABLE commandes DROP FOREIGN KEY fk_client;

# Renommer une table
RENAME TABLE clients TO customers;
-- ou
ALTER TABLE clients RENAME TO customers;

4.4 Gerer les tables

# Lister les tables de la base courante
SHOW TABLES;

# Voir la structure d'une table
DESCRIBE clients;
-- ou
SHOW COLUMNS FROM clients;

# Voir la commande CREATE TABLE complete
SHOW CREATE TABLE clients\G

# Voir les index d'une table
SHOW INDEX FROM clients;

# Supprimer une table
DROP TABLE ma_table;
DROP TABLE IF EXISTS ma_table;

# Vider une table (supprime toutes les lignes, remet l'AUTO_INCREMENT a 1)
TRUNCATE TABLE ma_table;

# Copier la structure d'une table (sans les donnees)
CREATE TABLE clients_copie LIKE clients;

# Copier la structure ET les donnees
CREATE TABLE clients_copie AS SELECT * FROM clients;
Astuce : Utiliser UNSIGNED pour les colonnes INT qui ne contiendront jamais de valeurs negatives (comme les cles primaires). Cela double la plage positive disponible. Preferer DECIMAL a FLOAT pour les montants financiers afin d'eviter les erreurs d'arrondi.

5. Requetes essentielles

5.1 SELECT - Lecture de donnees

# Selection de base
SELECT * FROM clients;
SELECT nom, prenom, email FROM clients;

# Avec condition
SELECT * FROM clients WHERE actif = 1;
SELECT * FROM clients WHERE nom LIKE 'Dur%';
SELECT * FROM clients WHERE date_naissance BETWEEN '1980-01-01' AND '1990-12-31';
SELECT * FROM clients WHERE ville IN ('Montreal', 'Quebec', 'Toronto');
SELECT * FROM clients WHERE telephone IS NOT NULL;

# Tri et limite
SELECT * FROM clients ORDER BY nom ASC, prenom ASC;
SELECT * FROM clients ORDER BY created_at DESC LIMIT 10;
SELECT * FROM clients ORDER BY id DESC LIMIT 10 OFFSET 20;  -- pagination

# Alias et calculs
SELECT
    CONCAT(prenom, ' ', nom) AS nom_complet,
    email,
    TIMESTAMPDIFF(YEAR, date_naissance, CURDATE()) AS age
FROM clients
WHERE actif = 1
ORDER BY nom_complet;

# Sous-requete
SELECT * FROM clients
WHERE id IN (SELECT DISTINCT client_id FROM commandes WHERE statut = 'validee');

# Regroupement (GROUP BY)
SELECT ville, COUNT(*) AS nombre_clients
FROM clients
WHERE actif = 1
GROUP BY ville
HAVING COUNT(*) > 5
ORDER BY nombre_clients DESC;

# Valeurs distinctes
SELECT DISTINCT ville FROM clients ORDER BY ville;

5.2 INSERT - Insertion de donnees

# Insertion simple
INSERT INTO clients (nom, prenom, email, telephone)
VALUES ('Dupont', 'Jean', 'jean.dupont@email.com', '514-555-1234');

# Insertion multiple
INSERT INTO clients (nom, prenom, email) VALUES
    ('Martin', 'Sophie', 'sophie.martin@email.com'),
    ('Tremblay', 'Pierre', 'pierre.tremblay@email.com'),
    ('Roy', 'Marie', 'marie.roy@email.com');

# Insertion avec SELECT (copie entre tables)
INSERT INTO clients_archive (nom, prenom, email, created_at)
SELECT nom, prenom, email, created_at FROM clients WHERE actif = 0;

# INSERT ... ON DUPLICATE KEY UPDATE (upsert)
INSERT INTO clients (email, nom, prenom)
VALUES ('jean.dupont@email.com', 'Dupont', 'Jean-Pierre')
ON DUPLICATE KEY UPDATE nom = VALUES(nom), prenom = VALUES(prenom);

# Recuperer le dernier ID insere
SELECT LAST_INSERT_ID();

5.3 UPDATE - Modification de donnees

# Modifier une ligne
UPDATE clients SET telephone = '514-555-9999' WHERE id = 42;

# Modifier plusieurs colonnes
UPDATE clients
SET ville = 'Montreal', code_postal = 'H2X 1Y4', updated_at = NOW()
WHERE id = 42;

# Modifier plusieurs lignes avec condition
UPDATE clients SET actif = 0 WHERE created_at < '2020-01-01';

# Modifier avec une sous-requete
UPDATE clients SET actif = 0
WHERE id NOT IN (SELECT DISTINCT client_id FROM commandes WHERE created_at > '2024-01-01');

# Incrementer une valeur
UPDATE produits SET stock = stock - 1 WHERE id = 15 AND stock > 0;
Attention : Toujours utiliser une clause WHERE avec UPDATE et DELETE. Sans WHERE, toutes les lignes de la table seront modifiees ou supprimees. En cas de doute, faire d'abord un SELECT avec la meme condition pour verifier les lignes concernees.

5.4 DELETE - Suppression de donnees

# Supprimer une ligne
DELETE FROM clients WHERE id = 42;

# Supprimer avec condition
DELETE FROM clients WHERE actif = 0 AND created_at < '2020-01-01';

# Supprimer avec LIMIT (utile pour supprimer par lots)
DELETE FROM logs WHERE created_at < '2024-01-01' LIMIT 10000;

# Vider completement une table (plus rapide que DELETE sans WHERE)
TRUNCATE TABLE logs;

5.5 JOIN - Jointures entre tables

# INNER JOIN : seulement les correspondances
SELECT c.nom, c.prenom, o.id AS commande_id, o.montant, o.statut
FROM clients c
INNER JOIN commandes o ON c.id = o.client_id
WHERE o.statut = 'validee'
ORDER BY o.created_at DESC;

# LEFT JOIN : tous les clients, meme sans commande
SELECT c.nom, c.prenom, COUNT(o.id) AS nb_commandes, COALESCE(SUM(o.montant), 0) AS total
FROM clients c
LEFT JOIN commandes o ON c.id = o.client_id
GROUP BY c.id, c.nom, c.prenom
ORDER BY total DESC;

# RIGHT JOIN : toutes les commandes, meme si le client n'existe plus
SELECT c.nom, c.prenom, o.id AS commande_id, o.montant
FROM clients c
RIGHT JOIN commandes o ON c.id = o.client_id;

# Jointure multiple
SELECT c.nom, c.prenom, o.id AS commande_id, p.nom AS produit, lo.quantite, lo.prix_unitaire
FROM clients c
INNER JOIN commandes o ON c.id = o.client_id
INNER JOIN lignes_commande lo ON o.id = lo.commande_id
INNER JOIN produits p ON lo.produit_id = p.id
WHERE o.statut = 'validee'
ORDER BY o.created_at DESC;

# Self JOIN (table jointe sur elle-meme)
SELECT e.nom AS employe, m.nom AS manager
FROM employes e
LEFT JOIN employes m ON e.manager_id = m.id;

# UNION : combiner les resultats de deux requetes
SELECT nom, prenom, 'client' AS type FROM clients
UNION ALL
SELECT nom, prenom, 'fournisseur' AS type FROM fournisseurs
ORDER BY nom;
Astuce : Utiliser des alias courts (c pour clients, o pour commandes) pour rendre les requetes avec jointures plus lisibles. Preferer INNER JOIN avec clause ON explicite plutot que la syntaxe WHERE implicite pour la clarte.

6. Fonctions utiles

6.1 Fonctions de date

# Date et heure courantes
SELECT NOW();                              -- 2024-12-15 14:30:00
SELECT CURDATE();                          -- 2024-12-15
SELECT CURTIME();                          -- 14:30:00
SELECT UNIX_TIMESTAMP();                   -- timestamp Unix courant

# Extraire des parties de date
SELECT YEAR(NOW());                        -- 2024
SELECT MONTH(NOW());                       -- 12
SELECT DAY(NOW());                         -- 15
SELECT DAYNAME(NOW());                     -- Sunday
SELECT DAYOFWEEK(NOW());                   -- 1 (dimanche) a 7 (samedi)
SELECT HOUR(NOW());                        -- 14

# Formater une date
SELECT DATE_FORMAT(NOW(), '%d/%m/%Y');            -- 15/12/2024
SELECT DATE_FORMAT(NOW(), '%d %M %Y a %H:%i');   -- 15 December 2024 a 14:30

# Calculs de dates
SELECT DATE_ADD(NOW(), INTERVAL 30 DAY);          -- +30 jours
SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH);         -- -1 mois
SELECT DATE_ADD(NOW(), INTERVAL 2 HOUR);           -- +2 heures
SELECT DATEDIFF('2024-12-31', '2024-01-01');       -- 365 (difference en jours)
SELECT TIMESTAMPDIFF(MONTH, '2024-01-01', NOW());  -- nombre de mois ecoules
SELECT TIMESTAMPDIFF(YEAR, date_naissance, CURDATE()) AS age FROM clients;

# Debut et fin de periodes
SELECT LAST_DAY(NOW());                    -- dernier jour du mois courant
SELECT DATE_FORMAT(NOW(), '%Y-%m-01');     -- premier jour du mois courant

6.2 Fonctions de chaines

# Concatenation
SELECT CONCAT(prenom, ' ', nom) AS nom_complet FROM clients;
SELECT CONCAT_WS(', ', ville, code_postal, pays) AS adresse_complete FROM clients;

# Majuscules / Minuscules
SELECT UPPER(nom) FROM clients;            -- DUPONT
SELECT LOWER(email) FROM clients;          -- jean.dupont@email.com

# Longueur
SELECT LENGTH(nom) FROM clients;           -- longueur en octets
SELECT CHAR_LENGTH(nom) FROM clients;      -- longueur en caracteres

# Extraction
SELECT SUBSTRING(nom, 1, 3) FROM clients;       -- 3 premiers caracteres
SELECT LEFT(nom, 5) FROM clients;                -- 5 caracteres depuis la gauche
SELECT RIGHT(email, 10) FROM clients;            -- 10 caracteres depuis la droite

# Recherche et remplacement
SELECT REPLACE(telephone, '-', '') FROM clients;    -- supprimer les tirets
SELECT LOCATE('@', email) FROM clients;             -- position du @
SELECT TRIM('  texte  ');                            -- supprime les espaces
SELECT TRIM(BOTH '-' FROM '--texte--');              -- supprime les tirets

# Remplissage
SELECT LPAD(id, 6, '0') FROM clients;       -- 000042
SELECT RPAD(nom, 20, '.') FROM clients;     -- Dupont.............

# Inversion
SELECT REVERSE(nom) FROM clients;            -- tnopuD

6.3 Fonctions d'agregation

# Comptage
SELECT COUNT(*) AS total_clients FROM clients;
SELECT COUNT(DISTINCT ville) AS nb_villes FROM clients;

# Somme, Moyenne, Min, Max
SELECT
    COUNT(*) AS nb_commandes,
    SUM(montant) AS total,
    AVG(montant) AS moyenne,
    MIN(montant) AS minimum,
    MAX(montant) AS maximum
FROM commandes
WHERE statut = 'validee';

# Groupement avec agregation
SELECT
    statut,
    COUNT(*) AS nombre,
    SUM(montant) AS total,
    ROUND(AVG(montant), 2) AS moyenne
FROM commandes
GROUP BY statut
ORDER BY total DESC;

# Groupement par date (par mois)
SELECT
    DATE_FORMAT(created_at, '%Y-%m') AS mois,
    COUNT(*) AS nb_commandes,
    SUM(montant) AS chiffre_affaires
FROM commandes
WHERE statut = 'validee'
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
ORDER BY mois DESC;

# GROUP_CONCAT : concatener des valeurs groupees
SELECT
    c.nom,
    c.prenom,
    GROUP_CONCAT(o.id ORDER BY o.id SEPARATOR ', ') AS commandes_ids
FROM clients c
INNER JOIN commandes o ON c.id = o.client_id
GROUP BY c.id, c.nom, c.prenom;

6.4 Fonctions conditionnelles (IF / CASE)

# IF simple
SELECT nom, IF(actif = 1, 'Actif', 'Inactif') AS statut FROM clients;

# IFNULL / COALESCE (gerer les valeurs NULL)
SELECT nom, IFNULL(telephone, 'Non renseigne') AS telephone FROM clients;
SELECT nom, COALESCE(telephone, email, 'Aucun contact') AS contact FROM clients;

# CASE WHEN
SELECT
    nom,
    montant,
    CASE
        WHEN montant >= 1000 THEN 'Premium'
        WHEN montant >= 500  THEN 'Standard'
        WHEN montant >= 100  THEN 'Basique'
        ELSE 'Micro'
    END AS categorie
FROM commandes
ORDER BY montant DESC;

# CASE dans un GROUP BY
SELECT
    CASE
        WHEN TIMESTAMPDIFF(YEAR, date_naissance, CURDATE()) < 25 THEN '18-24'
        WHEN TIMESTAMPDIFF(YEAR, date_naissance, CURDATE()) < 35 THEN '25-34'
        WHEN TIMESTAMPDIFF(YEAR, date_naissance, CURDATE()) < 50 THEN '35-49'
        ELSE '50+'
    END AS tranche_age,
    COUNT(*) AS nombre
FROM clients
GROUP BY tranche_age
ORDER BY tranche_age;

6.5 Fonctions numeriques

SELECT ROUND(3.14159, 2);         -- 3.14
SELECT CEIL(3.2);                  -- 4
SELECT FLOOR(3.8);                 -- 3
SELECT ABS(-42);                   -- 42
SELECT MOD(10, 3);                 -- 1
SELECT RAND();                     -- nombre aleatoire entre 0 et 1
SELECT FLOOR(RAND() * 100) + 1;   -- nombre aleatoire entre 1 et 100

7. Sauvegarde et restauration

7.1 mysqldump - Sauvegarde classique

# Sauvegarder une base complete
mysqldump -u root -p ma_base > /var/backups/mysql/ma_base_$(date +%Y%m%d).sql

# Sauvegarder avec compression
mysqldump -u root -p ma_base | gzip > /var/backups/mysql/ma_base_$(date +%Y%m%d).sql.gz

# Sauvegarder toutes les bases
mysqldump -u root -p --all-databases > /var/backups/mysql/all_databases.sql

# Sauvegarder plusieurs bases specifiques
mysqldump -u root -p --databases base1 base2 base3 > /var/backups/mysql/multi_bases.sql

# Sauvegarder seulement certaines tables
mysqldump -u root -p ma_base table1 table2 > /var/backups/mysql/tables_specifiques.sql

# Sauvegarder uniquement la structure (sans les donnees)
mysqldump -u root -p --no-data ma_base > /var/backups/mysql/ma_base_structure.sql

# Sauvegarder uniquement les donnees (sans la structure)
mysqldump -u root -p --no-create-info ma_base > /var/backups/mysql/ma_base_data.sql

# Options recommandees pour la production
mysqldump -u root -p \
    --single-transaction \
    --routines \
    --triggers \
    --events \
    --quick \
    --lock-tables=false \
    ma_base | gzip > /var/backups/mysql/ma_base_$(date +%Y%m%d_%H%M%S).sql.gz
Astuce : L'option --single-transaction est essentielle pour les tables InnoDB : elle effectue un dump coherent sans verrouiller les tables, permettant aux ecritures de continuer pendant la sauvegarde. Ne pas l'utiliser sur des tables MyISAM.

7.2 mysqlpump - Sauvegarde parallelisee (MySQL 5.7+)

# Sauvegarde parallelisee (plus rapide sur les grosses bases)
mysqlpump -u root -p --default-parallelism=4 ma_base > /var/backups/mysql/ma_base.sql

# Sauvegarder tout sauf certaines bases
mysqlpump -u root -p --exclude-databases=information_schema,performance_schema,sys \
    --all-databases > /var/backups/mysql/all_databases.sql

# Sauvegarder avec compression integree
mysqlpump -u root -p --compress-output=ZLIB ma_base > /var/backups/mysql/ma_base.zlib

7.3 Restauration

# Restaurer depuis un fichier SQL
mysql -u root -p ma_base < /var/backups/mysql/ma_base.sql

# Restaurer un fichier compresse (gzip)
gunzip < /var/backups/mysql/ma_base.sql.gz | mysql -u root -p ma_base

# Restaurer depuis le prompt MySQL
mysql> USE ma_base;
mysql> SOURCE /var/backups/mysql/ma_base.sql;

# Restaurer toutes les bases
mysql -u root -p < /var/backups/mysql/all_databases.sql

# Restaurer avec affichage de progression (pv)
pv /var/backups/mysql/ma_base.sql | mysql -u root -p ma_base
# ou avec compression
pv /var/backups/mysql/ma_base.sql.gz | gunzip | mysql -u root -p ma_base
Astuce : Installer pv (pipe viewer) pour voir la progression de la restauration :
sudo apt install pv -y    # Debian/Ubuntu
sudo dnf install pv -y    # CentOS/RHEL

7.4 Script de sauvegarde automatisee

#!/bin/bash
# /usr/local/bin/mysql-backup.sh

BACKUP_DIR="/var/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION=30  # jours

# Creer le repertoire si necessaire
mkdir -p "$BACKUP_DIR"

# Sauvegarder chaque base individuellement
for DB in $(mysql -u root -e "SHOW DATABASES;" -s --skip-column-names | grep -Ev "^(information_schema|performance_schema|sys)$"); do
    mysqldump -u root \
        --single-transaction \
        --routines \
        --triggers \
        --events \
        "$DB" | gzip > "${BACKUP_DIR}/${DB}_${DATE}.sql.gz"
    echo "[OK] ${DB} sauvegardee"
done

# Supprimer les sauvegardes de plus de N jours
find "$BACKUP_DIR" -name "*.sql.gz" -type f -mtime +${RETENTION} -delete
echo "[OK] Nettoyage des sauvegardes de plus de ${RETENTION} jours"
# Rendre le script executable
sudo chmod +x /usr/local/bin/mysql-backup.sh

# Ajouter au crontab (sauvegarde quotidienne a 2h du matin)
sudo crontab -e
0 2 * * * /usr/local/bin/mysql-backup.sh >> /var/log/mysql-backup.log 2>&1
Attention : Eviter de stocker le mot de passe en clair dans les scripts. Utiliser un fichier ~/.my.cnf avec des permissions restrictives :
# Creer le fichier ~/.my.cnf
[client]
user=root
password=VotreMotDePasse

# Securiser les permissions
chmod 600 ~/.my.cnf

8. Variables et configuration

8.1 Fichiers de configuration

  • Debian / Ubuntu (MySQL) : /etc/mysql/mysql.conf.d/mysqld.cnf
  • Debian / Ubuntu (MariaDB) : /etc/mysql/mariadb.conf.d/50-server.cnf
  • CentOS / RHEL : /etc/my.cnf ou /etc/my.cnf.d/
  • Windows (XAMPP) : C:\xampp\mysql\bin\my.ini
  • Windows (MySQL Server) : C:\ProgramData\MySQL\MySQL Server 8.0\my.ini

8.2 Configuration recommandee (my.cnf)

[mysqld]
# --- Reseau ---
bind-address            = 127.0.0.1
port                    = 3306
max_connections         = 200
max_connect_errors      = 100000
wait_timeout            = 600
interactive_timeout     = 600

# --- Jeu de caracteres ---
character-set-server    = utf8mb4
collation-server        = utf8mb4_unicode_ci

# --- InnoDB (moteur principal) ---
innodb_buffer_pool_size = 1G          # 50-70% de la RAM sur un serveur dedie
innodb_log_file_size    = 256M
innodb_flush_log_at_trx_commit = 1    # Securite maximale (2 pour + de perf)
innodb_flush_method     = O_DIRECT
innodb_file_per_table   = 1
innodb_io_capacity      = 2000
innodb_io_capacity_max  = 4000

# --- Taille des paquets ---
max_allowed_packet      = 64M

# --- Tables temporaires ---
tmp_table_size          = 64M
max_heap_table_size     = 64M

# --- Tri et jointure ---
sort_buffer_size        = 4M
join_buffer_size        = 4M
read_buffer_size        = 2M
read_rnd_buffer_size    = 2M

# --- Cache de tables ---
table_open_cache        = 4000
table_definition_cache  = 2000

# --- Logs ---
log_error               = /var/log/mysql/error.log
slow_query_log          = 1
slow_query_log_file     = /var/log/mysql/slow.log
long_query_time         = 2
log_queries_not_using_indexes = 1

# --- Binlog (necessaire pour la replication) ---
# server-id             = 1
# log_bin               = /var/log/mysql/mysql-bin
# binlog_expire_logs_seconds = 604800   # 7 jours
# binlog_format         = ROW

[client]
default-character-set   = utf8mb4

[mysql]
default-character-set   = utf8mb4

8.3 Consulter les variables

# Voir toutes les variables
SHOW VARIABLES;

# Filtrer par nom
SHOW VARIABLES LIKE 'max_connections';
SHOW VARIABLES LIKE 'innodb_buffer%';
SHOW VARIABLES LIKE '%timeout%';
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';

# Voir toutes les variables globales (meme resultat)
SHOW GLOBAL VARIABLES LIKE '%log%';

# Variables de session
SHOW SESSION VARIABLES LIKE 'wait_timeout';

# Voir le statut du serveur (compteurs)
SHOW GLOBAL STATUS;
SHOW GLOBAL STATUS LIKE 'Threads_%';
SHOW GLOBAL STATUS LIKE 'Connections';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';

8.4 Modifier les variables a chaud

# Modifier une variable globale (prend effet pour les nouvelles connexions)
SET GLOBAL max_connections = 300;
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;

# Modifier une variable de session (prend effet seulement pour la session courante)
SET SESSION wait_timeout = 3600;
SET SESSION group_concat_max_len = 1000000;

# Verifier la modification
SHOW VARIABLES LIKE 'max_connections';
Attention : Les modifications avec SET GLOBAL sont perdues au redemarrage du serveur MySQL. Pour les rendre permanentes, il faut aussi les ajouter dans le fichier my.cnf. Certaines variables (comme innodb_buffer_pool_size) ne peuvent pas etre modifiees a chaud dans toutes les versions.

9. Performance

9.1 EXPLAIN - Analyser une requete

# Analyser le plan d'execution d'une requete
EXPLAIN SELECT * FROM clients WHERE nom = 'Dupont';

# Format detaille (JSON)
EXPLAIN FORMAT=JSON SELECT * FROM clients WHERE nom = 'Dupont';

# EXPLAIN ANALYZE (MySQL 8.0.18+ / MariaDB 10.1+) - execute reellement la requete
EXPLAIN ANALYZE SELECT c.nom, COUNT(o.id)
FROM clients c
LEFT JOIN commandes o ON c.id = o.client_id
GROUP BY c.id;

Colonnes importantes de EXPLAIN :

Colonne Signification A surveiller
typeType de jointureALL = scan complet (mauvais). Preferer : ref, eq_ref, const
possible_keysIndex utilisablesNULL = aucun index disponible
keyIndex reellement utiliseNULL = aucun index utilise
rowsLignes estimees a examinerPlus c'est bas, mieux c'est
ExtraInformations supplementairesUsing filesort ou Using temporary = potentiellement lent

9.2 Index - Optimiser les requetes

# Voir les index existants
SHOW INDEX FROM clients;

# Creer un index simple
CREATE INDEX idx_nom ON clients(nom);

# Creer un index unique
CREATE UNIQUE INDEX idx_email ON clients(email);

# Creer un index composite (colonnes souvent utilisees ensemble)
CREATE INDEX idx_ville_actif ON clients(ville, actif);

# Index sur un prefixe (pour les colonnes TEXT/VARCHAR longues)
CREATE INDEX idx_adresse ON clients(adresse(50));

# Index FULLTEXT (recherche en texte integral)
CREATE FULLTEXT INDEX idx_ft_description ON produits(description);
-- Utilisation :
SELECT * FROM produits WHERE MATCH(description) AGAINST('ecran haute definition' IN NATURAL LANGUAGE MODE);

# Supprimer un index
DROP INDEX idx_nom ON clients;

# Verifier si un index est utilise
EXPLAIN SELECT * FROM clients WHERE nom = 'Dupont' AND actif = 1;
Astuce : Regles d'or pour les index :
  • Indexer les colonnes utilisees dans les clauses WHERE, JOIN, ORDER BY et GROUP BY
  • Pour un index composite, l'ordre des colonnes compte : mettre la colonne la plus selective en premier
  • Trop d'index ralentissent les INSERT/UPDATE/DELETE (chaque index doit etre mis a jour)
  • Un index composite (a, b, c) couvre les recherches sur (a), (a, b) et (a, b, c), mais pas (b) ou (c) seuls

9.3 Slow Query Log

# Activer le log des requetes lentes (a chaud)
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;           -- seuil en secondes
SET GLOBAL log_queries_not_using_indexes = 1;

# Verifier la configuration
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';

# Lire le fichier de slow log
sudo tail -100 /var/log/mysql/slow.log

# Analyser le slow log avec mysqldumpslow
sudo mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# -s t : trier par temps total
# -t 10 : top 10 requetes

# Ou avec pt-query-digest (Percona Toolkit, plus puissant)
sudo apt install percona-toolkit -y
pt-query-digest /var/log/mysql/slow.log

9.4 OPTIMIZE TABLE

# Optimiser une table (reorganise les donnees et reconstruit les index)
OPTIMIZE TABLE clients;

# Optimiser plusieurs tables
OPTIMIZE TABLE clients, commandes, produits;

# Analyser les statistiques d'une table (aide l'optimiseur de requetes)
ANALYZE TABLE clients;

# Verifier l'integrite d'une table
CHECK TABLE clients;

# Reparer une table (MyISAM seulement)
REPAIR TABLE ma_table_myisam;

9.5 Diagnostic de performance

# Nombre de connexions actives vs max
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';

# Ratio d'utilisation du buffer pool InnoDB (objectif : > 95%)
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
-- Ratio = (read_requests - reads) / read_requests * 100

# Tables les plus volumineuses
SELECT table_name, table_rows,
       ROUND(data_length / 1024 / 1024, 2) AS data_mb,
       ROUND(index_length / 1024 / 1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = 'ma_base'
ORDER BY data_length + index_length DESC
LIMIT 20;

# Tables sans cle primaire (a corriger pour les performances)
SELECT t.table_schema, t.table_name
FROM information_schema.tables t
LEFT JOIN information_schema.table_constraints c
    ON t.table_schema = c.table_schema
    AND t.table_name = c.table_name
    AND c.constraint_type = 'PRIMARY KEY'
WHERE t.table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
    AND t.table_type = 'BASE TABLE'
    AND c.constraint_name IS NULL;
Attention : OPTIMIZE TABLE verrouille la table pendant l'operation. Sur les tres grosses tables en production, planifier cette operation pendant une fenetre de maintenance. Pour InnoDB, OPTIMIZE TABLE equivaut a ALTER TABLE ... FORCE qui reconstruit entierement la table.

10. Replication

La replication master/slave (ou source/replica en terminologie MySQL 8) permet de copier automatiquement les donnees d'un serveur principal vers un ou plusieurs serveurs secondaires. Utile pour la lecture distribuee, la sauvegarde en temps reel et la haute disponibilite.

10.1 Configuration du serveur Master (source)

# Editer /etc/mysql/mysql.conf.d/mysqld.cnf (ou /etc/my.cnf)
[mysqld]
server-id               = 1
log_bin                 = /var/log/mysql/mysql-bin
binlog_format           = ROW
binlog_expire_logs_seconds = 604800    # 7 jours (MySQL 8)
# expire_logs_days      = 7           # pour MySQL 5.7 / MariaDB
max_binlog_size         = 100M
binlog_do_db            = ma_base     # optionnel : repliquer seulement cette base
# binlog_ignore_db      = test        # optionnel : exclure une base
# Redemarrer MySQL
sudo systemctl restart mysql

# Se connecter et creer l'utilisateur de replication
mysql -u root -p

CREATE USER 'replicateur'@'192.168.1.%' IDENTIFIED BY 'M0tDePasse!Replica2024';
GRANT REPLICATION SLAVE ON *.* TO 'replicateur'@'192.168.1.%';
FLUSH PRIVILEGES;

# Verrouiller les tables et noter la position du binlog
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
-- +------------------+----------+--------------+------------------+
-- | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
-- +------------------+----------+--------------+------------------+
-- | mysql-bin.000003 |      785 | ma_base      |                  |
-- +------------------+----------+--------------+------------------+
-- NOTER le File et la Position !
# Dans un autre terminal, faire le dump initial
mysqldump -u root -p --all-databases --master-data=2 --single-transaction > /tmp/master_dump.sql

# De retour dans le prompt MySQL, deverrouiller les tables
UNLOCK TABLES;

# Copier le dump vers le serveur slave
scp /tmp/master_dump.sql user@192.168.1.51:/tmp/

10.2 Configuration du serveur Slave (replica)

# Editer /etc/mysql/mysql.conf.d/mysqld.cnf sur le serveur slave
[mysqld]
server-id               = 2           # DOIT etre different du master
relay_log               = /var/log/mysql/mysql-relay-bin
read_only               = 1           # Empecher les ecritures sur le slave
# replicate_do_db       = ma_base     # optionnel : repliquer seulement cette base
# Redemarrer MySQL sur le slave
sudo systemctl restart mysql

# Importer le dump du master
mysql -u root -p < /tmp/master_dump.sql

# Configurer la connexion vers le master
mysql -u root -p

CHANGE MASTER TO
    MASTER_HOST='192.168.1.50',
    MASTER_USER='replicateur',
    MASTER_PASSWORD='M0tDePasse!Replica2024',
    MASTER_LOG_FILE='mysql-bin.000003',
    MASTER_LOG_POS=785;

# Syntaxe MySQL 8.0.23+ :
-- CHANGE REPLICATION SOURCE TO
--     SOURCE_HOST='192.168.1.50',
--     SOURCE_USER='replicateur',
--     SOURCE_PASSWORD='M0tDePasse!Replica2024',
--     SOURCE_LOG_FILE='mysql-bin.000003',
--     SOURCE_LOG_POS=785;

# Demarrer la replication
START SLAVE;
-- MySQL 8.0.22+ : START REPLICA;

10.3 Verifier et surveiller la replication

# Sur le slave : verifier le statut de la replication
SHOW SLAVE STATUS\G

# Points importants a verifier :
-- Slave_IO_Running: Yes       (connexion au master OK)
-- Slave_SQL_Running: Yes      (application des events OK)
-- Seconds_Behind_Master: 0    (pas de retard)
-- Last_Error:                 (vide si tout va bien)

# Sur le master : verifier les slaves connectes
SHOW PROCESSLIST;
SHOW SLAVE HOSTS;

# Verifier la position du binlog
SHOW MASTER STATUS;

10.4 Depannage de la replication

# Arreter la replication
STOP SLAVE;

# Ignorer une erreur de replication (avec precaution)
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;

# Reinitialiser completement la configuration du slave
STOP SLAVE;
RESET SLAVE ALL;

# Resynchroniser le slave depuis zero
# 1. Sur le master : faire un nouveau dump
# 2. Sur le slave : importer et reconfigurer CHANGE MASTER TO
Attention : Ignorer des erreurs de replication avec SQL_SLAVE_SKIP_COUNTER peut causer des divergences de donnees entre le master et le slave. Utiliser cette commande uniquement en dernier recours et verifier la coherence des donnees apres. En cas de doute, resynchroniser completement le slave.

11. Securisation

11.1 mysql_secure_installation

Script interactif a executer immediatement apres l'installation :

sudo mysql_secure_installation

# Le script propose de :
# 1. Definir/modifier le mot de passe root
# 2. Supprimer les utilisateurs anonymes              -> Y
# 3. Desactiver la connexion root a distance          -> Y
# 4. Supprimer la base de test                        -> Y
# 5. Recharger les tables de privileges               -> Y

11.2 Restreindre les acces reseau

# Dans my.cnf [mysqld] :
# Ecouter uniquement sur localhost (recommande si pas d'acces distant)
bind-address = 127.0.0.1

# Ou sur une interface specifique
bind-address = 192.168.1.50

# Desactiver le reseau completement (connexion socket uniquement)
skip-networking

11.3 Politique de mots de passe (MySQL 8)

# Verifier le plugin de validation
SHOW VARIABLES LIKE 'validate_password%';

# Configurer la politique
SET GLOBAL validate_password.policy = MEDIUM;    -- LOW, MEDIUM, STRONG
SET GLOBAL validate_password.length = 12;
SET GLOBAL validate_password.mixed_case_count = 1;
SET GLOBAL validate_password.number_count = 1;
SET GLOBAL validate_password.special_char_count = 1;

# Dans my.cnf pour rendre permanent :
[mysqld]
validate_password.policy = MEDIUM
validate_password.length = 12

11.4 Supprimer les elements inutiles

# Supprimer la base de test
DROP DATABASE IF EXISTS test;

# Supprimer les comptes anonymes
DELETE FROM mysql.user WHERE User = '';

# Supprimer les comptes root distants
DELETE FROM mysql.user WHERE User = 'root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');

# Appliquer
FLUSH PRIVILEGES;

# Verifier les comptes restants
SELECT user, host, plugin FROM mysql.user;

11.5 Chiffrement SSL/TLS des connexions

# Verifier si SSL est actif
SHOW VARIABLES LIKE '%ssl%';
SHOW STATUS LIKE 'Ssl_cipher';

# Forcer SSL pour un utilisateur
ALTER USER 'appuser'@'%' REQUIRE SSL;

# Forcer SSL avec certificat client
ALTER USER 'appuser'@'%' REQUIRE X509;

# Connexion avec SSL depuis le client
mysql -u appuser -p --ssl-mode=REQUIRED -h serveur.example.com

# Dans my.cnf pour activer SSL globalement :
[mysqld]
require_secure_transport = ON
ssl-ca   = /etc/mysql/ssl/ca-cert.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key  = /etc/mysql/ssl/server-key.pem

11.6 Audit et journalisation

# Activer le log general (toutes les requetes - attention a l'espace disque)
SET GLOBAL general_log = 1;
SET GLOBAL general_log_file = '/var/log/mysql/general.log';

# Desactiver quand l'audit est termine
SET GLOBAL general_log = 0;

# Activer le log des requetes lentes
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 2;
Astuce : Bonnes pratiques de securite MySQL en resume :
  • Executer mysql_secure_installation immediatement apres l'installation
  • Utiliser bind-address = 127.0.0.1 sauf si l'acces distant est necessaire
  • Creer un utilisateur dedie par application (jamais utiliser root)
  • Appliquer le principe du moindre privilege (GRANT minimal)
  • Utiliser des mots de passe forts (12+ caracteres)
  • Activer SSL/TLS pour les connexions distantes
  • Maintenir MySQL/MariaDB a jour (correctifs de securite)
  • Sauvegarder regulierement et tester les restaurations

12. Commandes d'administration

12.1 Processus et connexions

# Voir tous les processus en cours
SHOW PROCESSLIST;
SHOW FULL PROCESSLIST;

# Tuer un processus bloquant (remplacer par l'ID du processus)
KILL 42;

# Tuer uniquement la requete (garder la connexion)
KILL QUERY 42;

# Voir les connexions actives groupees par utilisateur
SELECT user, host, db, command, time, state
FROM information_schema.processlist
WHERE command != 'Sleep'
ORDER BY time DESC;

# Trouver les requetes longues (> 60 secondes)
SELECT id, user, host, db, command, time, state, info
FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 60
ORDER BY time DESC;

12.2 Statut du serveur

# Statut general
STATUS;

# Statut detaille
SHOW GLOBAL STATUS;

# Metriques importantes
SHOW GLOBAL STATUS LIKE 'Uptime';                  -- temps de fonctionnement
SHOW GLOBAL STATUS LIKE 'Questions';                -- nombre total de requetes
SHOW GLOBAL STATUS LIKE 'Threads_%';                -- threads actifs
SHOW GLOBAL STATUS LIKE 'Connections';              -- connexions totales
SHOW GLOBAL STATUS LIKE 'Aborted_%';                -- connexions echouees
SHOW GLOBAL STATUS LIKE 'Slow_queries';             -- requetes lentes
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';  -- tables temp sur disque
SHOW GLOBAL STATUS LIKE 'Open_tables';              -- tables ouvertes
SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_%';        -- verrous InnoDB

# Statut InnoDB detaille
SHOW ENGINE INNODB STATUS\G

12.3 Informations sur les tables

# Statut de toutes les tables d'une base
SHOW TABLE STATUS FROM ma_base;

# Statut d'une table specifique
SHOW TABLE STATUS FROM ma_base LIKE 'clients'\G

# Informations utiles retournees :
-- Engine : moteur de stockage (InnoDB, MyISAM, etc.)
-- Rows : nombre de lignes estimees
-- Data_length : taille des donnees en octets
-- Index_length : taille des index en octets
-- Auto_increment : prochaine valeur auto-increment
-- Create_time : date de creation
-- Update_time : derniere modification

# Voir les tables ouvertes
SHOW OPEN TABLES FROM ma_base;

12.4 Gestion des verrous

# Voir les verrous en attente (MySQL 8 / MariaDB 10.5+)
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;

# Version simplifiee pour identifier les blocages
SELECT
    r.trx_id AS waiting_trx_id,
    r.trx_mysql_thread_id AS waiting_thread,
    r.trx_query AS waiting_query,
    b.trx_id AS blocking_trx_id,
    b.trx_mysql_thread_id AS blocking_thread,
    b.trx_query AS blocking_query
FROM information_schema.innodb_lock_waits w
INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;

# Verrouiller / deverrouiller manuellement les tables
LOCK TABLES clients READ;           -- verrou en lecture (partage)
LOCK TABLES clients WRITE;          -- verrou en ecriture (exclusif)
UNLOCK TABLES;

12.5 Maintenance courante

# Vider les caches
FLUSH PRIVILEGES;               -- recharger les privileges
FLUSH TABLES;                   -- fermer toutes les tables ouvertes
FLUSH LOGS;                     -- rotation des fichiers de log
FLUSH QUERY CACHE;              -- vider le cache de requetes (MySQL 5.7, supprime en 8)
RESET QUERY CACHE;              -- reinitialiser le cache de requetes

# Verifier et reparer les tables (utile apres un crash)
CHECK TABLE clients;
REPAIR TABLE clients;           -- MyISAM seulement
OPTIMIZE TABLE clients;         -- reorganiser les donnees

# Reinitialiser la valeur auto-increment
ALTER TABLE clients AUTO_INCREMENT = 1;

# Voir la version et les plugins
SHOW PLUGINS;
SELECT VERSION();

# Voir les moteurs de stockage disponibles
SHOW ENGINES;
Astuce : Pour surveiller MySQL en continu, utiliser mysqladmin depuis le terminal :
# Statut rapide
mysqladmin -u root -p status

# Statut etendu (toutes les 5 secondes)
mysqladmin -u root -p extended-status -i 5

# Verifier si le serveur est actif
mysqladmin -u root -p ping

# Lister les processus
mysqladmin -u root -p processlist

# Rotation des logs
mysqladmin -u root -p flush-logs

13. Depannage courant

13.1 MySQL ne demarre pas

# Verifier le statut et les messages d'erreur
sudo systemctl status mysql
sudo journalctl -u mysql --since "10 minutes ago"

# Consulter le log d'erreur MySQL
sudo tail -100 /var/log/mysql/error.log

# Causes frequentes et solutions :

# 1. Espace disque insuffisant
df -h /var/lib/mysql
# Solution : liberer de l'espace ou deplacer le datadir

# 2. Permissions incorrectes sur le datadir
sudo ls -la /var/lib/mysql
sudo chown -R mysql:mysql /var/lib/mysql
sudo chmod 750 /var/lib/mysql

# 3. Fichier PID manquant ou corrompu
sudo rm -f /var/run/mysqld/mysqld.pid
sudo mkdir -p /var/run/mysqld
sudo chown mysql:mysql /var/run/mysqld
sudo systemctl start mysql

# 4. Port 3306 deja utilise
sudo ss -tlnp | grep 3306
# Identifier et arreter le processus conflictuel

# 5. Erreur dans my.cnf
# Commenter les dernieres modifications et redemarrer
sudo systemctl start mysql

13.2 Mot de passe root oublie

# 1. Arreter MySQL
sudo systemctl stop mysql

# 2. Demarrer en mode sans authentification
sudo mysqld_safe --skip-grant-tables --skip-networking &

# 3. Se connecter sans mot de passe
mysql -u root

# 4. Modifier le mot de passe
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NouveauMotDePasse!2024';
FLUSH PRIVILEGES;
EXIT;

# 5. Arreter le mode sans authentification et redemarrer normalement
sudo killall mysqld
sudo systemctl start mysql

# 6. Tester la connexion avec le nouveau mot de passe
mysql -u root -p

13.3 Erreur "Too many connections"

# Verifier le nombre de connexions actives
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';

# Solution temporaire : augmenter la limite a chaud
SET GLOBAL max_connections = 500;

# Solution permanente : modifier my.cnf
# [mysqld]
# max_connections = 500

# Identifier les connexions inutiles (en veille depuis longtemps)
SELECT id, user, host, db, command, time
FROM information_schema.processlist
WHERE command = 'Sleep' AND time > 300
ORDER BY time DESC;

# Tuer les connexions en veille de plus de 5 minutes
# (faire un script ou utiliser wait_timeout)
SET GLOBAL wait_timeout = 300;
SET GLOBAL interactive_timeout = 300;

13.4 Erreur "Table is full"

# Verifier l'espace disque
df -h /var/lib/mysql

# Verifier la taille de la table
SELECT table_name,
       ROUND(data_length / 1024 / 1024, 2) AS data_mb,
       ROUND(index_length / 1024 / 1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = 'ma_base' AND table_name = 'ma_table';

# Verifier les limites de la variable tmp_table_size
SHOW VARIABLES LIKE 'tmp_table_size';
SHOW VARIABLES LIKE 'max_heap_table_size';

# Augmenter si necessaire
SET GLOBAL tmp_table_size = 128 * 1024 * 1024;         -- 128 Mo
SET GLOBAL max_heap_table_size = 128 * 1024 * 1024;    -- 128 Mo

# Si la table InnoDB est fragmentee, la reconstruire
ALTER TABLE ma_table ENGINE=InnoDB;

13.5 Erreur "Lock wait timeout exceeded"

# Identifier la transaction bloquante
SHOW ENGINE INNODB STATUS\G
-- Chercher la section "LATEST DETECTED DEADLOCK" et "TRANSACTIONS"

# Voir les verrous actifs
SELECT * FROM information_schema.innodb_trx;

# Identifier qui bloque qui
SELECT
    r.trx_id AS waiting_trx,
    r.trx_mysql_thread_id AS waiting_thread,
    r.trx_query AS waiting_query,
    b.trx_id AS blocking_trx,
    b.trx_mysql_thread_id AS blocking_thread,
    b.trx_query AS blocking_query
FROM information_schema.innodb_lock_waits w
INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;

# Tuer la transaction bloquante (avec precaution)
KILL 42;   -- remplacer 42 par le blocking_thread

# Augmenter le timeout si necessaire
SET GLOBAL innodb_lock_wait_timeout = 120;  -- 120 secondes (defaut: 50)

13.6 Erreur de connexion "Access denied"

# Verifier les privileges de l'utilisateur
SELECT user, host, plugin FROM mysql.user WHERE user = 'mon_user';
SHOW GRANTS FOR 'mon_user'@'localhost';

# Verifier que l'hote correspond
# 'mon_user'@'localhost' != 'mon_user'@'%' != 'mon_user'@'192.168.1.10'

# Recreer l'utilisateur si necessaire
DROP USER IF EXISTS 'mon_user'@'localhost';
CREATE USER 'mon_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'MotDePasse';
GRANT ALL PRIVILEGES ON ma_base.* TO 'mon_user'@'localhost';
FLUSH PRIVILEGES;

# Probleme courant MySQL 8 : plugin d'authentification
# Par defaut caching_sha2_password, certains clients ne le supportent pas
ALTER USER 'mon_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'MotDePasse';

13.7 Table corrompue

# Verifier l'integrite d'une table
CHECK TABLE ma_table;

# Reparer une table MyISAM
REPAIR TABLE ma_table;

# Pour InnoDB, la reparation se fait en reconstruisant la table
ALTER TABLE ma_table ENGINE=InnoDB;

# Si MySQL ne demarre pas a cause d'une table corrompue
# Ajouter dans my.cnf temporairement :
# [mysqld]
# innodb_force_recovery = 1
# (valeurs de 1 a 6, commencer par 1 et augmenter si necessaire)

# Redemarrer, faire un dump, puis restaurer sur une instance propre
mysqldump -u root -p --all-databases > /tmp/emergency_backup.sql

# Retirer innodb_force_recovery de my.cnf apres la restauration
Attention : Le mode innodb_force_recovery ne doit etre utilise qu'en dernier recours. A partir du niveau 4, il est en lecture seule et certaines donnees peuvent etre perdues. Toujours faire un dump complet des qu'on arrive a demarrer le serveur en mode recovery.

13.8 Aide-memoire de depannage

Probleme Premiere chose a verifier Commande
MySQL ne demarre pasLog d'erreursudo tail -50 /var/log/mysql/error.log
Requetes lentesSlow query logSHOW VARIABLES LIKE 'slow_query%';
Connexion refuseePort et bind-addresssudo ss -tlnp | grep 3306
Acces refuseUtilisateur et privilegesSHOW GRANTS FOR 'user'@'host';
Espace disque pleinTaille du datadirdf -h /var/lib/mysql
Trop de connexionsConnexions activesSHOW GLOBAL STATUS LIKE 'Threads_connected';
Verrous / BlocagesTransactions en coursSHOW ENGINE INNODB STATUS\G
Replication en retardStatut du slaveSHOW SLAVE STATUS\G
Performance generaleBuffer pool et indexSHOW GLOBAL STATUS LIKE 'Innodb_buffer%';
Astuce : Pour un diagnostic rapide et complet de l'etat du serveur MySQL, utiliser ces commandes dans l'ordre :
# 1. Le serveur est-il actif ?
sudo systemctl status mysql

# 2. Y a-t-il des erreurs recentes ?
sudo tail -20 /var/log/mysql/error.log

# 3. Combien de connexions actives ?
mysql -u root -p -e "SHOW GLOBAL STATUS LIKE 'Threads_connected';"

# 4. Y a-t-il des requetes bloquees ?
mysql -u root -p -e "SHOW PROCESSLIST;" | grep -v Sleep

# 5. Espace disque disponible ?
df -h /var/lib/mysql

# 6. Memoire utilisee ?
free -h