Stratégies de mise à l'échelle de la base de données : réplicas en lecture, partage et au-delà
Les performances des bases de données constituent le goulot d'étranglement dans 78 % des problèmes de mise à l'échelle des applications Web. Les applications peuvent évoluer horizontalement avec un minimum d'effort, mais les bases de données résistent à la mise à l'échelle horizontale. Les stratégies que vous choisissez pour la mise à l'échelle de la base de données déterminent si votre application dessert 100 ou 100 000 utilisateurs avec des performances acceptables.
Ce guide couvre l'ensemble des stratégies de mise à l'échelle des bases de données, depuis les simples optimisations qui retardent le besoin de mise à l'échelle jusqu'aux techniques avancées telles que le partitionnement horizontal.
Points clés à retenir
- Optimisez les requêtes et ajoutez des index avant d'ajouter l'infrastructure --- cela résout 60 % des problèmes de performances des bases de données
- Les réplicas en lecture constituent la stratégie de mise à l'échelle la moins risquée et gèrent 80 % des charges de travail lourdes en lecture.
- Le pooling de connexions est obligatoire une fois que votre application exécute plus de 10 instances
- Le partitionnement horizontal est un dernier recours qui introduit une complexité applicative importante
L'échelle d'évolution
Échelle dans cet ordre. Chaque étape est moins chère et moins risquée que la suivante :
Étape 1 : Optimisation des requêtes (gratuite)
Avant d'ajouter une infrastructure, assurez-vous que votre base de données existante fonctionne de manière optimale.
-- Find slow queries in PostgreSQL
SELECT
calls,
mean_exec_time::numeric(10,2) AS avg_ms,
total_exec_time::numeric(10,2) AS total_ms,
query
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;
Optimisations courantes :
- Ajouter des index manquants pour les colonnes fréquemment filtrées
- Remplacez
SELECT *par des listes de colonnes spécifiques - Utilisez
EXPLAIN ANALYZEpour identifier les analyses séquentielles sur les grandes tables - Ajouter des index composites pour les clauses WHERE multi-colonnes
- Implémenter la pagination avec la pagination du jeu de clés au lieu de
OFFSET
-- Bad: OFFSET pagination (scans all skipped rows)
SELECT * FROM orders ORDER BY created_at DESC LIMIT 20 OFFSET 10000;
-- Good: Keyset pagination (index-only scan)
SELECT * FROM orders
WHERE created_at < '2026-03-15T10:00:00Z'
ORDER BY created_at DESC
LIMIT 20;
Étape 2 : Mise à l'échelle verticale ($)
Augmentez le processeur, la RAM et le stockage sur votre serveur de base de données existant. Cela fait gagner du temps et ne nécessite aucune modification de l’application.
| Taille de l'instance | Processeur virtuel | RAM | Connexions | Coût mensuel (RDS) |
|---|---|---|---|---|
| db.t3.medium | 2 | 4 Go | 100 | 65 $ |
| db.r6g.large | 2 | 16 Go | 200 | 175 $ |
| db.r6g.xlarge | 4 | 32 Go | 400 | 350 $ |
| db.r6g.2xlarge | 8 | 64 Go | 800 | 700 $ |
La plupart des applications atteignent leur limite à 64 Go de RAM et 8 vCPU. Au-delà de cela, la mise à l’échelle verticale devient prohibitive.
Étape 3 : Regroupement de connexions ($)
Application (50 pods x 20 connections = 1,000 connections)
|
v
PgBouncer (25 database connections, transaction pooling)
|
v
PostgreSQL (25 active connections, manageable)
Configuration du PgBouncer :
[databases]
app = host=db.example.com port=5432 dbname=production
[pgbouncer]
listen_port = 6432
listen_addr = 0.0.0.0
auth_type = md5
pool_mode = transaction
default_pool_size = 25
max_client_conn = 1000
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3
Étape 4 : Lire les répliques ($$)
Les réplicas en lecture gèrent les requêtes SELECT, déchargeant 60 à 90 % de la charge de la base de données du principal.
Architecture :
Write queries --> Primary database
|
Replication (async)
|
+----+----+
| |
Read queries --> Replica 1 Replica 2
Routage au niveau de l'application (exemple Drizzle ORM) :
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
const primaryPool = new Pool({ connectionString: process.env.DATABASE_URL });
const replicaPool = new Pool({ connectionString: process.env.DATABASE_REPLICA_URL });
export const primaryDb = drizzle(primaryPool);
export const replicaDb = drizzle(replicaPool);
// In service code:
// Write operations use primaryDb
async createOrder(data: OrderInput) {
return primaryDb.insert(orders).values(data).returning();
}
// Read operations use replicaDb
async getOrders(organizationId: string) {
return replicaDb.select().from(orders)
.where(eq(orders.organizationId, organizationId))
.orderBy(desc(orders.createdAt));
}
Considérations relatives au délai de réplication : la réplication asynchrone introduit un délai (généralement de 10 à 100 ms). Immédiatement après une écriture, la lecture à partir du réplica peut renvoyer des données obsolètes. Utilisez le principal pour les lectures qui suivent les écritures dans le même flux utilisateur.
Étape 5 : Mise en cache ($$)
La mise en cache Redis élimine entièrement les requêtes répétées de base de données.
async getProduct(id: string): Promise<Product> {
const cacheKey = `product:${id}`;
// Check cache first
const cached = await this.redis.get(cacheKey);
if (cached) return JSON.parse(cached);
// Cache miss: query database
const product = await this.db.select().from(products)
.where(eq(products.id, id))
.limit(1);
// Cache for 5 minutes
await this.redis.setex(cacheKey, 300, JSON.stringify(product[0]));
return product[0];
}
Stratégie d'invalidation du cache : invalider lors de l'écriture. Lorsqu'un produit est mis à jour, supprimez la clé de cache. Utilisez un modèle de mise en cache (l'application gère le cache) plutôt que l'écriture directe (la base de données gère le cache).
Étape 6 : Partage horizontal ($$$)
Le partage distribue les données sur plusieurs instances de base de données en fonction d'une clé de partition.
| Stratégie de partage | Descriptif | Idéal pour |
|---|---|---|
| Basé sur le hachage | Hachez la clé de fragment, répartissez-la uniformément | Même la distribution des données |
| Basé sur la plage | Attribuer des plages aux fragments (par exemple, A-M, N-Z) | Séries chronologiques, données géographiques |
| Basé sur les locataires | Une partition par locataire/organisation | SaaS multi-locataires |
Quand fragmenter :
- La base de données unique dépasse 1 To et continue de croître
- Le débit d'écriture dépasse ce qu'un seul primaire peut gérer
- Les coûts de mise à l'échelle verticale dépassent 2 000 $/mois sans marge
Quand NE PAS fragmenter :
- Vous n'avez pas épuisé les étapes 1 à 5
- Vos données tiennent dans une seule base de données de 500 Go
- Les requêtes entre fragments sont courantes dans votre application
Optimisations spécifiques à PostgreSQL
Partitionnement (avant le partitionnement)
Le partitionnement des tables PostgreSQL divise les grandes tables en tables physiques plus petites tout en conservant une seule table logique :
-- Partition orders by month
CREATE TABLE orders (
id UUID PRIMARY KEY,
organization_id UUID NOT NULL,
created_at TIMESTAMP NOT NULL,
total DECIMAL(10,2)
) PARTITION BY RANGE (created_at);
CREATE TABLE orders_2026_01 PARTITION OF orders
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE orders_2026_02 PARTITION OF orders
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
CREATE TABLE orders_2026_03 PARTITION OF orders
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
Le partitionnement améliore de 10 à 100 fois les performances des requêtes temporelles sur les grandes tables, car PostgreSQL analyse uniquement les partitions pertinentes.
Passation de l'aspirateur et entretien
-- Check table bloat
SELECT
schemaname,
relname,
n_live_tup,
n_dead_tup,
round(n_dead_tup::numeric / greatest(n_live_tup, 1) * 100, 2) AS dead_pct
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
Configurez autovacuum de manière agressive pour les tables à écriture élevée :
ALTER TABLE orders SET (
autovacuum_vacuum_threshold = 100,
autovacuum_vacuum_scale_factor = 0.05,
autovacuum_analyze_threshold = 50,
autovacuum_analyze_scale_factor = 0.02
);
Surveillance des performances de la base de données
Suivez ces métriques pour comprendre quand et comment évoluer :
| Métrique | Outil | Seuil d'alerte |
|---|---|---|
| Latence des requêtes (P95) | pg_stat_statements | >500ms |
| Connexions actives | pg_stat_activité | >80 % du maximum |
| Taux de réussite du cache | pg_stat_base de données | <95% |
| Retard de réplication | pg_stat_replication | >1 seconde |
| Gonflement de la table | pg_stat_user_tables | >20 % de tuples morts |
| Attente d'E/S disque | iostat/CloudWatch | >20 ms |
Un taux de réussite du cache inférieur à 95 % est l’indicateur le plus fort que vous avez besoin de plus de mémoire. L’augmentation de shared_buffers et effective_cache_size est souvent moins chère et plus rapide que l’ajout de réplicas en lecture.
Suivi des performances des requêtes
-- Enable pg_stat_statements (postgresql.conf)
-- shared_preload_libraries = 'pg_stat_statements'
-- Find the top 10 most time-consuming queries
SELECT
queryid,
calls,
mean_exec_time::numeric(10,2) AS avg_ms,
total_exec_time::numeric(10,2) AS total_ms,
rows,
query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
Passez en revue les 10 principales requêtes chaque semaine. L'optimisation d'une seule requête fréquemment exécutée peut réduire la charge globale de la base de données de 10 à 30 %.
Questions fréquemment posées
Comment savoir quand il est temps de passer à l'échelle ?
Surveillez trois métriques : latence des requêtes P95 (alerte à 500 ms), utilisation de la connexion (alerte à 80 %) et utilisation du processeur (alerte à 70 % soutenue). Si vous atteignez régulièrement ces seuils, passez à l’étape suivante de l’échelle d’évolution. Ne pré-optimisez pas --- mettez à l'échelle lorsque les données vous le demandent.
Lire les réplicas ou la mise en cache --- lequel en premier ?
Commencez par la mise en cache. La mise en cache Redis est plus simple à mettre en œuvre, élimine plus de charge (les accès au cache ignorent entièrement la base de données) et coûte moins cher. Ajoutez des réplicas en lecture lorsque votre taux de réussite du cache est déjà supérieur à 80 % mais que la base de données principale est toujours sous la pression des échecs de cache et des opérations d'écriture.
Comment fonctionne la mise à l'échelle de la base de données avec Odoo ?
Odoo utilise exclusivement PostgreSQL. Commencez par l'optimisation des requêtes (Odoo génère des requêtes complexes pour le reporting). Ajoutez PgBouncer pour le regroupement de connexions lorsque vous dépassez 50 utilisateurs simultanés. Utilisez des réplicas en lecture pour les requêtes de reporting (configurez l'option --db-replica d'Odoo). ECOSIRE fournit une optimisation des performances Odoo, y compris le réglage de la base de données.
La base de données gérée (RDS/Cloud SQL) vaut-elle le prix ?
Oui, pour la plupart des entreprises. Les bases de données gérées gèrent les sauvegardes, les correctifs, le basculement et la surveillance automatisés. Le surcoût de 30 à 40 % par rapport à PostgreSQL autogéré est compensé par le temps d'ingénierie que vous gagnez. L'exception concerne les déploiements à grande échelle où le coût supplémentaire d'une grande instance dépasse le coût d'un administrateur de base de données à temps partiel.
Ce qui vient ensuite
La mise à l’échelle des bases de données est un élément d’une stratégie plus large de mise à l’échelle de l’infrastructure. Combinez-le avec l'optimisation CDN pour les actifs statiques, la mise à l'échelle automatique Kubernetes pour les pods d'application et les tests de charge pour valider vos décisions de mise à l'échelle dans des conditions réalistes.
Contactez ECOSIRE pour des conseils en optimisation de bases de données, ou consultez notre guide DevOps pour la feuille de route complète de l'infrastructure.
Publié par ECOSIRE – aider les entreprises à faire évoluer leur infrastructure de données en toute confiance.
Rédigé par
ECOSIRE Research and Development Team
Création de produits numériques de niveau entreprise chez ECOSIRE. Partage d'analyses sur les intégrations Odoo, l'automatisation e-commerce et les solutions d'entreprise propulsées par l'IA.
Articles connexes
Le guide complet de l'intégration Power BI + Odoo
Connectez Power BI à Odoo ERP pour des analyses avancées. Requêtes directes PostgreSQL, tables clés, tableaux de bord ventes/inventaire/RH et configuration d'actualisation incrémentielle.
Modèles de passerelle API et meilleures pratiques pour les applications modernes
Implémentez des modèles de passerelle API, notamment la limitation de débit, l'authentification, le routage des requêtes, les disjoncteurs et la gestion des versions API pour les architectures Web évolutives.
Optimisation des performances CDN : le guide complet pour une livraison mondiale plus rapide
Optimisez les performances CDN avec des stratégies de mise en cache, l'informatique de pointe, l'optimisation des images et des architectures multi-CDN pour une diffusion mondiale plus rapide du contenu.