Mirajv1.0
FR

15. Administration du serveur

Ce chapitre s'adresse aux administrateurs qui exploitent miraj-server, le serveur réseau de MIRAJ. Il couvre le démarrage et l'arrêt, la configuration (ligne de commande et fichier XML), l'organisation du dossier de données, la durabilité, la sécurité opérationnelle, la supervision, la sauvegarde et les réglages de performance.

15.1 Démarrage et arrêt du serveur#

15.1.1 Commande de base#

miraj-server.exe --root D:\donnees --lang fr --log

--root désigne le dossier de données du serveur (un dossier par base, plus un sous-dossier miraj pour les comptes et les journaux internes). En son absence, le serveur utilise un dossier data relatif au répertoire courant.

Pour le dépannage et la maintenance des comptes (voir 15.5.3), le serveur dispose d'un mode dédié :

miraj-server.exe --root D:\donnees --reset-accounts [--key-dir <dossier>]

15.1.2 Arrêt du serveur#

Le seul arrêt ordonné de miraj-server est l'instruction SQL :

SHUTDOWN;

Elle demande le privilège SHUTDOWN. Le serveur écrit toutes les tables modifiées, répond au client, écrit une dernière fois ce qui est arrivé entre-temps, puis le processus s'arrête (SHUTDOWN : serveur arrêté à la demande d'un client. dans la console ou le journal). Un arrêt par cette instruction ne laisse donc rien à rejouer au redémarrage.

Le serveur ne traite en revanche aucun signal d'arrêt : que l'arrêt vienne d'un Ctrl-C, de la fermeture du processus (arrêt d'un service, kill) ou d'une coupure de la machine, aucun point de sauvegarde final n'est écrit avant la sortie. Tout arrêt qui ne passe pas par SHUTDOWN doit donc être traité, du point de vue de l'administrateur, comme un arrêt brutal : c'est le journal d'écriture (WAL) et sa reprise au redémarrage qui garantissent l'intégrité des données (voir 15.4). C'est différent de l'usage embarqué de MIRAJ (bibliothèque miraj.dll ou API Rust native) : là, la fermeture de l'instance (Miraj::drop) écrit tout avant de rendre la main.

Un dossier de données n'est ouvert que par un processus à la fois : le fichier miraj.lck de la racine est verrouillé tant que le serveur tourne. miraj-cli, miraj-backup --root ou miraj-dump --root lancés sur ce dossier pendant ce temps échouent avec « Dossier de données … déjà ouvert par un autre processus MIRAJ » ; passer alors par le réseau.

15.1.3 Options de ligne de commande#

Toutes les options sont facultatives ; les valeurs par défaut sont celles appliquées quand ni le fichier miraj_config.xml ni la ligne de commande ne les changent (voir 15.2 pour l'ordre de priorité).

OptionValeur par défautDescription
--root <dossier>dataDossier de données du serveur.
--config <fichier>à côté de l'exécutable, sinon <root>\miraj_config.xmlChemin explicite du fichier de configuration XML (voir 15.2).
--port <port>7007Port TCP d'écoute.
--bind <adresse>127.0.0.1Adresse d'écoute. Un avertissement est émis si une adresse non locale est choisie (voir 15.5.4).
--lang <code>enLangue des messages d'erreur des sessions : en, fr, zh, hi, es, ar, pt, ru, de, ja.
--logdésactivéTient à jour <root>\miraj\server.log (connexions, erreurs SQL, pannes internes ; voir 15.6).
--result-buffer <Mo>64Mémoire gardée par connexion pour un client lent à lire son résultat.
--write-timeout <secondes>60Attente maximale d'un client qui ne lit plus son résultat avant déconnexion.
--connect-timeout <secondes>10Attente maximale de la poignée de main d'un client, TLS comprise : échéance absolue comptée dès l'acceptation, qu'un client envoyant un octet de temps en temps ne repousse pas.
--max-connections <n>151Connexions simultanées au plus, de 1 à 100 000 (@@max_connections, modifiable par SET GLOBAL). Au-delà, le client reçoit l'erreur 1040 à la place de la poignée de main, sans qu'aucune session ne soit créée (voir 15.5.2).
--max-connect-errors <n>100Échecs d'authentification consécutifs d'une même adresse qui la bloquent 15 minutes (@@max_connect_errors, modifiable par SET GLOBAL) ; voir 15.5.2.
--proxy-protocol-from <adresse,…>aucuneAdresses IP de miraj-proxy, séparées par des virgules. Une connexion venue de l'une d'elles doit commencer par l'en-tête PROXY v2, qui donne l'adresse du vrai client : c'est elle qui choisit le compte (user@hôte), subit le blocage après échecs et s'affiche dans SHOW PROCESSLIST. Un client ainsi annoncé n'est jamais localhost, même sur la boucle locale : les comptes @localhost (et ceux des hôtes 127.0.0.1, ::1) lui sont fermés. Sans en-tête dans le délai de connexion, la connexion est fermée. Voir 22.5.
--initial-root-password <mot de passe>tiré au sortMot de passe de root à la création du coffre des comptes (premier démarrage sur un dossier, ou --reset-accounts) ; ignoré, avec une note au démarrage, une fois le coffre créé (sauf pour donner un mot de passe à un root local qui n'en a pas) ; vide, refusé. Aussi par la variable d'environnement MIRAJ_INITIAL_ROOT_PASSWORD (priorité : ligne de commande, environnement, fichier de configuration). Voir 11.5.2.
--require-password ON|OFFONRefuse un compte sans mot de passe joignable depuis une autre machine (@@require_password, modifiable par SET GLOBAL ; voir 14.2).
--idle-timeout <secondes>28800 (8 h)Attente maximale d'une commande d'un client déjà connecté.
--max-allowed-packet <octets>67108864 (64 Mio)Taille maximale d'une commande d'un client, des données longues d'une requête préparée et du fichier d'un LOAD DATA LOCAL, de 1024 octets à 1 Gio (@@max_allowed_packet). Une commande plus longue reçoit l'erreur 1153 et la connexion est fermée ; des données longues ou un fichier plus longs reçoivent l'erreur 1153 et la connexion reste ouverte. Avant l'authentification, un message est limité à 64 Kio.
--key-dir <dossier>profil du compte qui lance le serveurDossier de la clé du coffre des comptes, hors du dossier de données (voir 15.5.1).
--lock-wait-timeout <secondes>50Attente maximale d'un verrou de table explicite (LOCK TABLES) ou d'une ligne tenue par SELECT … FOR UPDATE avant l'erreur 1205 (valeur initiale de lock_wait_timeout et innodb_lock_wait_timeout).
--sql-mode <modes>STRICT_TRANS_TABLESValeur initiale de la variable sql_mode des sessions (@@GLOBAL.sql_mode, modifiable par SET GLOBAL pour les sessions suivantes). Mode strict par défaut : une valeur NULL écrite dans une colonne NOT NULL, ou une valeur invalide, rend une erreur (1048, 1366…). --sql-mode "" (ou une liste sans STRICT_TRANS_TABLES, STRICT_ALL_TABLES ni TRADITIONAL) : mode non strict, la valeur devient la valeur implicite de la colonne (0, chaîne vide…) avec un avertissement ; c'est le réglage attendu par les applications écrites pour un serveur configuré en mode non strict.
--deferred-update ON|OFFONMise à jour différée : un UPDATE admissible d'une ligne désignée par sa clé ne la prend pas et est réévalué au COMMIT, si bien que deux transactions qui modifient la même ligne valident toutes les deux (voir 13.2). Valeur initiale de la variable deferred_update des sessions ; OFF : l'UPDATE prend sa ligne tout de suite.
--concurrency mvocc|pessimisticmvoccModèle de concurrence (voir 13.2). mvocc : contrôle multiversion optimiste, sans verrou de ligne ni attente. pessimistic rétablit l'ancien modèle à verrous de ligne (attente bornée, erreurs 1205 / 1213) ; option temporaire, le temps de comparer les deux modèles.
--lazy-databases ON|OFFONChargement paresseux : les tables d'une base ne sont lues qu'à sa première utilisation (USE, table citée, SHOW TABLES FROM…), ce qui accélère le démarrage d'un dossier de nombreuses bases. La base système et les bases qui portent des événements planifiés sont ouvertes dès le démarrage ; un nœud de cluster ouvre toutes les siennes. OFF lit toutes les bases au démarrage.
--lob-threshold <octets>8192Taille à partir de laquelle une valeur BLOB quitte la table et rejoint le magasin .bmrj.
--lob-cache <Mo>128Taille du cache de lecture des BLOB déportés.
--event-scheduler ON|OFF|DISABLEDOFFÉtat du planificateur d'événements au démarrage ; DISABLED interdit SET GLOBAL event_scheduler = ON ensuite.
--parallel-threads <n>un fil par cœurFils que toutes les requêtes du serveur peuvent occuper en tout (éditions Entreprise et Cluster ; sans effet en éditions Express et Developer, avec avertissement).
--save-policy relaxed|statement|periodicrelaxedPolitique de durabilité (voir 15.4.1).
--save-interval <millisecondes>5000Intervalle du vidage périodique de fond (politiques relaxed et periodic).
--slow-query-logdésactivéActive le journal des requêtes lentes.
--slow-query-log-file <fichier><root>\miraj\slow.logFichier du journal des requêtes lentes ; le préciser active aussi le journal.
--long-query-time <secondes>10Durée au-delà de laquelle une instruction est considérée comme une requête lente.
--max-statement-time <secondes>0 (sans limite)Durée au-delà de laquelle une instruction est abandonnée (valeur initiale de la variable de session max_statement_time).
--group-concat-max-len <octets>1048576 (1 Mio)Longueur maximale d'un résultat de GROUP_CONCAT et de JSON_ARRAYAGG, de 4 à 4 294 967 295 octets (valeur initiale de la variable group_concat_max_len). Un résultat plus long est tronqué, sans couper un caractère, avec l'avertissement 1260 : une requête ne peut pas faire allouer une chaîne géante. Chaque session la change par SET [SESSION] group_concat_max_len = n ; SET GLOBAL fixe celle des sessions ouvertes ensuite.
--secure-file-priv <dossier>vide (LOAD_FILE désactivé)Seul dossier lisible par LOAD_FILE, pour les comptes ayant le privilège FILE. Ne doit ni contenir ni se trouver dans --backup-dir, le dossier de données, celui des clés, celui de la clé privée TLS (--tls-key) ou celui du journal des requêtes lentes, ni contenir le fichier de configuration ; sinon le serveur refuse de démarrer.
--backup-dir <dossier>vide (BACKUP / RESTORE refusés)Seul dossier où BACKUP DATABASE écrit et d'où RESTORE DATABASE lit ; créé s'il n'existe pas. Mêmes règles que --secure-file-priv, dont il doit être disjoint : un compte FILE lirait sinon les sauvegardes par LOAD_FILE et en déposerait une forgée par INTO DUMPFILE (voir 15.7 et chapitre 18).
--tls-cert <fichier.pem> / --tls-key <fichier.pem>absentCertificat et clé privée TLS. Dès qu'ils sont fournis, le serveur exige TLS : un client qui ne chiffre pas est refusé (erreur 3159).
--allow-plain-with-tlsdésactivéAvec un certificat, accepte aussi les clients non chiffrés : TLS seulement proposé (ancien comportement).
--require-tlsdésactivéExige TLS en toutes circonstances, même avec --allow-plain-with-tls (suppose --tls-cert / --tls-key).
--change-events-buffer-events <n>100000Événements de changement gardés en mémoire pour la reprise d'un abonné (chapitre 20 ; éditions Entreprise et Cluster, sans effet en Express).
--change-events-buffer-size <octets>67108864 (64 Mio)Taille maximale de ces événements.
--change-events-bulk-rows <n>1000Lignes d'une table modifiées par une transaction au-delà desquelles un seul événement BULK est publié.
--change-events-max-payload <octets>8000Taille maximale de la charge d'un NOTIFY.
--change-events-max-listeners <n>64Abonnements LISTEN par session.
--change-events-wait-timeout <secondes>30Attente de WAIT FOR CHANGES sans TIMEOUT (fraction permise).
--change-events-retention <secondes>600Durée pendant laquelle une table reste suivie après la déconnexion de son dernier abonné ; 0 : arrêt immédiat. Sur un cluster, ces réglages sont propres à chaque nœud (voir 16.11).
--cluster-config <cluster.toml>cluster.toml à côté de l'exécutableÉdition Cluster : configuration du nœud dans une réplication multi-nœud (voir 15.1.4).
--mcp ON|OFF et --mcp-*OFFPoint d'accès MCP pour les assistants IA (éditions Entreprise, Cluster et Developer) : voir chapitre 19.
--rest ON|OFFOFFPoint d'accès REST (HTTP/HTTPS, JSON), toutes éditions : voir chapitre 23.
--rest-port <port> / --rest-bind <adresse>7009 / 127.0.0.1Port et adresse du point d'accès REST ; hors de la boucle locale, TLS obligatoire (--tls-cert, --tls-key).
--rest-endpoints ON|OFF / --rest-tables ON|OFF / --rest-sql ON|OFFON / OFF / OFFFamilles d'accès REST ouvertes sur le serveur : endpoints écrits en SQL (CREATE ENDPOINT), lignes des tables, SQL libre.
--rest-basic ON|OFF / --rest-basic-access <familles>ON / ENDPOINTSConnexion REST par compte et mot de passe (HTTPS ou boucle locale) et familles qu'elle permet.
--rest-idle-timeout, --rest-statement-timeout, --rest-max-rows, --rest-cors-origins60, 30, 1000, videDélai d'une connexion HTTP sans requête, durée maximale d'une instruction, lignes par réponse, origines CORS admises.
--journal-gap-replaydésactivéReprise forcée, le temps d'un démarrage : dans une base dont le journal est abîmé au milieu, les enregistrements intacts qui suivent la zone perdue sont aussi rejoués (voir 15.4.4). À n'utiliser qu'en dernier recours.
--reset-accounts—Recrée le coffre des comptes avec le seul root local et un nouveau mot de passe (voir 15.5.3). Utilisable avec --root, --key-dir et --initial-root-password.
--help / -h / /?—Affiche l'usage et quitte.

Le point d'accès MCP et le point d'accès REST ont chacun leurs options (--mcp-*, --rest-*) ; les tableaux détaillés sont aux chapitres 19 et 23. Les options de journal d'archive (sauvegarde incrémentielle continue) se règlent par miraj_config.xml et SET GLOBAL, pas par la ligne de commande (voir 18.1.5).

Remarque de terminologie : le nom des options en ligne de commande utilise des tirets (--long-query-time), celui des clés XML et des variables de session des soulignés (long_query_time) ; ce chapitre respecte cette convention à chaque endroit.

15.1.4 Réplication (édition Cluster)#

L'édition Cluster ajoute la réplication multi-nœud : un nœud primaire écrit, des nœuds secondaires reçoivent son journal en continu et le rejouent en lecture seule. Les clients se connectent à un secondaire sur son port client habituel (7007 par défaut) ; toute tentative d'écriture y échoue en erreur

  1. Les nœuds communiquent entre eux sur un port distinct, en TLS mutuel (certificat signé par l'autorité du cluster). La configuration se fait par --cluster-config :
[node]
id = "n1"                    # identifiant stable du nœud
listen = "0.0.0.0:7107"      # port entre nœuds (pas celui des clients)
advertise = "10.0.0.1:7107"  # adresse annoncée aux autres nœuds

[cluster]
name = "gestium-prod"
seeds = ["10.0.0.1:7107", "10.0.0.2:7107"]
tls_cert = "node.pem"        # chemins relatifs au fichier
tls_key = "node-key.pem"
tls_ca = "cluster-ca.pem"
ack = "written"              # ou "durable" : le secondaire vide son
                              # journal sur disque avant d'accuser
journal_retention_mb = 1024  # au-delà, un secondaire absent est
                              # réamorcé à son retour

Au premier démarrage, tous les nœuds sont secondaires. Le rôle primaire se désigne une fois par :

SET GLOBAL cluster_role = 'primary';

les autres nœuds rejoignent alors le primaire (copie des bases puis flux du journal). La promotion d'un secondaire utilise la même commande ; un ancien primaire relancé après une bascule est mis à l'écart (@@cluster_role = 'FENCED', lecture seule) jusqu'à ce qu'on le fasse rejoindre le nouveau primaire :

SET GLOBAL cluster_role = 'secondary';

ses écritures non répliquées sont alors mises de côté dans <root>\miraj\quarantaine. Suivi de la réplication :

SHOW CLUSTER STATUS;
SELECT * FROM information_schema.MIRAJ_NODES;
SELECT * FROM information_schema.MIRAJ_REPLICATION;
SELECT @@read_only, @@cluster_role, @@cluster_epoch, @@cluster_primary;

15.2 Fichier de configuration miraj_config.xml#

MIRAJ accepte une configuration par fichier XML, sur le modèle d'un fichier .cnf/.ini classique. Trois niveaux se combinent, dans cet ordre de priorité croissante :

  1. valeur par défaut interne ;
  2. valeur du fichier miraj_config.xml ;
  3. argument de la ligne de commande (priorité la plus forte).

Le fichier lu est, dans l'ordre : le chemin donné par --config ; miraj_config.xml dans le dossier de miraj-server.exe, s'il y existe ; sinon <root>\miraj_config.xml (créé avec un squelette commenté s'il manque). Le chemin retenu est écrit au démarrage (Configuration : …). Une variable absente ou en commentaire garde sa valeur par défaut. Extrait :

<?xml version="1.0" encoding="UTF-8"?>
<miraj>
    <!-- Langue des messages d'erreur des sessions (en, fr, zh, hi, es, ar, pt, ru, de, ja) -->
    <!-- <language>en</language> -->
    <!-- Politique de sauvegarde (relaxed, statement, periodic) -->
    <!-- <save_policy>relaxed</save_policy> -->
    <!-- Adresse d'écoute du serveur -->
    <!-- <bind>127.0.0.1</bind> -->
    <!-- Port TCP d'écoute du serveur -->
    <!-- <port>7007</port> -->
    <!-- Fichier <dossier>/miraj/server.log tenu à jour -->
    <!-- <log>false</log> -->
</miraj>

Pour appliquer une valeur, décommentez la ligne et changez la valeur ; le reste du fichier (les autres variables en commentaire) n'a pas besoin d'être modifié.

Un second fichier, miraj_default.xml, est régénéré à chaque démarrage du serveur : il liste toutes les variables reconnues avec leur valeur par défaut courante. C'est un fichier de référence, jamais relu par le serveur — ne pas y écrire ses propres réglages, ils seraient effacés au prochain démarrage. Utilisez-le pour retrouver, version après version, la liste complète des clés disponibles et leur valeur par défaut.

Les clés du fichier XML recouvrent les mêmes réglages que les options de ligne de commande : language, save_policy, save_interval, checkpoint_interval, checkpoint_large_table, lock_wait_timeout, sql_mode, deferred_update, concurrency, key_dir, lob_threshold, lob_cache, event_scheduler, parallel_threads, slow_query_log, slow_query_log_file, long_query_time, max_statement_time, group_concat_max_len, secure_file_priv, backup_dir, require_password, bind, port, log, result_buffer, write_timeout, connect_timeout, idle_timeout, max_allowed_packet, max_connections, max_connect_errors, initial_root_password, tls_cert, tls_key, require_tls, allow_plain_with_tls, cluster_config, proxy_protocol_from, journal_archive, journal_archive_dir, journal_archive_max_lag (voir 18.1.5), change_events_buffer_events, change_events_buffer_size, change_events_bulk_rows, change_events_max_payload, change_events_max_listeners, change_events_wait_timeout, change_events_retention, mcp, mcp_port, mcp_bind, mcp_idle_timeout, mcp_statement_timeout, mcp_max_rows, rest, rest_port, rest_bind, rest_idle_timeout, rest_statement_timeout, rest_max_rows, rest_cors_origins, rest_endpoints, rest_tables, rest_sql, rest_basic, rest_basic_access. Seuls --lazy-databases, --journal-gap-replay, --reset-accounts et --config n'ont pas de clé XML (journal_archive* n'ont pas d'option de ligne de commande). La liste exacte, avec la valeur par défaut de chacune, est celle de miraj_default.xml.

initial_root_password n'est lu qu'à la création du coffre des comptes : retirez-le du fichier une fois le serveur démarré, il y est en clair.

checkpoint_interval (300000 ms, soit 5 min, par défaut) n'a pas d'équivalent en ligne de commande : c'est l'intervalle entre deux points de sauvegarde (voir 15.4.2), réglable uniquement par ce fichier. Il en va de même pour checkpoint_large_table (64 Mo par défaut), la taille à partir de laquelle une table en mémoire n'est plus réécrite à chaque point de sauvegarde périodique (voir 15.4.2).

Usage recommandé : le fichier XML convient à un réglage permanent, propre à une instance (un dossier de données), qu'on ne veut pas répéter à chaque lancement dans un script ou un service Windows ; la ligne de commande reste utile pour un réglage ponctuel (diagnostic, script de test) qui doit l'emporter sur le fichier.

15.3 Organisation du dossier de données#

<dossier racine>/
  miraj_config.xml       configuration (si présente)
  miraj_default.xml      référence des valeurs par défaut, régénéré au démarrage
  miraj.lck              verrou du dossier : un seul processus à la fois
  <base>/
    <table>.mrj          une table = un fichier (format MIRA v2)
    <table>.bmrj         magasin des BLOB longs de la table (créé au premier dépôt)
    <table>.dmrj         une table disque (ENGINE = Aria / MyISAM / DISK) : lignes par pages
    <table>.dbmrj        grosses valeurs d'une table disque (créé au premier dépôt)
    <table>.dmrj.ckpt    point de sauvegarde en cours d'une table disque (temporaire)
    doublewrite.mrw      double écriture des pages des tables disque (vidée après usage)
    journal.mrl          journal d'écriture (WAL) de la base
    sequences.mrq        séquences de la base (définition et état), si elle en a
    views.mrv, routines.mrp, triggers.mrt, events.mre
                         vues, procédures et fonctions, déclencheurs, événements
    replica.mrs          état de réplication du nœud (édition Cluster)
  miraj/
    accounts.mra         coffre chiffré des comptes
    server.log           journal serveur, si --log est actif
    slow.log             journal des requêtes lentes, si activé
    quarantaine/          écritures non répliquées d'un nœud FENCED (édition Cluster)

Points pratiques pour l'administration :

  • Chaque base est un sous-dossier du dossier racine ; chaque table un fichier .mrj dans ce sous-dossier. Créer ou supprimer une base revient à créer ou supprimer ce sous-dossier (fait par le serveur lui-même via CREATE DATABASE / DROP DATABASE, jamais à la main pendant que le serveur tourne).
  • Un BLOB qui dépasse --lob-threshold (8192 octets par défaut) est déposé dans le fichier .bmrj de sa table plutôt que dans le .mrj ; ce fichier n'apparaît qu'après le premier dépôt d'un BLOB de cette taille. Les deux fichiers doivent être copiés ensemble : un .mrj sans son .bmrj associé perd ses valeurs BLOB déportées. Après un abaissement de --lob-threshold, les valeurs déjà stockées qui atteignent le nouveau seuil rejoignent le .bmrj à l'ouverture de la base (la table est réécrite au point de sauvegarde suivant). Sur le primaire d'un cluster, ce déplacement passe par le journal, dans un plafond de 256 Mio par table (voir 16.7) : au-delà, les valeurs restent dans la table, lisibles, et une ligne AVERTISSEMENT est écrite dans le journal du serveur.
  • Une table disque est un fichier .dmrj au lieu du .mrj (voir Moteurs de stockage) : ses lignes y restent, seuls ses index sont chargés en mémoire, avec un cache de pages de 256 Mo (non réglable pour l'instant). Ses valeurs de plus d'un quart de page vont dans <table>.dbmrj ; comme pour le .bmrj, les deux fichiers vont ensemble. Au point de sauvegarde, les pages modifiées sont d'abord écrites dans <table>.dmrj.ckpt, puis à leur place : un .ckpt complet trouvé à l'ouverture est rejoué, un .ckpt incomplet est ignoré. Ne supprimez pas ce fichier à la main. Une page écrite hors d'un point de sauvegarde passe d'abord par doublewrite.mrw, un fichier par base : à l'ouverture, toute page déchirée par une coupure est restaurée d'après lui, avant la lecture des tables (constat dans les notes de reprise). Une page abîmée sans copie se signale par CHECK TABLE et se répare par REPAIR TABLE (voir Maintenance des tables).
  • journal.mrl est le journal d'écriture de la base : il permet de rejouer les écritures postérieures au dernier point de sauvegarde de chaque table en cas d'arrêt brutal (voir 15.4). Ne jamais le supprimer ou le modifier à la main.
  • Le coffre des comptes miraj/accounts.mra est propre au dossier de données ; sa clé de déchiffrement est rangée ailleurs (voir 15.5.1). Copier un dossier de données sans sa clé rend le coffre illisible au redémarrage.
  • Sauvegarde : à l'arrêt du serveur (voir 15.1.2 et 15.7), une copie simple du dossier racine (bases, miraj/, fichiers de configuration) suffit à en obtenir une image cohérente, à condition de copier aussi la clé du coffre des comptes si elle est nécessaire à la restauration (voir 15.5.1 et 11.7).

15.4 Durabilité et reprise après incident#

15.4.1 Politiques de sauvegarde (save_policy)#

MIRAJ écrit chaque instruction de modification dans le journal (WAL) de la base concernée avant de considérer l'instruction terminée ; la façon dont ce journal atteint le disque dépend de la politique choisie :

PolitiqueComportementPerte maximale tolérée en cas d'arrêt brutal du processusPerte maximale tolérée en cas de coupure de la machine
relaxed (par défaut)Le journal est écrit dans son fichier avant que l'instruction ne rende la main, sans attendre le disque ; un fil de fond le vide sur disque toutes les --save-interval ms (5000 par défaut).Aucune (le fichier a déjà reçu l'écriture du système d'exploitation).Les écritures du dernier intervalle --save-interval.
statementComme relaxed, en attendant en plus le vidage sur disque à la fin de chaque instruction d'écriture.Aucune.Aucune.
periodicRien n'est écrit avant le passage du fil de fond (toutes les --save-interval ms).Les écritures des dernières --save-interval ms.Les écritures des dernières --save-interval ms.

Ces trois politiques peuvent aussi être choisies par session, sans changer le réglage du serveur :

SET SESSION save_policy = 'statement';

(une quatrième valeur, manual, existe en interne mais est refusée en politique de serveur : elle n'écrirait jamais rien tant que la session ne l'ordonne pas, ce qui n'a pas de sens comme politique par défaut).

Comment choisir :

  • statement maximise la sécurité (aucune perte possible, y compris lors d'une coupure de courant) au prix d'une latence d'écriture par instruction liée au disque — à retenir pour des données qu'aucune perte, même minime, ne peut affecter.
  • relaxed (le réglage par défaut) offre un bon compromis : aucune perte en cas d'arrêt du seul processus serveur (le plus fréquent en pratique), et une fenêtre de perte bornée par --save-interval seulement en cas de coupure de la machine elle-même.
  • periodic réduit encore la latence d'écriture en échange d'une fenêtre de perte identique dans les deux cas (processus ou machine) — à réserver à des charges où le débit d'écriture prime sur la garantie de durabilité stricte.

Dans tous les cas, le journal lui-même reste cohérent : ce qui peut être perdu, ce sont les toutes dernières écritures non encore vidées, jamais l'intégrité du fichier.

15.4.2 Points de sauvegarde (checkpoints)#

Un point de sauvegarde consolide l'état courant des tables et permet de tronquer le journal d'écriture d'autant. Il se déclenche automatiquement :

  • toutes les checkpoint_interval millisecondes (300 000 ms, soit 5 min, par défaut — réglable uniquement via miraj_config.xml, voir 15.2) ;
  • quand le journal dépasse 64 Mo ;
  • à la fermeture propre d'une instance embarquée (Miraj::drop).

Une table en mémoire est écrite en entier dans son fichier .mrj. Pour ne pas réécrire toutes les 5 min une grosse table dont quelques lignes seulement ont changé, le point de sauvegarde périodique saute les tables en mémoire d'au moins checkpoint_large_table mégaoctets (64 par défaut) : leurs modifications restent dans le journal, qui les protège exactement comme avant. Elles sont écrites au point de sauvegarde complet (journal de 64 Mo atteint, SHUTDOWN, fermeture propre de l'instance), ou par une instruction qui a besoin de leur fichier à jour (RENAME TABLE, REPAIR TABLE…). Une sauvegarde à chaud (BACKUP) copie le journal avec elles.

Petites tablesTables d'au moins checkpoint_large_table Mo
Point de sauvegarde périodiqueécritesreportées (le journal les protège)
Point de sauvegarde completécritesécrites

Le journal est alors raccourci jusqu'au plus ancien fichier qui en a encore besoin, et seulement si la rotation retire au moins autant qu'elle recopie. La sécurité en cas de coupure ne change pas, elle dépend de save_policy (voir 15.4.1). En contrepartie, la reprise après un arrêt brutal peut rejouer jusqu'à 64 Mo de journal au lieu d'une minute d'écritures. Les tables disque ne sont jamais reportées : leur point de sauvegarde n'écrit que les pages modifiées. checkpoint_large_table à 0 écrit toutes les tables à chaque point de sauvegarde, comme avant.

Un vidage du journal ou un point de sauvegarde qui échoue en arrière-plan (disque plein, fichier tenu par un antivirus, droits retirés) est retenté à chaque cycle et consigné dans le journal du serveur, une fois par base et par cause, sans répétition à chaque cycle : SAUVEGARDE : point de sauvegarde de la base … impossible : …, puis SAUVEGARDE : … rétabli quand il réussit de nouveau. Tant que l'échec dure, les modifications restent protégées par le journal, qui grossit.

15.4.3 Lignes supprimées et OPTIMIZE TABLE#

Un DELETE marque les lignes comme supprimées sans les retirer tout de suite. Elles sont retirées physiquement (compactage) :

  • pendant les écritures, dès qu'elles atteignent 20 % des lignes de la table ;
  • à chaque point de sauvegarde (voir 15.4.2), quel qu'en soit le nombre.

Le magasin de BLOB (.bmrj) n'est compacté automatiquement qu'au-delà de 64 Mo, quand au moins la moitié de son contenu est devenue orpheline.

OPTIMIZE TABLE t [, …] fait les deux tout de suite, sans condition de seuil : lignes supprimées retirées, fichier .mrj réécrit, magasin de BLOB compacté s'il contient des octets orphelins. On s'en sert après une grosse purge, ou pour choisir le moment de la réécriture (maintenance de nuit).

OPTIMIZE TABLE ventes, ventes_lignes;

Le résultat compte une ou plusieurs lignes par table (Table, Op, Msg_type, Msg_text) :

  • info : nombre de lignes supprimées retirées, magasin de BLOB compacté ;
  • dernière ligne status : OK, Table is already up to date (rien à retirer) ou Operation failed (table inconnue, vue, ou table tenue par une transaction ouverte ou un LOCK TABLES : le compactage est alors reporté au prochain point de sauvegarde où la table sera libre).

L'instruction valide la transaction ouverte de la session et prend le catalogue en écriture : les autres instructions attendent la fin de la réécriture. Il faut les privilèges SELECT et INSERT sur chaque table. Avec la politique manual, les lignes sont retirées en mémoire et le fichier n'est écrit qu'à la sauvegarde explicite. Dans l'édition Cluster, le compactage fait sur le primaire est journalisé et rejoué par les secondaires ; sur un secondaire, OPTIMIZE TABLE ne fait rien (note). Options acceptées sans effet : NO_WRITE_TO_BINLOG, LOCAL, WAIT n, NOWAIT.

15.4.4 Reprise après un arrêt brutal#

Au démarrage, le serveur ouvre les fichiers de table de chaque base puis rejoue les enregistrements du journal postérieurs au LSN (numéro de séquence du journal) déjà présent dans chaque fichier .mrj. Les écritures de fichiers sont atomiques (fichier temporaire, vidage, puis remplacement — sous Windows via MoveFileExW avec MOVEFILE_WRITE_THROUGH), et le magasin de BLOB (.bmrj) est toujours vidé sur disque avant le journal et le fichier de table qui le référencent : un arrêt brutal peut au pire laisser des octets de BLOB orphelins dans le .bmrj, jamais une référence sans contenu. D'après le dossier technique du projet, ce mécanisme a été validé par 120 arrêts brutaux aléatoires du processus pendant des écritures, sans incohérence observée.

Fichiers abîmés trouvés à l'ouverture : avant toute réparation, les fichiers concernés sont recopiés dans <root>\miraj\quarantaine\<horodatage>\<base>\ avec un rapport.txt, et chaque constat est consigné au démarrage (lignes RÉCUPÉRATION : … du journal du serveur).

  • Fin du journal : une écriture interrompue (dernier enregistrement incomplet, suite de zéros) est retirée sans bruit, c'est l'effet normal d'un arrêt brutal. Une fin illisible plus longue qu'un enregistrement, sans rien de valide après (usure du support plutôt qu'écriture interrompue), est recopiée en quarantaine puis retirée, avec un constat.
  • Zone abîmée au milieu du journal : seuls les enregistrements qui la précèdent sont rejoués ; ceux qui la suivent restent dans la copie en quarantaine. --journal-gap-replay les rejoue aussi, le temps d'un démarrage : ils peuvent dépendre des modifications perdues, et une table dont un enregistrement ne s'applique pas garde ce qui l'a été avant lui (constat reprise forcée : …). Les tables ainsi complétées sont écrites aussitôt.
  • Fichiers .mrj et .dmrj d'une même table (conversion de moteur interrompue) : le plus récent est gardé, un .mrj au contenu invalide perd ; l'autre est recopié en quarantaine avant d'être retiré. Si l'un des deux ne peut pas être lu (fichier tenu par un antivirus), rien n'est supprimé et la table reste illisible jusqu'au démarrage suivant.
  • Base système miraj illisible (journal tenu par un antivirus, disque en erreur) : son ouverture est retentée pendant quelques secondes, puis le serveur démarre sans elle. Elle est marquée indisponible (répertoire intact), les autres bases sont servies, et seules les connexions locales sont acceptées : un hôte distant reçoit l'erreur 9048, sur le port principal comme sur les points d'accès REST et MCP. Le journal du serveur l'annonce (BASE SYSTÈME INDISPONIBLE (…)) ; corriger la cause puis redémarrer le serveur.

Comme rappelé en 11.1.2, miraj-server ne dispose pas d'un arrêt propre : c'est précisément ce mécanisme de reprise, et non une séquence d'extinction, qui garantit l'intégrité des données à chaque redémarrage.

15.5 Sécurité opérationnelle#

15.5.1 Coffre des comptes et gestion de la clé#

Les comptes (utilisateurs, mots de passe, privilèges, rôles) sont gardés dans un coffre chiffré et authentifié par AES-256-GCM, <root>\miraj\accounts.mra — jamais journalisé, écrit de façon atomique, protégé contre la restauration d'une copie ancienne par un numéro de génération croissant.

La clé de 32 octets qui protège ce coffre est rangée hors du dossier de données :

  • sous Windows, elle est chiffrée par DPAPI pour la machine (lisible par n'importe quel compte de cette machine, y compris SYSTEM au démarrage sans session ouverte), dans %LOCALAPPDATA%\MIRAJ\keys ;
  • sur les autres systèmes, dans un fichier aux droits 0600 ;
  • --key-dir <dossier> (ou la clé XML key_dir) permet de choisir un autre emplacement, par exemple pour partager la clé entre plusieurs services ou la ranger sur un support distinct du dossier de données.

Conséquence pour la sauvegarde et le déploiement : copier uniquement le dossier de données ne suffit pas à pouvoir rouvrir les comptes sur une autre machine ou sous un autre compte Windows ; il faut aussi transférer la clé (ou son dossier --key-dir), en la protégeant au moins aussi bien qu'un mot de passe administrateur.

15.5.2 Mot de passe initial, connexions et écoute réseau#

Mot de passe initial de root. Au premier démarrage de miraj-server sur un dossier, le coffre des comptes est créé avec le seul compte root local, qui reçoit un mot de passe de 24 caractères tiré au sort par le générateur du système. Il est affiché une seule fois sur la console, avec la commande pour le changer, et n'est écrit nulle part (ni dans server.log, ni dans le coffre autrement que sous forme de vérificateur) :

Compte root@localhost créé avec le mot de passe provisoire : Xq7mPt3hKc9WbZ2rNf8sLd4v

Pour une installation automatisée, fixez-le vous-même par --initial-root-password, par la variable d'environnement MIRAJ_INITIAL_ROOT_PASSWORD (préférable : la ligne de commande d'un processus est visible des autres utilisateurs) ou par la clé initial_root_password du fichier de configuration ; il n'est alors pas affiché. Une valeur vide passée par la ligne de commande ou par la variable est refusée (le serveur ne démarre pas). À la création du coffre, server.log consigne d'où vient le mot de passe (ligne de commande, variable, fichier de configuration ou tirage), jamais le mot de passe lui-même. Ces réglages sont ignorés, avec une note, dès que le coffre existe, sauf si le root local y est resté sans mot de passe (coffre écrit par le moteur embarqué, miraj-cli par exemple, avant le premier démarrage du serveur) : il reçoit alors le mot de passe initial. Un root local sans mot de passe est signalé par un avertissement à chaque démarrage. Dans un cluster, seul le primaire crée les comptes : un secondaire qui démarre sur un dossier vierge sans mot de passe initial crée un coffre d'attente dont le root est inaccessible, puis reçoit les comptes du primaire. Remplacez le mot de passe provisoire dès la première connexion :

ALTER USER 'root'@'localhost' IDENTIFIED BY 'un mot de passe robuste';

Écoute réseau. Le serveur écoute par défaut sur la boucle locale (127.0.0.1). Choisir une adresse d'écoute non locale (--bind) sans TLS déclenche un avertissement, et le serveur signale les comptes sans mot de passe qui deviendraient alors joignables à distance. Tant que require_password est actif (défaut), un tel compte ne peut d'ailleurs pas être créé (erreur 9033, voir 14.2).

Nombre de connexions. Au plus --max-connections connexions (151 par défaut) sont servies à la fois ; chacune prend sa place avant que sa session ne soit créée. Au-delà, le client reçoit l'erreur 1040 (« Too many connections ») à la place de la poignée de main, et le refus est signalé au plus une fois par minute dans le journal. Une poignée de main doit s'achever en --connect-timeout secondes, TLS comprise, comptées dès l'acceptation.

Échecs d'authentification. Chaque réponse fausse (erreur 1045) est comptée pour l'adresse du client : la réponse est retardée de 100 ms au premier échec, du double à chaque échec suivant, 5 s au plus. Au bout de --max-connect-errors échecs consécutifs (100 par défaut), l'adresse est bloquée 15 minutes, délai relancé par chaque nouvelle tentative : ses connexions sont refusées dès l'acceptation par l'erreur 1129, même avec le bon mot de passe. Une authentification réussie remet le compte de l'adresse à zéro. Blocages et refus répétés sont inscrits dans le journal (SÉCURITÉ : …). Pour débloquer toutes les adresses :

FLUSH HOSTS;   -- privilège RELOAD

Les adresses sans échec depuis une heure sont oubliées, et au plus 10 000 adresses sont suivies (la plus anciennement active est évincée au-delà). Attention : tous les clients d'une même machine partagent son adresse, y compris la boucle locale ; un script qui se trompe de mot de passe en boucle peut bloquer les autres clients de sa machine.

Identité avant authentification. Tant qu'une connexion n'a pas achevé son authentification, elle n'appartient à aucun compte : SHOW PROCESSLIST la montre en unauthenticated user, aux seuls comptes qui voient toutes les sessions (PROCESS), seuls à pouvoir l'arrêter par KILL.

15.5.3 --reset-accounts : procédure de secours#

Un coffre des comptes altéré, supprimé ou dont la clé est absente/illisible empêche le démarrage du serveur. La procédure de secours locale est :

miraj-server.exe --root D:\donnees --reset-accounts [--key-dir <dossier>]

Cette commande recrée le coffre avec pour seul compte le root local, puis se termine (elle ne démarre pas le serveur réseau). root reçoit un nouveau mot de passe tiré au sort, affiché une seule fois sur la console comme au premier démarrage, ou celui de --initial-root-password (ou de MIRAJ_INITIAL_ROOT_PASSWORD), qui n'est pas affiché. Elle doit être exécutée localement, avec un accès au dossier de données et, implicitement, un accès équivalent à celui de la clé. Après un --reset-accounts :

  • tous les comptes et privilèges précédemment définis sont perdus (seul root local subsiste) ; recréez les comptes applicatifs et redonnez les privilèges nécessaires ;
  • remplacez sans attendre le mot de passe provisoire de root (voir la commande ALTER USER ci-dessus).

N'utilisez cette procédure qu'en dernier recours (coffre corrompu, clé perdue, compte administrateur bloqué sans autre accès) : elle ne restaure rien, elle repart d'un état vierge de comptes.

15.5.4 Recommandations pour un déploiement en production#

  • Conservez l'écoute sur la boucle locale (127.0.0.1, valeur par défaut) tant qu'un pare-feu applicatif ne protège pas un accès plus large ; ne choisissez une adresse non locale qu'après avoir sécurisé tous les comptes par mot de passe.
  • Activez TLS (--tls-cert / --tls-key) dès que les clients se connectent au travers d'un réseau qui n'est pas physiquement isolé : il est alors exigé de tous les clients. N'utilisez --allow-plain-with-tls que le temps de migrer des clients qui ne savent pas encore chiffrer.
  • Ajustez --max-connections au nombre de clients attendus et laissez --max-connect-errors à une valeur basse : un essai de mots de passe est ralenti puis bloqué.
  • Séparez la clé du coffre des comptes (--key-dir) du support de sauvegarde du dossier de données, ou documentez clairement qu'elle doit être copiée à part lors d'une restauration.
  • Laissez secure_file_priv vide (valeur par défaut, LOAD_FILE désactivé) sauf besoin explicite ; si vous l'activez, pointez-le vers un dossier dédié, distinct du dossier de données, du dossier des clés, de celui de la clé TLS et du dossier des sauvegardes (le serveur refuse de démarrer sinon).
  • Démarrez systématiquement avec --log (voir 15.6) pour conserver une trace des erreurs SQL et des incidents internes.
  • Gardez à l'esprit l'absence d'arrêt propre (15.1.2) : prévoyez la politique de sauvegarde (15.4.1) en fonction de la perte de données que vous pouvez tolérer en cas de coupure, plutôt que de compter sur une extinction soignée du service.

15.6 Supervision#

15.6.1 SHOW STATUS et SHOW VARIABLES#

SHOW VARIABLES;              -- réglages courants de la session/du serveur
SHOW VARIABLES LIKE 'save_%';
SHOW STATUS;                 -- compteurs d'activité
SHOW STATUS LIKE 'Miraj_%';

SHOW VARIABLES restitue les mêmes valeurs que SELECT @@nom pour chaque variable ; c'est ce que lisent à la connexion les outils clients courants (consoles graphiques, connecteurs).

SHOW STATUS expose notamment, à ce jour :

VariableSignification
Miraj_parallel_fair_shareFils qu'une requête peut prendre en ce moment : le budget divisé par le nombre d'instructions en cours (voir 15.8).
Miraj_parallel_queriesNombre de requêtes ayant utilisé l'exécution parallèle (éditions Entreprise et Cluster).
Miraj_parallel_queries_downgradedRequêtes parallèles dont le nombre de fils a été réduit à leur part équitable.
Miraj_parallel_queries_serializedRequêtes retombées en exécution série faute de fils disponibles.
Miraj_parallel_queries_serialized_loadRequêtes parallélisables exécutées en série parce que le serveur avait autant d'instructions en cours que de fils.
Miraj_parallel_queries_serialized_volumeAgrégations poursuivies en série après leur premier morceau (presque un groupe par ligne).
Miraj_parallel_statements_activeInstructions en cours d'exécution (hors attentes : SLEEP, WAIT FOR CHANGES, verrous de table).
Miraj_parallel_workersFils du budget de parallélisme configuré.
Miraj_parallel_workers_busyFils actuellement occupés.
QuestionsInstructions exécutées depuis le démarrage, toutes sessions confondues, réussies ou non, hors erreurs de syntaxe (une par instruction d'un lot ; celles des procédures et des déclencheurs ne comptent pas).
Slow_queriesNombre de requêtes consignées dans le journal des requêtes lentes.
UptimeSecondes écoulées depuis le démarrage du serveur.
Max_statement_time_exceededNombre d'instructions abandonnées pour dépassement de max_statement_time.
Threads_connectedConnexions ouvertes (1 pour une session embarquée).
Max_used_connectionsPlus grand nombre de connexions simultanées depuis le démarrage.
Connection_errors_max_connectionsConnexions refusées faute de place (max_connections, erreur 1040).
Aborted_connectsConnexions fermées avant l'authentification : échec d'authentification, poignée de main illisible ou trop lente, adresse bloquée.
Miraj_journal_archive, Miraj_journal_archive_databases, _bases, _bases_pending, _broken, _bytes, _lag_bytes, _last_timeArchivage continu du journal (18.1.5) : état, bases suivies, sauvegardes complètes prises par le serveur ou en attente, bases dont la lignée est rompue, octets archivés, retard sur le journal, heure du dernier segment.
Miraj_cluster_fragment_*, Miraj_cluster_sync_*, Miraj_cluster_unlogged_rebootstrapsÉdition Cluster : lectures et écritures de partitions tenues par un autre nœud, attentes de l'accusé des secondaires synchrones, réamorçages (chapitre 16).
Miraj_change_events_published, _buffered, _buffer_bytes, _evicted, _resyncs, _bulk, _listeners, _waits, _last_seqÉvénements de changement : publiés, gardés en mémoire (nombre et octets), évincés, RESYNC rendus, BULK, abonnements actifs, WAIT FOR CHANGES exécutés, dernier numéro publié (chapitre 20). Compteurs propres à chaque nœud d'un cluster.

Cette liste s'enrichira au fil des versions ; consultez SHOW STATUS sans filtre pour la liste complète de l'instance en service.

15.6.2 information_schema#

information_schema (lecture seule) donne une vue structurée utile à l'administration, notamment : SCHEMATA, TABLES, COLUMNS, STATISTICS, VIEWS, TABLE_CONSTRAINTS, KEY_COLUMN_USAGE, REFERENTIAL_CONSTRAINTS, ainsi que les tables de privilèges USER_PRIVILEGES, SCHEMA_PRIVILEGES, TABLE_PRIVILEGES, APPLICABLE_ROLES, ENABLED_ROLES. Un compte ne voit, dans SHOW DATABASES, SHOW TABLES et information_schema, que ce qu'il a le privilège d'atteindre.

En édition Cluster, information_schema.MIRAJ_NODES et information_schema.MIRAJ_REPLICATION complètent SHOW CLUSTER STATUS pour le suivi de la réplication (voir 15.1.4).

Autres vues de supervision : PROCESSLIST (comme SHOW PROCESSLIST), INNODB_TRX (transactions ouvertes), PARTITIONS, ENDPOINTS et ENDPOINT_PRIVILEGES (API REST, chapitre 23), ENGINES, COLLATIONS. MIRAJ_PARALLELISM donne une ligne : édition, fils du budget et fils occupés, taille des morceaux (MORSEL_ROWS), requêtes parallèles, en série ou réduites et part équitable, les mêmes chiffres que les compteurs Miraj_parallel_*.

Tables propres à MIRAJ pour la concurrence : MIRAJ_CONCURRENCY (modèle en service, transactions actives, filigrane de purge, transaction la plus ancienne et compteurs de la mise à jour différée), MIRAJ_VERSIONS (versions en attente, par table) et MIRAJ_DEFERRED_UPDATE (mise à jour différée, une ligne de compteurs cumulés depuis le démarrage) :

SELECT INTENTS_RECORDED, ROWS_APPLIED, COMMIT_FAILURES, FAILED_READ_VALIDATION,
       NORMAL_KEY, NORMAL_TRIGGER
FROM information_schema.MIRAJ_DEFERRED_UPDATE;

INTENTS_RECORDED compte les UPDATE différés, ROWS_APPLIED les lignes écrites au COMMIT, IMAGES_MATERIALIZED les UPDATE différés rattrapés par une écriture ordinaire de la même transaction ; COMMIT_FAILURES les COMMIT refusés d'une transaction qui avait des UPDATE différés, détaillés par cause (FAILED_WITNESS : ligne changée de sorte que le WHERE ou une colonne lue par un déclencheur AFTER UPDATE ne vaut plus ; FAILED_ROW_GONE, FAILED_CONCURRENT_WRITER, FAILED_WARNING, FAILED_TABLE_REDEFINED, FAILED_ERROR, FAILED_READ_VALIDATION : la transaction avait lu une ligne modifiée depuis) ; ENGINE_RETRIES les instructions en auto-validation rejouées par le moteur après un conflit ; les colonnes NORMAL_* les UPDATE d'une session à ON qui n'ont pas pu être différés, par raison (SESSION, FORM, TABLE, TRIGGER, KEY, ROW_COUNT, ROW, CALCULATION). Un FAILED_READ_VALIDATION élevé signale des transactions qui lisent une ligne avant de la mettre à jour (voir 13.2).

15.6.3 Journalisation#

  • --log (ou log dans le fichier XML) tient à jour <root>\miraj\server.log : ce fichier ne reçoit, horodatées, que les erreurs SQL renvoyées aux clients (avec la commande en cause), les pannes internes (paniques capturées, jamais transmises à l'appelant) et les problèmes de fonctionnement du serveur. Les requêtes qui réussissent n'y apparaissent pas ; les connexions et les erreurs sont en revanche aussi affichées sur la sortie standard.
  • Les mots de passe et les clés de chiffrement ne sont jamais écrits en clair dans ces journaux : le texte d'une commande y est gardé avec IDENTIFIED BY '<secret>', PASSWORD('<secret>'), SET PASSWORD … = '<secret>', AES_ENCRYPT(x, '<secret>')… Il en va de même pour la colonne Info de SHOW PROCESSLIST et de information_schema.PROCESSLIST, visible des autres comptes. La console miraj-cli n'écrit pas ces commandes dans son historique sur disque (elles restent rappelables pendant la session).
  • --slow-query-log (ou slow_query_log / slow_query_log_file) consigne dans <root>\miraj\slow.log (ou le fichier choisi) chaque instruction plus longue que --long-query-time secondes, avec le volume envoyé au client (Bytes_sent). SET GLOBAL log_output = 'TABLE' bascule ce journal vers la table miraj.slow_log ; FLUSH SLOW LOGS rouvre le fichier après une rotation externe.
  • Au démarrage (--slow-query-log-file ou fichier XML), le chemin du journal lent est libre, absolu ou relatif au dossier de données. En cours d'exécution, SET GLOBAL slow_query_log_file = '…' est confiné : le chemin doit être relatif, sans .., lecteur ni :, et son dossier doit déjà exister dans le dossier de données (à défaut, dans le dossier du fichier fixé au démarrage par un chemin absolu) sans en sortir par un lien. Un lien symbolique, un dossier, un fichier du moteur (.mrj, .mrl…) ou le dossier des clés du coffre sont refusés. Tout refus renvoie l'erreur 1231 ; SHOW WARNINGS en donne le motif. Pour écrire le journal ailleurs, fixez le chemin au démarrage.

15.7 Sauvegarde et restauration#

Le serveur sauvegarde une base à chaud, sans l'arrêter. Démarrez-le avec un dossier de sauvegardes (--backup-dir, voir 15.1.3), puis :

BACKUP DATABASE gestion TO 'gestion-2026-09-24';
RESTORE DATABASE gestion_copie FROM 'gestion-2026-09-24';

La copie est cohérente, avec toutes les transactions validées jusqu'à un instant et aucune au-delà. Les écritures ne sont suspendues que quelques millisecondes. L'outil miraj-backup pilote ces instructions depuis une tâche planifiée, et miraj-dump exporte une base en script SQL portable, comptes compris. Tout est détaillé au chapitre 18.

Ces sauvegardes ne contiennent pas le coffre des comptes. Exportez les comptes avec miraj-dump --users, ou copiez à froid le sous-dossier miraj\ et la clé du coffre (voir ci-dessous). Leur manifeste est signé avec une clé dérivée de celle du coffre : un serveur qui a une autre clé restaure la sauvegarde avec un avertissement, un manifeste falsifié est refusé (voir 18.1.4).

Copie à froid du dossier complet, pour un changement de machine avec les comptes, par exemple :

  1. Arrêtez le serveur. Comme rappelé en 11.1.2, miraj-server n'offre pas de séquence d'arrêt propre : traitez chaque arrêt comme un arrêt brutal. C'est la reprise par journal (15.4.4) qui garantit un dossier cohérent au prochain démarrage.
  2. Copiez le dossier de données (--root) dans son intégralité : bases (.mrj, .bmrj, .dmrj et .dbmrj des tables disque, journal.mrl), sous-dossier miraj\ (comptes, journaux internes) et fichiers de configuration XML éventuels.
  3. Copiez séparément la clé du coffre des comptes si elle est nécessaire à la restauration sur une autre machine ou sous un autre compte Windows (voir 15.5.1) : elle n'est pas dans le dossier de données.
  4. Pour restaurer, replacez ce dossier (et la clé, si nécessaire) à l'emplacement attendu, puis redémarrez le serveur normalement.

Pour une base intégrée (usage via miraj.dll ou l'API Rust, hors serveur réseau), la fermeture normale de l'instance (Miraj::drop) écrit tout avant de rendre la main : une sauvegarde à froid après cette fermeture n'a pas à se soucier d'un arrêt brutal.

15.8 Performance : réglages à connaître#

Ce chapitre reste orienté administration ; pour le détail technique de l'exécution parallèle et ses limites, voir roadmap.md et docs/LIMITES.md.

L'exécution parallèle (éditions Entreprise et Cluster — l'édition Express, compilée sans la fonctionnalité Cargo parallel, exécute chaque requête sur le seul fil de sa session, et l'édition Developer limite le budget du serveur à un seul fil) répartit sur plusieurs fils les lectures volumineuses : filtres, projections, agrégation, tri, DISTINCT, jointures INNER/LEFT, et la lecture sous-jacente des UPDATE/DELETE/INSERT ... SELECT — avec, dans tous les cas, les mêmes résultats et le même ordre qu'en exécution série.

Deux réglages contrôlent ce parallélisme :

  • --parallel-threads <n> (serveur) : fixe le nombre total de fils que toutes les requêtes du serveur peuvent occuper ensemble. Par défaut, un fil par cœur de la machine. À réduire si le serveur partage la machine avec d'autres services, ou à ajuster si des mesures montrent une contention entre requêtes concurrentes.
  • max_parallel_degree (variable de session) : borne le degré de parallélisme d'une session donnée, dans la limite du budget serveur ci-dessus. Utile pour réserver l'essentiel des fils à un traitement interactif tout en laissant un traitement de fond (import, recalcul en masse) se dérouler avec un degré plus faible, ou l'inverse.

EXPLAIN affiche le degré de parallélisme retenu pour une requête donnée, ce qui permet de vérifier qu'un réglage a l'effet attendu avant de le généraliser. EXPLAIN ANALYZE (ou ANALYZE, voir 8.21) exécute la requête et rend en plus les lignes et les temps réels de chaque opérateur : le moyen de repérer l'étape coûteuse d'une requête lente consignée dans le journal des requêtes lentes. Sans envoyer les lignes au client, il exécute vraiment la requête, et ANALYZE UPDATE|DELETE modifie vraiment les lignes : à lancer avec la même prudence qu'une instruction ordinaire.

Bascule automatique entre mono-cœur et multi-cœur#

Les éditions Entreprise et Cluster basculent d'elles-mêmes entre exécution multi-cœur et exécution mono-cœur, sans réglage :

  • Selon la charge. Le serveur compte les instructions en cours d'exécution. Une requête ne reçoit que sa part équitable des fils : le budget divisé par ce nombre. Seule, elle prend tous les cœurs. À deux, elle en prend la moitié. Dès qu'il y a autant d'instructions en cours que de fils, chacune s'exécute sur le seul fil de sa session, comme en mono-cœur : aucun fil supplémentaire ne vient disputer les cœurs aux autres sessions. Une session en attente (SLEEP, WAIT FOR CHANGES, verrou de table d'une autre instruction ou d'un LOCK TABLES) ne compte pas. Les fils sont rendus dès la fin de la lecture, sans attendre que le client ait reçu tout le résultat.
  • Selon la requête. Une agrégation GROUP BY traite d'abord un morceau sur le fil de la session. Si elle y trouve presque un groupe par ligne (regroupement sur une clé), elle continue en série. Dans ce cas, fusionner les résultats partiels des fils coûterait plus que le parallélisme ne rapporte.
  • Selon la taille de la table. Une lecture répartie est découpée en environ 8 morceaux par fil, de 16 384 lignes au moins et de parallel_morsel_rows lignes au plus (131 072 par défaut). Sur une table d'un million de lignes, tous les cœurs reçoivent ainsi du travail, et le morceau d'essai d'un GROUP BY n'en retarde qu'une petite part.

Les compteurs Miraj_parallel_statements_active, Miraj_parallel_fair_share et Miraj_parallel_queries_serialized_load / _downgraded / _volume (voir 15.6) montrent ces décisions. EXPLAIN indique le degré que la requête demande. Le degré qu'elle obtient à l'exécution dépend de la charge du moment. --parallel-threads et max_parallel_degree restent des plafonds : SET max_parallel_degree = 1 force toujours l'exécution en série d'une session.

Pour la volumétrie des BLOB, --lob-threshold et --lob-cache (voir 15.1.3 et 15.3) influent aussi sur la performance : un seuil plus bas déporte plus de valeurs hors des fichiers de table (utile si les lignes doivent rester compactes en mémoire), un cache plus grand réduit les lectures répétées de BLOB volumineux au prix de la mémoire qu'il occupe.

15.9 Gestionnaire graphique Miraj Server Manager#

Sous Windows, Miraj Server Manager (miraj-manager.exe, installé par l'assistant de Miraj Express) rassemble les gestes courants d'administration d'un serveur de la machine, depuis une icône de la zone de notification :

  • démarrer, arrêter et redémarrer le serveur, par la tâche planifiée « Miraj Express » de l'installation ou en lançant miraj-server.exe --root <données> --log ;
  • suivre son état, sa consommation de processeur, de mémoire et de disque, avec les courbes des dix dernières minutes ;
  • suivre les requêtes : nombre total (Questions), requêtes par seconde, requêtes en cours (SHOW FULL PROCESSLIST), requêtes lentes (Slow_queries, mysql.slow_log) et réglage du journal des requêtes lentes ;
  • faire une sauvegarde complète (BACKUP ALL DATABASES) vers un dossier choisi, immédiate ou planifiée, avec un compte dédié miraj_backup limité à BACKUP_ADMIN.

Ses textes suivent la variable language de miraj_config.xml. Son fonctionnement est décrit au chapitre 21.