Come posso accelerare "mostra colonne" su MySQL?


7

La mia applicazione dipende dall'esecuzione di "mostra colonne" per alcune tabelle. Sono necessari circa 60 ms per l'esecuzione, mentre tutte le altre query richiedono meno di un ms. L'interrogazione information_schemadiretta è persino più lenta.

Il database contiene circa 250 database, con 100-200 tabelle per database (circa 20.000 tabelle in totale).

  • Come posso sapere perché queste operazioni sono così lente?
  • Esiste forse qualche impostazione che posso modificare per renderla più veloce o memorizzarla nella cache lato SQL?

(L'applicazione esegue circa 14 query di questo tipo per caricamento di pagina: sono ben consapevole che questo codice legacy deve essere ripulito, ma sto cercando possibili opzioni mentre lavoro sulla correzione a lungo termine.)


1
per interesse, in quale scenario i 60ms sarebbero troppo lenti per esaminare le colonne di una tabella? Non è qualcosa che dovresti fare per ogni richiesta

1
Che cosa vuoi dire mostra colonne? Raccogliere i nomi delle colonne da una tabella o stampare un'intera colonna? Se sono i nomi ... perché non lo prendi solo una volta e lo memorizzi nella tua applicazione ... se ciò non è possibile perché non crei semplicemente un'altra tabella che contiene tutte le colonne basate sulla tabella?

@Jaitsu: No, non è qualcosa che dovremmo fare, ma è così. Codice legacy. Fino a quando non avrò un po 'di tempo per ripulirlo e farlo correttamente, voglio vedere se riesco ad accelerarlo. Ne ho circa 14 che eseguono ogni caricamento di pagina.

@FlorinStingaciu: Sì, nomi di colonna. Inserirli in un altro tavolo potrebbe accelerare le cose, ma uscirà dalla sincronizzazione, il che vanifica l'intero scopo di chiedere direttamente al tavolo.

1
@Mat: non è una cattiva idea. Votato per la migrazione a dba.

Risposte:


12

MySQL ricalcola le statistiche delle tabelle per determinate operazioni che accedono alle INFORMATION_SCHEMAtabelle ( SHOW COLUMNSè solo un comodo alias per le query INFORMATION_SCHEMA.COLUMNS). Impostare innodb_stats_on_metadata su false, che impedirà che questo ricalcolo si verifichi quando si chiedono i metadati dalla tabella.

SET GLOBAL innodb_stats_on_metadata=0;

e aggiungi quanto segue a my.cnf

[mysqld]
innodb_stats_on_metadata = 0

Avrei dovuto menzionare che sto effettivamente usando MyISAM. Ho provato a impostarlo comunque, ma non ha prodotto alcun vantaggio.
Aprire l'

Hai considerato ALTER TABLE per ENGINE = InnoDB? :) C'è una buona ragione per usare MyISAM?
Aaron Brown,

Ragioni ereditate per lo più, penso. Temo che cosa potrebbe accadere se lo provassi; non sono sicuro che tutti gli FK si allineeranno. Ci penserò ancora un po '.
Aprire l'

@AaronBrown +1 per questa risposta perché chiunque affronta questa situazione con un database all-InnoDB ha bisogno di queste informazioni.
RolandoMySQLDBA l'

1
+1 per metterlo [mysqld]lì. Per molti potrebbe essere ovvio che questa impostazione andrebbe sotto mysqld, ma potrebbe non essere ovvio per coloro che porrebbero questa domanda. A proposito, questo ha accelerato SELECT COUNT(*)su uno dei miei information_schematavoli a 6 secondi da oltre un minuto. Ancora lento, ma un enorme miglioramento.
Buttle Butkus,

3

Ti suggerisco di creare un database che abbia le INFORMATION_SCHEMAtabelle (o solo quelle necessarie) come repliche. Indicizzali in modo appropriato e otterrai un miglioramento delle prestazioni.

Il problema della sincronizzazione tra questo database ed INFORMATION_SCHEMAè però difficile.

Potresti avere una procedura che sincronizza queste tabelle ogni ora o ogni 5 minuti (con che frequenza viene modificata la struttura delle tabelle?).

Un'altra idea sarebbe quella di utilizzare MySQL Proxy per raccogliere eventuali ALTER TABLEdichiarazioni (ed CREATEe DROPed CREATE INDEXe qualunque altre dichiarazioni modificare le informazioni di cui avete bisogno) e quindi sincronizzare lo schema di informazioni replicate dopo queste affermazioni successo.


Se hai bisogno solo dei nomi delle colonne e non di altre informazioni, come tipo di dati, lunghezza o indici disponibili, potresti forse sostituire l'uso di SHOW COLUMNSquery (veloci) che restituiscono solo 1 riga, con LIMIT 1o nessuna, con o LIMIT 0o:

SELECT * FROM TableName WHERE FALSE ;

Nonostante i consigli generali contro l'uso di SELECT *, questo può essere un caso legittimo in cui nient'altro è utile. (tutto il resto *, ma , può causare errori!)


2

In questo caso particolare, penso che INFORMATION_SCHEMAsia un'aringa rossa. Dai miei test sulle SHOW COLUMNSprestazioni, la innodb_stats_on_metadatavariabile non sembra fare alcuna differenza su entrambe le tabelle MyISAM o InnoDB.

Tuttavia, dal manuale di MySQL 5.0 ...

Alcune condizioni impediscono l'uso di una tabella temporanea in memoria, nel qual caso il server utilizza invece una tabella su disco:

[...]

  • Le istruzioni SHOW COLUMNSe The DESCRIBEusano BLOBcome tipo per alcune colonne, quindi la tabella temporanea utilizzata per i risultati è una tabella su disco.

Questo sembra essere stato rimosso dal manuale a partire da MySQL 5.5, ma sembra ancora applicarsi in quella versione ...

mysql> SHOW VARIABLES LIKE 'version';
+---------------+-------------------------+
| Variable_name | Value                   |
+---------------+-------------------------+
| version       | 5.5.41-0ubuntu0.14.04.1 |
+---------------+-------------------------+
1 row in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0     |
+-------------------------+-------+
1 row in set (0.00 sec)

mysql> SHOW COLUMNS FROM mysql.user;
[...snip...]
42 rows in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 1     |
+-------------------------+-------+
1 row in set (0.00 sec)

Le informazioni sul campo restituite con un set di risultati della query contengono le stesse informazioni restituite da SHOW COLUMNS, pertanto è SELECT * FROM my_table LIMIT 0necessario ottenere lo stesso risultato senza creare una tabella temporanea su disco per query.

Un breve esempio per prendere i nomi dei campi in PHP ...

$mysql = new mysqli('localhost', 'root', '', 'my_database');
$field_names = array();
$result = $mysql->query("SELECT * FROM my_table LIMIT 0");
$fields = $result->fetch_fields();
foreach ($fields as $fields)
{
    $field_names[] = $field->name;
}
var_dump($field_names);

Il recupero delle informazioni sul campo in questo modo è un po 'più complicato da decodificare. Dovrai consultare la descrizione della MYSQL_FIELDstruttura sottostante per estrarre i tipi di dati e i flag, ma funziona circa 7 volte più velocemente sul mio sistema.


1

Mi piace il primo suggerimento nella risposta di @ yerpcube (+1), ma vorrei proporre qualcosa

  • creare un'altra istanza di database sulla porta 3307
  • mysqldump il database di produzione in file di testo SQL utilizzando le seguenti opzioni:
    • --no-data
    • --routines
    • --triggers
    • --all-databaseso --databasesseguito da un elenco di database desiderati
  • Caricare il file di testo SQL nella porta 3307 Istanza MySQL

Pertanto, mysqldump dovrebbe apparire come segue:

mysqldump --no-data --routines --triggers --all-databases > ImportFile.sql

Questo è tutto. Andando avanti, tutto ciò che devi fare è connetterti a questa istanza di database della porta 3307 ed eseguire qualsiasi query relativa allo schema al contenuto del tuo cuore. Se si è a conoscenza di una tabella nel database di produzione che viene modificata, è sufficiente mysqldump lo schema dalla produzione e ricaricarlo nuovamente nell'istanza della porta 3307.

ATTENZIONE: se installi un'istanza mysql sullo stesso computer della produzione, assicurati assolutamente di connetterti a quell'istanza usando

mysql -u... -p... -h127.0.0.1 -P3307 < ImportFile.sql

Se esegui

mysql -u... -p... -P3307 < ImportFile.sql

Produrrà la produzione di tubi. Quindi sii attento !!!!

Un'alternativa sarebbe usare solo un server DB separato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.