Per impostazione predefinita WordPress utilizza un solo database (tipicamente MySQL/MariaDB) definito in wp-config.php tramite costanti come DB_NAME, DB_USER, DB_PASSWORD e DB_HOST. In molti progetti, però, si presenta l’esigenza di usare più di un database: separare dati applicativi dai contenuti, integrare un gestionale esterno, archiviare log/telemetria, distribuire il carico (repliche in lettura), o “shardare” tabelle molto grandi.
Questo articolo spiega approcci, pro e contro e implementazioni per lavorare con più database in WordPress, con esempi pronti da adattare.
Quando ha senso usare più database
- Integrazione con un database esterno (ERP/CRM, catalogo prodotti, magazzino, anagrafiche).
- Separazione dei dati: ad esempio log applicativi o eventi in un DB dedicato.
- Prestazioni e scalabilità: repliche in lettura, separazione di tabelle ad alta intensità I/O.
- Manutenzione e governance: policy diverse di backup/retention per set di dati differenti.
- Conformità: isolare dati sensibili in un’istanza/cluster separato con controlli più rigidi.
Panoramica degli approcci
- Connessione aggiuntiva “ad hoc” per leggere/scrivere su un database esterno (senza toccare il DB principale di WordPress).
- Routing del DB di WordPress su più database (repliche, sharding, separazione per tabelle) tramite un db drop-in (ad es. HyperDB / LudicrousDB) o una personalizzazione avanzata di
wpdb. - Servizi esterni (API, data warehouse, ecc.) che sostituiscono l’accesso diretto al secondo DB: non è “più database in WP” in senso stretto, ma spesso è più robusto e sicuro.
Prerequisiti e note importanti
- Verifica che l’hosting consenta connessioni a DB remoti (firewall, allowlist IP, porte, TLS).
- Evita di salvare credenziali in chiaro nel repository: usa variabili d’ambiente o secret manager.
- Proteggi le query: usa preparazione parametri, escaping, permessi minimi per l’utente DB.
- Considera la coerenza: due database significano due transazioni separate (niente ACID cross-DB senza sistemi dedicati).
Approccio 1: collegarsi a un database esterno con una seconda istanza di wpdb
Il modo più semplice per usare un secondo database è creare una nuova istanza della classe wpdb. Così WordPress continua a usare il proprio DB, mentre tu esegui query sul DB esterno quando serve.
1.1 Definire le credenziali in modo sicuro
In wp-config.php puoi leggere le credenziali da variabili d’ambiente (o da un file non versionato). Esempio:
// wp-config.php
define('EXTDB_NAME', getenv('EXTDB_NAME') ?: '');
define('EXTDB_USER', getenv('EXTDB_USER') ?: '');
define('EXTDB_PASSWORD', getenv('EXTDB_PASSWORD') ?: '');
define('EXTDB_HOST', getenv('EXTDB_HOST') ?: 'localhost');
// Facoltativo: charset/collation del DB esterno
define('EXTDB_CHARSET', 'utf8mb4');
define('EXTDB_COLLATE', 'utf8mb4_unicode_ci');
1.2 Creare un client DB esterno (mu-plugin o plugin)
È consigliabile incapsulare la connessione in una funzione o classe, idealmente in un MU-Plugin (wp-content/mu-plugins/) così viene caricato sempre e prima dei plugin standard.
<?php
// file: wp-content/mu-plugins/external-db.php
if (!defined('ABSPATH')) { exit; }
function my_extdb() : wpdb {
static $extdb = null;
if ($extdb instanceof wpdb) {
return $extdb;
}
// Crea una nuova istanza: NON influenza $wpdb globale
$extdb = new wpdb(EXTDB_USER, EXTDB_PASSWORD, EXTDB_NAME, EXTDB_HOST);
// Imposta charset e collate, se necessari
if (defined('EXTDB_CHARSET') && EXTDB_CHARSET) {
$extdb->set_charset($extdb->dbh, EXTDB_CHARSET, defined('EXTDB_COLLATE') ? EXTDB_COLLATE : '');
}
// In produzione evita di mostrare errori SQL
$extdb->hide_errors();
return $extdb;
}
1.3 Eseguire query in sicurezza
Usa prepare() per parametri, ed evita di interpolare valori direttamente nella stringa SQL.
$extdb = my_extdb();
$user_id = 123;
$row = $extdb->get_row(
$extdb->prepare(
"SELECT id, email, status FROM external_users WHERE wp_user_id = %d",
$user_id
),
ARRAY_A
);
if ($row) {
// ...
}
1.4 Scritture, error handling e debug
Quando inserisci o aggiorni, controlla sempre il risultato e gli errori. Per troubleshooting puoi temporaneamente abilitare show_errors() su ambiente di sviluppo.
$extdb = my_extdb();
$inserted = $extdb->insert(
'external_events',
[
'wp_user_id' => 123,
'event_type' => 'login',
'created_at' => current_time('mysql', true),
],
['%d', '%s', '%s']
);
if ($inserted === false) {
error_log('EXTDB insert error: ' . $extdb->last_error);
}
1.5 Performance: caching e riduzione delle query
Se il DB esterno viene consultato spesso, riduci la pressione con cache applicativa (transient o object cache persistente). Esempio con transients:
$cache_key = 'ext_user_' . $user_id;
$data = get_transient($cache_key);
if ($data === false) {
$extdb = my_extdb();
$data = $extdb->get_row(
$extdb->prepare("SELECT * FROM external_users WHERE wp_user_id = %d", $user_id),
ARRAY_A
);
// Cache per 5 minuti
set_transient($cache_key, $data, 5 * MINUTE_IN_SECONDS);
}
Approccio 2: usare più database per le tabelle di WordPress (routing/sharding/repliche)
Questo approccio è più complesso: invece di aggiungere un DB “esterno”, si modifica come WordPress sceglie la connessione per le sue query interne. È utile quando vuoi:
- repliche in lettura (read replicas) per alleggerire il primario;
- separare tabelle (ad es.
wp_optionssu un DB,wp_postssu un altro); - sharding per multisite o per tabelle enormi.
2.1 Il concetto di “db drop-in”
WordPress supporta un file speciale chiamato db.php in wp-content/, detto drop-in. Se presente, WordPress lo carica per gestire la connessione al database. Soluzioni come HyperDB o LudicrousDB sfruttano questo meccanismo per instradare le query verso diversi server/database in base a regole.
Vantaggi: routing centralizzato, repliche e failover.
Svantaggi: maggiore complessità operativa, test accurati necessari, attenzione a coerenza e latenza tra primario e replica.
2.2 Schema tipico con primario + replica in lettura
Una strategia comune è inviare le query SELECT a una replica e le query INSERT/UPDATE/DELETE al primario. Il drop-in decide in base al tipo di query e (spesso) al contesto: durante login, aggiornamenti di opzioni, scrittura di sessioni e carrelli, è preferibile forzare il primario.
Molte implementazioni forniscono una configurazione in PHP; concettualmente, lo schema è questo:
// Esempio concettuale (non è una configurazione universale)
// Primario (scritture) e Replica (letture)
$databases = [
'primary' => [
'host' => '10.0.0.10',
'name' => 'wp_main',
'user' => 'wp_user',
'pass' => '***',
'write' => true,
'read' => true,
],
'replica_1' => [
'host' => '10.0.0.11',
'name' => 'wp_main',
'user' => 'wp_ro',
'pass' => '***',
'write' => false,
'read' => true,
],
];
// Regola: SELECT -> replica, altro -> primario
function pick_db_for_query(string $sql) : string {
return (preg_match('/^\s*SELECT\b/i', $sql)) ? 'replica_1' : 'primary';
}
In pratica, dovrai seguire la documentazione della soluzione scelta (HyperDB/LudicrousDB o alternativa) perché formati e funzionalità variano. Prima di andare in produzione, testa:
- latenza di replica (eventuale lettura di dati “vecchi”);
- coerenza durante checkout, login, aggiornamenti profilo;
- fallimento di un nodo (failover) e recupero;
- compatibilità con plugin critici (e-commerce, membership, cache, ecc.).
2.3 Separare tabelle su database diversi
Separare tabelle può avere senso quando alcune sono molto più “calde” di altre. Esempi:
wp_options(molte letture) su un database ottimizzato per letture;wp_posts/wp_postmetasu un cluster dedicato;- tabelle di log su un DB a parte per non impattare il DB principale.
Il routing “per tabella” richiede regole robuste: WordPress (e molti plugin) eseguono query con join tra più tabelle. Se una query unisce tabelle su database diversi, non puoi fare un join diretto senza usare federazione o soluzioni specifiche, e potresti dover ripensare l’architettura.
Approccio 3: tabelle personalizzate in un secondo database
Se stai creando funzionalità custom (ad es. analytics, eventi, import massivi), spesso il compromesso migliore è:
- lasciare WordPress sul suo DB;
- creare tabelle custom in un DB separato;
- accedere con una seconda istanza
wpdb(Approccio 1).
3.1 Creazione tabelle e migrazioni
Per il DB di WordPress esiste dbDelta(). Per un DB esterno puoi gestire migrazioni con SQL versionato (o strumenti come Flyway/Liquibase a livello infrastrutturale). Un esempio minimale di “migrazione” gestita in plugin:
function my_extdb_migrate() {
$extdb = my_extdb();
$sql = "
CREATE TABLE IF NOT EXISTS external_events (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
wp_user_id BIGINT UNSIGNED NOT NULL,
event_type VARCHAR(64) NOT NULL,
created_at DATETIME NOT NULL,
PRIMARY KEY (id),
KEY wp_user_id (wp_user_id),
KEY event_type (event_type),
KEY created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
";
$ok = $extdb->query($sql);
if ($ok === false) {
error_log('EXTDB migrate error: ' . $extdb->last_error);
}
}
3.2 Accesso in lettura intensiva: indicizzazione e query plan
Quando separi dati su un DB esterno, ottimizzi più facilmente indici e schemi per il tuo caso d’uso. Buone pratiche:
- indice sulle colonne usate in filtri e ordinamenti;
- evita colonne “blob” se non necessarie;
- usa
EXPLAINper validare i piani di esecuzione; - paginazione coerente (chiavi stabili, evitare offset enormi).
EXPLAIN
SELECT wp_user_id, event_type, created_at
FROM external_events
WHERE wp_user_id = 123
ORDER BY created_at DESC
LIMIT 50;
Gestione di transazioni e consistenza tra due database
Se un’operazione deve scrivere su due database, devi scegliere una strategia di consistenza. In assenza di un coordinatore transazionale (2PC) è comune usare:
- Consistenza eventuale: scrivi su WP, metti un evento in coda e aggiorni il DB esterno in background.
- Outbox pattern: scrivi su WP e registri un record “outbox” nel DB principale; un worker legge e propaga verso il DB esterno, con retry.
- Compensazione: se la seconda scrittura fallisce, registri un’azione di rollback logico (non sempre possibile).
Per molti siti, la strada più solida è evitare doppie scritture sincrone in request/response, e spostare l’integrazione su job asincroni (cron, queue, worker).
Sicurezza
- Principio del minimo privilegio: crea utenti DB separati (read-only dove possibile).
- TLS se il DB è remoto; preferisci reti private/VPN.
- Sanificazione e query preparate:
$wpdb->prepare(). - Segreti: non hardcodare credenziali; limita accessi e ruota password.
- Audit: logga errori e tempi di risposta, ma senza includere password o dati sensibili.
Testing e deployment
- Ambienti separati (dev/stage/prod) con DB diversi e credenziali diverse.
- Test di integrazione: simula indisponibilità del DB esterno e verifica i fallback.
- Timeout ragionevoli e retry controllati (evitare “thundering herd”).
- Backup: definisci piani distinti per ciascun DB (RPO/RTO diversi).
Checklist rapida
- Definisci perché ti serve un secondo DB (integrazione? performance? isolamento?).
- Se ti serve solo un DB esterno: usa una seconda istanza di wpdb (Approccio 1).
- Se vuoi routing delle query WP: valuta un db drop-in e prepara test accurati (Approccio 2).
- Gestisci caching e indici per ridurre query lente.
- Tratta consistenza e doppie scritture come un problema di architettura (queue/outbox).
- Metti in sicurezza credenziali, rete, permessi e logging.
Appendice: snippet utili
Verificare connessione al DB esterno
$extdb = my_extdb();
if (!$extdb->dbh) {
error_log('EXTDB: handle non valido');
} else {
$ok = $extdb->query('SELECT 1');
if ($ok === false) {
error_log('EXTDB: test query fallita: ' . $extdb->last_error);
}
}
Comando per testare accesso da CLI (utile in debug infrastrutturale)
mysql -h EXTDB_HOST -u EXTDB_USER -p EXTDB_NAME -e "SELECT NOW();"
Con questi approcci puoi scegliere la soluzione più adatta: dalla semplice integrazione con un database esterno fino a strategie avanzate di scalabilità con repliche e routing. Inizia sempre dal caso più semplice che soddisfa i requisiti e introduci complessità solo quando è davvero necessaria.