À 02:13, quelqu’un dit : « Mais c’est Aurora — AWS le gère. » À 02:14, votre latence p95 plonge comme une piste de ski, l’appli réessaie comme si chaque tentative était payante, et la chaîne d’astreinte discute pour savoir s’il faut « juste ajouter un lecteur ».
Ceci n’est pas sur la page produit : les bases gérées enlèvent beaucoup de corvées, mais elles ne suppriment pas la physique. Aurora MySQL n’est pas « MySQL mais plus rapide ». C’est un système différent qui parle le protocole MySQL, avec une forme de défaillance différente, un modèle de stockage différent, et des leviers différents quand il faut réparer la production rapidement.
Géré ne veut pas dire plus rapide : ce qui change (et ce qui ne change pas)
Quand les gens disent « Aurora est plus rapide », ils veulent souvent dire « Aurora demande moins de travail ». C’est vrai. Mais ce sont des affirmations différentes, et les confondre crée des surprises coûteuses.
Ce que « géré » vous apporte réellement
- Maintenance des correctifs et fenêtres de maintenance (principalement). Vous cessez de faire à la main les mises à jour d’OS, les installations binaires et une grande partie de la cérémonie opérationnelle.
- Sauvegardes intégrées et restauration point-in-time sans devoir greffer des outils supplémentaires.
- Croissance automatique du stockage et moins de pages à 3 h du matin « disque plein » (mais cela peut encore arriver avec des tempêtes de connections et des tables temporaires, donc ne soyez pas trop sûr de vous).
- Réplicas gérés et orchestration du basculement avec moins de scripts personnalisés — plus la capacité de monter rapidement la lecture si votre charge s’y prête.
- Métriques et journaux sur une plateforme partagée (CloudWatch, Performance Insights), ce qui peut être vraiment utile si vous apprenez à les interpréter.
Ce que « géré » ne vous achète pas
- Correction automatique des requêtes. Votre ORM peut toujours générer une requête qui ressemble à une lettre de rançon.
- Liberté face aux mauvais choix de schéma. Un index manquant sur un chemin chaud vous fera mal sur n’importe quel moteur.
- Concurrence infinie. Threads, mutex, contention du buffer pool et attentes de verrou restent présents.
- Latence prévisible sous rafales. Aurora a des goulots différents, pas aucun.
- Basculement instantané sans conséquences. Il y a toujours un rayon d’impact : les caches se réchauffent, les statements préparés disparaissent, les transactions se roll-backent, et votre appli doit savoir se comporter.
Opérationnellement, le changement le plus important est celui-ci : avec MySQL auto-hébergé, vous possédez toute la pile et pouvez instrumenter ou ajuster presque tout. Avec Aurora MySQL, vous obtenez un filet de sécurité plus grand et moins de boutons. Ce compromis vaut souvent le coup, mais vous devez savoir quels boutons ont disparu — avant l’incident.
Une citation pour rester honnête : « L’espoir n’est pas une stratégie. » (idée paraphrasée, attribuée au général Gordon R. Sullivan et largement utilisée en ingénierie/ops)
Petite blague #1 : Aurora est géré, oui. Votre linge aussi — jusqu’au moment où vous laissez un stylo dans la poche.
Sous le capot : calcul, stockage, journalisation et pourquoi c’est important
MySQL auto-hébergé (y compris « MySQL sur EC2 ») est un schéma classique : le serveur de base de données possède son buffer pool, son redo log, son binary log, et lit/écrit sur un stockage de blocs (EBS, NVMe, SAN, ou ce que vous lui donnez). La performance dépend principalement du CPU, de la mémoire et des IOPS/latence du stockage — plus la conception des requêtes.
Aurora MySQL conserve la couche de calcul compatible MySQL, mais la couche de stockage est un service distribué à part. Ce choix d’architecture change tout en aval : récupération après crash, création de réplicas, basculement, et certaines classes de latence.
Couche de calcul : toujours MySQL-ish, toujours soumise au comportement MySQL
Vos sessions, verrous, transactions et le comportement de l’optimiseur restent de la famille MySQL. Les méchants habituels restent : mauvais plans, index manquants, lignes trop larges, tables temporaires qui s’emballent, verrous métadonnées, et la contention sur « une seule ligne chaude ».
Couche de stockage : ce n’est plus votre volume EBS
Le stockage d’Aurora est distribué sur plusieurs nœuds et zones de disponibilité, conçu pour s’auto-réparer. Le nœud de calcul envoie des enregistrements de journal au stockage ; le stockage gère la réplication. Les détails diffèrent selon la génération d’Aurora, mais le résultat opérationnel est constant :
- Durabilité et réplication sont intégrées dans le sous-système de stockage.
- Le calcul est plus remplaçable. Dans beaucoup de cas de panne, vous ne « réparez pas un volume », vous remplacez une instance de calcul qui s’attache à un stockage déjà répliqué.
- Certaines patterns d’E/S semblent différentes. Les écritures peuvent revenir moins chères dans certains cas car vous expédiez des enregistrements de journal, pas des écritures de pages complètes. Les lectures peuvent être très rapides quand elles sont en cache, et étonnamment pénibles quand vous ratez le cache et exigez beaucoup d’accès aléatoires.
Journalisation : la différence discrète qui mord pendant les incidents
Dans MySQL/InnoDB classique, les redo logs et le doublewrite buffer jouent un grand rôle dans la performance et la récupération après crash. Aurora change cette histoire. Vous devez vous attendre à :
- Caractéristiques différentes de récupération après crash (souvent plus rapides), parce que le stockage a déjà un flux de journal durable.
- Sensibilité différente aux blocages d’E/S. Parfois un blocage ressemble à « la base est gelée », alors que la cause racine est en amont du moteur (quorum de stockage, pépin réseau, voisin bruyant au mauvais endroit).
- Moins d’options « réparation avec des outils filesystem ». Vous ne pouvez pas fsck pour vous sortir d’une mauvaise journée parce que vous n’avez pas le filesystem.
Ce que vous perdez : un peu de contrôle, un peu d’observabilité, quelques astuces
Sur MySQL auto-hébergé, si nécessaire, vous pouvez : fixer l’affinité CPU, ajuster les ratios dirty du noyau, tuner le RAID, choisir XFS vs ext4, ou appliquer des patches Percona. Avec Aurora, vous travaillez via des groupes de paramètres, la taille des instances, et la conception des requêtes/schémas. Ce n’est pas pire. C’est différent — et ça restreint votre « boîte à outils d’urgence ».
Vérités sur la performance : où Aurora gagne, où il perd, et où c’est identique
Aurora peut sembler plus rapide parce que la montée en charge et la récupération sont plus rapides
La réputation d’Aurora est en partie méritée : vous pouvez lancer des lecteurs rapidement, le stockage croît sans que vous le surveilliez, et le basculement peut être plus propre qu’une installation MySQL artisanale. Si votre base était « MySQL sur une EC2 unique avec un réplica fragile et des sauvegardes mysqldump nocturnes », Aurora ressemblera à une fusée.
Mais le moteur obéit toujours aux mêmes lois
Si vous êtes lié par le CPU sur le parsing, le tri, les jointures ou la contention, Aurora ne le corrigera pas magiquement. Si vous êtes limité par l’I/O à cause de scans gigantesques, d’index manquants, ou d’un buffer pool trop petit, Aurora peut encore vous nuire. Parfois il blesse de nouvelles façons : vos pics de latence de lecture sont maintenant modelés par un système de stockage distribué et des chemins réseau, pas seulement la latence du disque local.
Charges qui tirent souvent profit d’Aurora
- Charges orientées lecture avec séparation claire vers des lecteurs, surtout si vous pouvez tolérer un peu de décalage du réplica.
- Charges mixtes qui bénéficient d’un basculement rapide et d’un provisionnement rapide de réplicas.
- Jeux de données volumineux où la gestion du stockage est une charge que vous voulez arrêter de payer.
Charges qui peuvent décevoir
- OLTP ultra-sensible à la latence où un p99 en millisecondes à un chiffre compte et où la gigue est inacceptable.
- Systèmes très écriture-intensifs et à forte contention avec clés chaudes, hauts taux de verrouillage ou fort churn d’index secondaires.
- Patrons de requêtes provoquant des lectures aléatoires en rafale (pensez : beaucoup de lookups point qui ratent le cache, plus un working set large).
« Aurora est plus rapide » veut parfois dire « Aurora est plus grosse »
Les instances Aurora peuvent être provisionnées grandes, et beaucoup de migrations incluent une montée silencieuse de la taille de l’instance. Félicitations : vous avez benchmarké « plus de CPU et de RAM » et appelé ça de l’architecture.
Petite blague #2 : Le cloud facilite la montée en charge ; il facilite aussi la montée de la facture plus vite que le débit.
La façon la plus honnête de comparer : définissez le goulot d’abord
Avant de choisir une plateforme, répondez par écrit à ceci :
- Êtes-vous principalement lié par le CPU, l’I/O, les verrous ou le réseau ?
- Votre douleur concerne-t-elle le débit moyen ou la latence en queue (tail) ?
- Avez-vous besoin d’un scale-out pour les lectures, ou de writes plus rapides ?
- Quel est votre budget d’échec (RTO/RPO), et l’application se comporte-t-elle réellement correctement pendant un basculement ?
Si vous ne pouvez pas répondre, vous ne choisissez pas une base de données. Vous choisissez une histoire.
Faits intéressants et contexte historique (rapide, concret)
- La popularité initiale de MySQL (fin des 90s/2000s) venait du fait d’être « assez bon » pour les workloads web et facile à exploiter comparé aux bases d’entreprise plus lourdes.
- InnoDB est devenu le moteur par défaut dans MySQL 5.5 (2010), orientant la majorité des déploiements MySQL vers MVCC, récupération après crash et verrouillage au niveau ligne comme baseline standard.
- Amazon a lancé RDS (2009) avant qu’Aurora n’existe, posant l’idée que « géré » signifiait moins de corvées ops, pas une nouvelle architecture de stockage.
- Aurora a fait ses débuts (2014) comme moteur séparé compatible MySQL, pas « MySQL stock avec un wrapper ». La compatibilité est un contrat ; les internes ne le sont pas.
- Les réplicas de lecture étaient autrefois une activité DIY dans beaucoup de structures — construire un réplica, le tuner, surveiller le lag, scripter le basculement — donc la gestion simplifiée des réplicas d’Aurora a semblé transformative.
- MySQL 8.0 (GA 2018) a apporté de grosses améliorations (dictionnaire de données, JSON amélioré, meilleures window functions, performance schema plus robuste), changeant la donne pour le MySQL « vanilla » d’aujourd’hui.
- La réplication basée sur GTID a mûri au fil du temps et réduit la complexité du basculement pour MySQL classique — mais elle dépend toujours des binary logs et du comportement d’application des réplicas.
- Les systèmes de stockage distribués dans les bases cloud sont devenus un modèle : découpler le calcul du stockage pour rendre le calcul remplaçable, accélérer la récupération et partager le stockage pour scaler les lectures.
Réplication et basculement : attentes vs réalité
Réplication MySQL classique : binlogs, apply, et lag palpable
La réplication standard MySQL est logique : le primaire écrit dans les binary logs ; les réplicas tirent et appliquent. Le lag provient du réseau, du CPU du réplica, du disque, ou d’un apply mono-thread (moins courant aujourd’hui, mais toujours possible selon la charge). Le basculement est un problème d’orchestration : promouvoir un réplica, s’assurer des positions GTID, repointer les applis, nettoyer le risque de split-brain.
Réplicas Aurora : tuyauterie différente, symptômes familiers
Aurora utilise une couche de stockage distribuée partagée ; les readers peuvent être ajoutés sans copier l’intégralité du dataset au sens traditionnel. Cela signifie généralement une provision plus rapide et parfois moins de lag de réplication. Mais les symptômes qui comptent — lectures périmées, incohérence read-your-writes, lag des réplicas sous charge — apparaissent toujours. Vous les diagnostiquez juste différemment.
Basculement : plus rapide n’est pas sans conséquence
Même si le basculement de la base est rapide, la récupération de votre application peut ne pas l’être. Points de douleur typiques :
- Pools de connexions qui inondent le nouveau writer par des stormes de reconnection.
- Statements préparés ou état de session qui disparaissent ; les applis supposant des sessions stateful se cassent de façons étranges.
- Transactions en vol qui roll-backent ; les retries multiplient la charge d’écritures exactement quand le système est fragile.
- Mise en cache DNS / caching d’endpoints qui retardent la concordance entre « basculement terminé » et « application récupérée ».
Règle de décision : si vous ne pouvez pas démontrer un basculement propre lors d’un test contrôlé — incluant le comportement de l’application — votre RTO est un vœu.
Pratique : 14 tâches avec commandes, sorties et décisions
Voici les vérifications que vous lancez réellement quand la production est mécontente. Les commandes sont montrées comme si vous aviez un accès shell à un hôte avec les clients MySQL et l’AWS CLI configurés. Tous les environnements ne ressemblent pas à celui-ci, mais le workflow tient.
Task 1: Confirm what you’re connected to (MySQL vs Aurora, version, and instance role)
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u app -p -e "SELECT @@version, @@version_comment, @@read_only, @@aurora_version;"
Enter password:
@@version @@version_comment @@read_only @@aurora_version
8.0.34 Aurora MySQL (Compatible with MySQL 8.0.34) 0 3.05.2
Ce que cela signifie : Vous êtes sur Aurora MySQL, nœud writer (@@read_only=0), avec une version moteur Aurora séparée de la version MySQL.
Décision : Adoptez les attentes spécifiques à Aurora : comportement du stockage, modèle de basculement et groupes de paramètres diffèrent du MySQL auto-hébergé.
Task 2: Check current load shape: threads, running queries, and lock waits
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SHOW GLOBAL STATUS LIKE 'Threads_running'; SHOW GLOBAL STATUS LIKE 'Threads_connected';"
Enter password:
Variable_name Value
Threads_running 42
Variable_name Value
Threads_connected 980
Ce que cela signifie : 980 sessions connectées dont 42 exécutent activement. Cela peut être normal (grand pool) ou un signal d’alerte (tempête de connections).
Décision : Si la latence monte, suspectez de la mise en file : trop de connexions ou quelques requêtes lentes causant un entassement.
Task 3: Identify top waits (Aurora / MySQL 8 performance schema)
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SELECT event_name, COUNT_STAR, SUM_TIMER_WAIT/1000000000000 AS total_s FROM performance_schema.events_waits_summary_global_by_event_name ORDER BY SUM_TIMER_WAIT DESC LIMIT 5;"
Enter password:
event_name COUNT_STAR total_s
wait/io/file/innodb/innodb_data_file 8221142 913.42
wait/synch/mutex/innodb/buf_pool_mutex 12882211 540.19
wait/io/table/sql/handler 2321441 210.03
wait/lock/metadata/sql/mdl 11222 98.77
wait/synch/cond/sql/COND_thr_lock 882212 75.11
Ce que cela signifie : Forte I/O sur les fichiers de données et contention sur le mutex du buffer pool ; aussi du temps de verrou métadonnée non négligeable.
Décision : Pour l’I/O : vérifiez les taux de hit cache et les plans de requêtes. Pour le mutex : suspectez des pages chaudes, trop de threads, ou une pression sur le buffer pool. Pour les MDL : vérifiez les DDL ou les transactions longues.
Task 4: Check buffer pool hit rate and reads
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
Enter password:
Variable_name Value
Innodb_buffer_pool_read_requests 9182211443
Innodb_buffer_pool_reads 22184219
Ce que cela signifie : Des lectures depuis le disque/stockage existent. Le ratio de hit est approximativement 1 - (reads/read_requests), ici très élevé, mais des misses absolus peuvent encore être douloureux lors des rafales.
Décision : Si la latence en queue suit les misses du cache, réduisez le working set (indexes, forme des requêtes) ou augmentez la mémoire/taille d’instance, ou réduisez les lectures aléatoires.
Task 5: Find the worst queries by total time (statement digest)
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SELECT digest_text, count_star, round(sum_timer_wait/1000000000000,1) AS total_s, round(avg_timer_wait/1000000000000,4) AS avg_s FROM performance_schema.events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 3\G"
Enter password:
*************************** 1. row ***************************
digest_text: SELECT * FROM orders WHERE customer_id = ? ORDER BY created_at DESC LIMIT ?
count_star: 812112
total_s: 642.7
avg_s: 0.0008
*************************** 2. row ***************************
digest_text: UPDATE inventory SET available = available - ? WHERE sku = ?
count_star: 922111
total_s: 610.3
avg_s: 0.0007
*************************** 3. row ***************************
digest_text: SELECT COUNT(*) FROM events WHERE tenant_id = ? AND created_at > ?
count_star: 2112
total_s: 501.9
avg_s: 0.2377
Ce que cela signifie : La troisième requête est peu fréquente mais à haute latence ; probablement un scan important ou un mauvais usage d’index.
Décision : Corrigez les requêtes « lentes mais rares » — elles dominent la latence en queue et la sévérité des incidents.
Task 6: Validate index usage with EXPLAIN (don’t guess)
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "EXPLAIN SELECT COUNT(*) FROM events WHERE tenant_id = 42 AND created_at > '2025-12-01'\G"
Enter password:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: events
partitions: NULL
type: range
possible_keys: idx_tenant_created
key: idx_tenant_created
key_len: 12
ref: NULL
rows: 1822112
filtered: 100.00
Extra: Using where; Using index
Ce que cela signifie : Utilise un index composite. Parcourt tout de même ~1.8M d’entrées d’index ; peut être légitime, peut être trop lent sous charge.
Décision : Si cela vous pagine, ajoutez un prédicat plus serré, une table de rollup, une stratégie de partitionnement, ou de la pré-agrégation. Parfois la bonne correction est « arrêter de compter en temps réel ».
Task 7: Check for metadata lock blockage (classic migration/DDL failure mode)
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS='PENDING' LIMIT 5\G"
Enter password:
*************************** 1. row ***************************
OBJECT_TYPE: TABLE
OBJECT_SCHEMA: appdb
OBJECT_NAME: orders
LOCK_TYPE: EXCLUSIVE
LOCK_DURATION: TRANSACTION
LOCK_STATUS: PENDING
OWNER_THREAD_ID: 18211
OWNER_EVENT_ID: 912
Ce que cela signifie : Une session attend un verrou exclusif sur orders, typiquement un DDL. Elle est bloquée par quelqu’un tenant un verrou partagé (souvent une transaction longue ou un curseur ouvert).
Décision : Identifiez les sessions bloquantes ; envisagez de tuer le bloqueur (avec prudence) ou de reprogrammer le DDL en utilisant des patterns de changement de schéma en ligne.
Task 8: Check replication / reader lag (Aurora via status variables)
cr0x@server:~$ mysql -h mydb-reader.cluster-ro-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SHOW GLOBAL STATUS LIKE 'Aurora_replica_lag%';"
Enter password:
Variable_name Value
Aurora_replica_lag_in_msec 128
Aurora_replica_lag_max_in_msec 902
Ce que cela signifie : Lag actuel ~128ms, maximum observé ~902ms. Pas terrible, mais si votre appli suppose read-your-writes sur les lecteurs, elle mentira parfois.
Décision : Si vous avez besoin de read-your-writes, routez ces lectures vers le writer ou implémentez une cohérence de session (jeton, cache, ou « lire depuis le writer après une écriture »).
Task 9: Verify temporary table pressure (often hidden behind “CPU spike”)
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%tables';"
Enter password:
Variable_name Value
Created_tmp_disk_tables 221122
Created_tmp_files 8122
Created_tmp_tables 982211
Ce que cela signifie : Beaucoup de tables temporaires sur disque. C’est généralement « vos requêtes trient/aggrègent sans bons index » ou « tmp_table_size trop petit », ou les deux.
Décision : Corrigez d’abord la requête (indexes, réduire l’ensemble de résultats). Puis ajustez la taille des tables temporaires si nécessaire. Donner de la mémoire à du SQL mauvais ne fait que changer la vitesse à laquelle cela vous fait mal.
Task 10: Check InnoDB row lock time (contention reality check)
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SHOW GLOBAL STATUS LIKE 'Innodb_row_lock%';"
Enter password:
Variable_name Value
Innodb_row_lock_current_waits 18
Innodb_row_lock_time 812211
Innodb_row_lock_time_avg 33
Innodb_row_lock_time_max 12004
Innodb_row_lock_waits 24218
Ce que cela signifie : Des attentes sur des verrous de lignes existent et le délai max est moche. C’est de la contention au niveau applicatif ou de la conception transactionnelle.
Décision : Raccourcissez les transactions, évitez « lire puis écrire plus tard », ajoutez des index pour réduire les lignes verrouillées, ou repensez les lignes chaudes (sharder les compteurs, éviter les files d’attente sur une seule ligne).
Task 11: Confirm connection storm / pool behavior from processlist
cr0x@server:~$ mysql -h mydb.cluster-xxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SHOW PROCESSLIST LIMIT 5;"
Enter password:
Id User Host db Command Time State Info
31122 app 10.0.12.55:48122 appdb Sleep 62 NULL
31123 app 10.0.12.55:48123 appdb Sleep 62 NULL
31124 app 10.0.13.18:51211 appdb Query 8 Sending data SELECT ...
31125 app 10.0.13.18:51212 appdb Query 8 Sending data SELECT ...
31126 app 10.0.14.77:39911 appdb Sleep 62 NULL
Ce que cela signifie : Beaucoup de connexions en sommeil suggèrent de grands pools. « Sending data » sur plusieurs threads suggère une requête produisant/streamant beaucoup de lignes ou effectuant de lourdes lectures.
Décision : Si vous voyez des milliers de sleepers et des pics au moment du basculement, limitez les tailles des pools et ajoutez un backoff avec jitter sur les reconnexions.
Task 12: Measure at the OS level on self-managed MySQL (CPU vs I/O)
cr0x@server:~$ iostat -x 1 3
Linux 6.1.0 (ip-10-0-2-10) 12/30/2025 _x86_64_ (8 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
22.31 0.00 5.88 9.42 0.12 62.27
Device r/s w/s rkB/s wkB/s await %util
nvme0n1 820.0 410.0 51200.0 18432.0 6.12 91.3
Ce que cela signifie : Forte utilisation disque et temps d’attente non négligeable. Vous êtes contraint par l’I/O ou proche de l’être.
Décision : Sur MySQL auto-hébergé, vous pouvez améliorer le stockage (NVMe/EBS plus rapide), ajuster le flushing, ou réduire l’I/O via de meilleures requêtes/indexes. Si vous êtes sur Aurora, vous ne lancez pas iostat sur la couche stockage — utilisez les métriques moteur et les événements d’attente à la place.
Task 13: Check CloudWatch metrics for Aurora (from CLI) to correlate with symptoms
cr0x@server:~$ aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name DatabaseConnections \
--statistics Average Maximum \
--period 60 \
--start-time 2025-12-30T01:00:00Z \
--end-time 2025-12-30T01:10:00Z \
--dimensions Name=DBClusterIdentifier,Value=mydb-cluster
{
"Label": "DatabaseConnections",
"Datapoints": [
{"Timestamp":"2025-12-30T01:02:00Z","Average":812.2,"Maximum":1201.0,"Unit":"Count"},
{"Timestamp":"2025-12-30T01:03:00Z","Average":910.4,"Maximum":1602.0,"Unit":"Count"}
]
}
Ce que cela signifie : Les connexions ont fortement augmenté. Cela correspond souvent à des déploiements, des basculements, des retries clients, ou une mauvaise configuration des pools.
Décision : Si le graphe de connexions ressemble à une falaise, stabilisez d’abord les clients : limites de pool, timeouts, backoff avant d’optimiser les requêtes.
Task 14: Validate parameter differences that change behavior (Aurora parameter group)
cr0x@server:~$ aws rds describe-db-parameters \
--db-parameter-group-name my-aurora-mysql8-params \
--query "Parameters[?ParameterName=='innodb_flush_log_at_trx_commit' || ParameterName=='sync_binlog' || ParameterName=='max_connections'].[ParameterName,ParameterValue,ApplyType]" \
--output table
----------------------------------------------
| DescribeDBParameters |
+--------------------------------+----------+
| innodb_flush_log_at_trx_commit| 1 |
| max_connections | 4000 |
| sync_binlog | 1 |
+--------------------------------+----------+
Ce que cela signifie : Les paramètres de durabilité sont stricts (1), et max_connections est élevé (ce qui peut ou non être judicieux).
Décision : Ne mettez pas max_connections haut juste parce que vous le pouvez ; cela peut amplifier la contention. Gardez-le aligné avec le CPU et la concurrence attendue, et corrigez d’abord la configuration des pools applicatifs.
Méthode de diagnostic rapide : trouver le goulot en quelques minutes
Vous n’avez pas le temps d’être philosophique pendant un incident. Il vous faut un entonnoir qui converge rapidement vers la cause racine, et qui ne vous mente pas.
Première étape : est-ce une tempête côté client ou un ralentissement côté base ?
- Vérifiez les connexions (CloudWatch DatabaseConnections ;
Threads_connected). - Vérifiez les connexions rejetées/échouées dans les logs applicatifs. Si les clients ré-essaient agressivement, la base devient victime, pas cause.
- Décision : Si les connexions ont augmenté de 2–10×, stabilisez d’abord le comportement client : plafonnez les pools, ajoutez du backoff, et raccourcissez les timeouts pour que les connexions mortes ne s’accumulent pas.
Deuxième étape : le writer est-il CPU-bound, lock-bound, ou I/O/wait-bound ?
- Signes CPU-bound : forte utilisation CPU, beaucoup de threads runnables, requêtes lourdes en tri/jointure. En termes MySQL : beaucoup de travail « statistique », gros sorts, traitement JSON, regex, etc.
- Signes lock-bound :
Innodb_row_lock_time_maxélevé, événements d’attente sur verrous, « Waiting for table metadata lock », lignes chaudes. - Signes I/O/wait-bound : fortes attentes I/O fichier, misses du buffer pool, pics dans les events liés au stockage, et « tout est lent mais le CPU n’est pas élevé ».
- Décision : Choisissez une seule classe de goulot et poursuivez-la ; ne faites pas de tuning en rafale.
Troisième étape : isolez le motif de requête le plus coupable
- Utilisez les digest de statements pour trouver le top en temps total et le top en temps moyen.
- Sortez des requêtes représentatives et lancez
EXPLAIN. - Décision : Corrigez la requête qui domine soit le temps total (douleur de débit), soit le temps moyen (douleur en latence tail), selon les symptômes.
Quatrième étape : validez la stratégie de réplicas et le routage des lectures
- Vérifiez le lag des réplicas et si l’app utilise les lecteurs pour des flux read-after-write.
- Décision : Si la cohérence importe, routez ces lectures vers le writer ou implémentez un contrôle explicite de cohérence de session.
Cinquième étape : seulement ensuite, considérez des changements de capacité
- Scalez la classe d’instance si le CPU ou la mémoire est clairement saturé.
- Ajoutez un lecteur seulement si vous avez vérifié que les lectures peuvent être externalisées et que le lag est acceptable.
- Décision : Le scaling est un palliatif valable, pas un diagnostic. Traitez-le comme un analgésique : utile, mais pas nutritif.
Trois mini-histoires d’entreprise venues du terrain
Mini-histoire #1 : L’incident causé par une mauvaise hypothèse (« Les réplicas Aurora sont toujours frais »)
L’entreprise était en migration de MySQL auto-hébergé vers Aurora MySQL. Le plan avait l’air sûr : garder les lectures sur l’endpoint reader, écrire sur le writer, et profiter des économies en scalant les lectures horizontalement. L’équipe appli a aussi fait un « petit » changement : après le checkout, la page de confirmation de commande lirait depuis le reader endpoint, parce que « lire c’est lire ».
Pendant une période calme, ça marchait. En période de pointe, quelques clients ont rafraîchi et n’ont pas vu leur commande. Les tickets support sont arrivés en premier ; puis l’équipe paiements a remarqué des autorisations en double car les clients ont réessayé le paiement. L’observabilité ne montrait rien de dramatique : CPU writer OK, pas d’erreurs de base, et le lag du réplica « seulement quelques centaines de millisecondes ».
La cause racine était l’hypothèse que « quelques centaines de millisecondes » équivalent à zéro pour les flux métier. Ce n’est pas le cas. Le chemin de checkout nécessitait une cohérence read-your-writes, et le système ne l’avait pas. Les clients n’avaient pas tort ; l’architecture avait tort.
La correction a été sobre et précise : toute lecture de confirmation post-écriture a été épinglée sur le writer pendant une courte fenêtre via un token de session, et l’UI a cessé de traiter « non trouvé » comme une permission de réessayer le paiement. Les lectures sur réplica sont restées pour la navigation, la recherche et l’historique. L’incident a cessé immédiatement, et la migration a continué — avec une nouvelle règle écrite en tête du runbook : « Les endpoints reader sont pour les lectures tolérantes à l’obsolescence. »
Mini-histoire #2 : L’optimisation qui a eu l’effet inverse (pooling de connexions « poussé à 11 »)
Une autre équipe avait un problème classique : trop de microservices, chacun avec son propre pool, chacun configuré par quelqu’un qui avait touché une base en 2016 pour la dernière fois. Lors de la migration vers Aurora, ils ont vu une valeur élevée de max_connections et ont décidé d’en « profiter ». Les pools ont été augmentés dans toute la flotte. Ils ont célébré la disparition des erreurs occasionnelles « trop de connexions ».
Puis la latence a commencé à dériver vers le haut lors des pics de trafic. Pas toujours. Juste assez pour provoquer une panne lente : timeouts ici, retries là, profondeur de file qui augmente en arrière-plan. Le writer n’était pas saturé CPU. Les métriques de stockage ne hurlaient pas. Mais performance schema montrait des mutex waits et des lock waits en hausse. Les threads passaient du temps à se coordonner, pas à travailler.
Le retour de bâton était simple : un nombre élevé de connexions a permis plus de travail concurrent que la base ne pouvait traiter efficacement. InnoDB et MySQL ne deviennent pas plus rapides si vous ajoutez des threads au-delà du point de contention ; ils deviennent un ordonnanceur très coûteux. Aurora ne l’a pas causé, mais Aurora a rendu plus facile d’entrer dans cet état parce que les garde-fous semblaient plus larges.
La reprise a consisté à revenir en arrière sur les tailles de pools, plus une amélioration de second ordre : l’équipe a ajouté une mise en file côté application pour quelques endpoints coûteux, lissant les pics. Le débit est resté le même, mais la latence en queue s’est améliorée dramatiquement. Ils ont arrêté de « tuner » MySQL en changeant seulement le nombre qui semblait le plus gros.
Mini-histoire #3 : La pratique ennuyeuse mais correcte qui a sauvé la mise (répétition de basculement avec comportement client)
Une fintech utilisait Aurora MySQL pour un service adjacent au grand livre. Ils avaient un rituel hebdomadaire : déclencher un basculement contrôlé dans un environnement non-prod qui reproduisait la topologie prod, puis observer ce que l’application faisait. Pas seulement la base. L’application.
Lors d’une répétition, la base a basculé en temps raisonnable, mais l’appli a mis des minutes à récupérer. Les pools gardaient des connexions mortes. Certains services ont retry immédiatement et en synchronisation, créant un thundering herd synchronisé. Un cache warmer a assommé le writer avec des requêtes de cold-start juste au moment où le système essayait de retrouver son équilibre.
Ils l’ont corrigé avant que ça ne compte : keepalive TCP plus court, timeouts clients sensés, backoff exponentiel avec jitter, et warmup de pool respectant une limite de débit globale. Ils ont aussi implémenté un flag « mode dégradé » qui réduisait les endpoints coûteux pendant la récupération.
Des mois plus tard, un vrai événement AZ a forcé un basculement réel. La base a fait sa part, mais la raison pour laquelle les clients n’ont pas remarqué est la répétition ennuyeuse. Rien d’héroïque n’est arrivé en astreinte. La meilleure réponse à un incident est celle où tout le monde a l’air légèrement déçu d’avoir été réveillé.
Erreurs courantes : symptôme → cause racine → correction
1) Symptom: “Aurora is slower than our old MySQL box”
Cause racine : Vous êtes passé d’un NVMe local avec un cache chaud à un chemin de stockage distribué avec des caractéristiques de latence différentes, et votre working set ne tient plus en mémoire.
Correction : Mesurez les misses du buffer pool et les events d’attente ; augmentez la mémoire d’instance si justifié ; réduisez les lectures aléatoires (index, forme des requêtes) et évitez les scans larges sur les chemins OLTP.
2) Symptom: p99 latency spikes during deploys or failovers
Cause racine : Tempêtes de connexions et retries synchronisés. La base devient la victime du comportement client.
Correction : Plafonnez les pools, ajoutez un backoff exponentiel avec jitter, raccourcissez les timeouts, et assurez-vous que votre appli reconstruit proprement connexions et statements.
3) Symptom: Readers show “missing” data right after writes
Cause racine : Lag des réplicas et cohérence éventuelle, même petite.
Correction : Routez les lectures read-after-write vers le writer ; implémentez des tokens de cohérence de session ; évitez d’utiliser l’endpoint reader pour les flux de confirmation.
4) Symptom: “Adding a reader didn’t help”
Cause racine : La charge est liée aux écritures, à la contention, ou dominée par des lectures qui doivent aller au writer (read-your-writes, transactions, écritures de tables temporaires).
Correction : Identifiez le goulot writer via les waits et les top queries ; réduisez l’amplification des écritures (indexes), corrigez les lignes chaudes, et séparez les patterns de lecture qui sont sûrs à externaliser.
5) Symptom: High CPU but low throughput
Cause racine : Mauvais plans de requêtes, tris/groupes lourds, ou trop de threads concurrents causant contention et changements de contexte.
Correction : Utilisez les digests du performance schema + EXPLAIN ; réduisez la concurrence (caps pools), ajoutez des indexes manquants, et éliminez les requêtes inutiles par requête.
6) Symptom: “Database hung” with many sessions in ‘Waiting for table metadata lock’
Cause racine : DDL en attente derrière une transaction longue ou un curseur ouvert tenant un verrou métadonnée.
Correction : Trouvez les bloqueurs via le performance schema ; tuez ou terminez les bloqueurs ; planifiez les DDL en faible trafic ; utilisez des techniques de changement de schéma en ligne adaptées.
7) Symptom: Sudden spikes in temp disk tables and latency
Cause racine : Requêtes produisant de grands résultats intermédiaires ; indexes insuffisants ; gros sorts ; group by sur colonnes non indexées.
Correction : Corrigez le SQL et les index ; réduisez les ensembles de résultats ; seulement ensuite envisagez d’ajuster la taille des tables temporaires.
8) Symptom: “Failover was quick, but the app was down”
Cause racine : Caching d’endpoints appli, DNS périmé, connexions longue durée, ou drivers qui ne se reconnectent pas proprement.
Correction : Validez le comportement de reconnexion client ; gardez des TTL/caches réalistes ; testez le basculement bout à bout.
Checklists / plan étape par étape
Checklist A: Choosing between self-managed MySQL and Aurora MySQL
- Définissez votre goulot : CPU, I/O, verrous, ou complexité opérationnelle.
- Notez votre RTO/RPO et confirmez que l’application peut survivre au basculement sans intervention opérateur.
- Classifiez les lectures : lesquelles sont tolérantes à l’obsolescence, lesquelles sont read-after-write.
- Décidez ce que vous voulez posséder : tuning OS/noyau/filesystem et choix de stockage (auto-hébergé) vs moins de boutons et durabilité gérée (Aurora).
- Benchmarkez avec une charge représentative : pas un dataset jouet, pas une boucle monotone. Incluez des rafales et des phases cache-cold.
- Planifiez l’observabilité : performance schema et slow logs, plus métriques cloud et suivi des digests de requêtes.
Checklist B: Migration plan that won’t humiliate you later
- Inventoriez les fonctionnalités : triggers, procédures stockées, event scheduler, modes SQL, collations, fuseaux horaires.
- Définissez explicitement les groupes de paramètres plutôt que d’hériter des valeurs par défaut en espérant qu’elles conviennent.
- Effectuez des écritures duales ou du change data capture uniquement si vous avez un plan de rollback clair ; sinon gardez-le plus simple.
- Validez les plans de requêtes sur Aurora. Le même SQL peut choisir des plans différents selon les versions et les statistiques.
- Faites un game day de basculement avec l’application, pas seulement la base de données.
- Basculez les lectures avec précaution : commencez par les endpoints tolérants à l’obsolescence ; gardez read-after-write sur le writer jusqu’à preuve du contraire.
- Plafonnez les pools avant le cutover pour éviter le choc de connexions.
Checklist C: Incident response when latency spikes
- Stabilisez les clients : interrompre les retry storms, plafonner les pools, appliquer du backoff.
- Identifiez les top waits (performance schema) et les top queries (digests).
- Choisissez un levier : tuer les requêtes runaway, ajouter un index, scaler l’instance, ou réduire la charge — mais faites-le délibérément.
- Protégez le writer : déplacez les lectures non critiques vers les lecteurs seulement si cohérent, sinon rate-limitez.
- Documentez le déclencheur : déploiement, changement de trafic, job batch, ou changement de schéma — pour qu’il ne revienne pas la semaine suivante.
FAQ
1) Is Aurora MySQL literally MySQL?
Non. C’est compatible MySQL au niveau protocole et fonctionnalités, mais l’architecture de stockage et de réplication est différente. Traitez-le comme un moteur distinct avec une couche de compatibilité.
2) Will Aurora always be faster than self-managed MySQL?
Non. Certaines charges s’améliorent grâce à une récupération plus rapide, un scaling plus simple et des valeurs par défaut gérées. D’autres régresseront à cause du comportement du cache, de la latence du stockage distribué, ou d’un mauvais dimensionnement d’instance. Benchmarkez votre charge.
3) If Aurora decouples compute and storage, does that mean storage latency doesn’t matter?
La latence du stockage compte toujours ; elle se manifeste simplement différemment. Vous ne pouvez pas « tuner le disque », mais vos requêtes attendent toujours l’I/O quand elles ratent le cache ou écrivent beaucoup.
4) Can I fix performance issues in Aurora with the same knobs as MySQL?
En partie. Vous avez toujours beaucoup de paramètres MySQL et des leviers schéma/requête. Mais vous perdez le tuning au niveau OS/filesystem et quelques instruments profonds. Attendez-vous à moins d’« astuces intelligentes », plus de « faire les fondamentaux ».
5) Should I send all reads to Aurora readers?
Non. N’envoyez que les lectures tolérantes à l’obsolescence. Tout ce qui exige read-your-writes doit soit lire le writer, soit implémenter une logique explicite de cohérence de session.
6) Why didn’t adding a read replica reduce writer CPU?
Parce que votre charge peut être liée aux écritures, à la contention, ou votre application lit toujours depuis le writer à cause du comportement transactionnel, du routage ou des contraintes de cohérence. Mesurez la distribution réelle des requêtes.
7) How do I compare costs fairly between MySQL on EC2 and Aurora?
Incluez le temps opérateur, le stockage de sauvegarde, l’outillage de réplication/basculement, et la fréquence des incidents. Incluez aussi les coûts « cachés » : instances sur-dimensionnées pour survivre aux basculements et tempêtes de connexions.
8) What’s the biggest reliability risk during migration?
Le comportement de l’application pendant le basculement et les hypothèses de cohérence. La plupart des migrations échouent parce que l’appli s’attend à ce que la base se comporte comme un serveur unique immortel.
9) Does Aurora remove the need for query optimization?
Non. Il enlève une partie de la charge opérationnelle, pas le besoin d’index pertinents et de requêtes saines. Le mauvais SQL est portable ; il vous fera mal partout.
Conclusion : étapes pratiques suivantes
Si vous hésitez entre MySQL auto-hébergé et Aurora MySQL, ne laissez pas « géré » remplacer « rapide ». Prenez la décision comme n’importe quelle décision de production : en identifiant le goulot réel que vous avez et les modes de défaillance que vous ne pouvez pas vous permettre.
- Faites un vrai benchmark avec votre charge : incluez des rafales, des phases cache-cold, et mesurez p95/p99 — pas seulement le débit moyen.
- Rédigez une politique de routage des lectures qui définit quelles lectures vont aux réplicas et lesquelles doivent aller au writer.
- Renforcez le comportement client : limites de pool, timeouts raisonnables, retries avec jitter, et logique de reconnexion testée.
- Instrumentez ce qui compte : top wait events, top query digests, métriques d’attente de verrous, création de tables temporaires, et lag des réplicas.
- Entraînez-vous au basculement bout à bout. Pas « le cluster a basculé ». L’appli a récupéré. Ce ne sont pas la même phrase.
Aurora est une bonne option si vous voulez une durabilité gérée, une réplication plus simple et moins de pièces à surveiller vous-même. MySQL auto-hébergé reste un bon choix si vous avez besoin d’un contrôle maximal, d’un comportement de latence local prévisible, ou de builds moteur personnalisés et d’un tuning profond. Choisissez le système qui correspond à vos contraintes, pas à vos espoirs.