Perché InnoDB non memorizza il conteggio delle righe?


19

Tutti sanno che, nelle tabelle che utilizzano InnoDB come motore, query simili SELECT COUNT(*) FROM mytablesono molto inesatte e molto lente, specialmente quando la tabella diventa più grande e ci sono inserimenti / eliminazioni di riga costanti durante l'esecuzione della query.

A quanto ho capito, InnoDB non memorizza il conteggio delle righe in una variabile interna, motivo per questo problema.

La mia domanda è: perché è così? Sarebbe così difficile archiviare tali informazioni? È un'informazione importante da sapere in così tante situazioni. L'unica difficoltà che vedo se un tale conteggio interno sarebbe implementato è quando sono coinvolte le transazioni: se la transazione non è impegnata, conti le righe da essa inserite o no?

PS: Non sono un esperto di DB, sono solo qualcuno che ha MySQL come un semplice hobby. Quindi, se ho appena chiesto qualcosa di stupido, non essere eccessivamente critico: D.


6
Lento, si. Inesatto, no. È lento perché dà il risultato esatto. Quando si dispone di una tabella di 200 milioni di righe e probabilmente di molte altre transazioni che inseriscono / eliminano nella stessa tabella, probabilmente più righe al secondo, un'altra domanda è "è necessario il numero esatto?"
ypercubeᵀᴹ

@ypercube So di aver visto alcune volte in phpmyadmin alcuni valori di conteggio delle righe che erano molto spenti. Inoltre, c'è un commento che dice qualcosa del tipo "potrebbe non essere accurato".
Radu Murzea,

1
Utenti di @RaduMurzea phpMyAdmin un metodo alternativo per calcolare il conteggio delle tabelle per le tabelle InnoDB per i motivi di velocità che conosci. È qui che entra in gioco l'inaccuratezza che hai citato. Le SELECT COUNT(*) FROM ...domande effettive sono precise. Se si preferisce, phpMyAdmin può essere configurato per utilizzare sempre il conteggio esatto delle righe a spese della velocità. Maggiori informazioni: stackoverflow.com/questions/11926259/...
DOOManiac

Risposte:


9

Sono d'accordo con @RemusRusanu (+1 per la sua risposta)

SELECT COUNT(*) FROM mydb.mytablein InnoDB si comporta come dovrebbe un motore di archiviazione transazionale. Confronta con MyISAM.

MyISAM

Se mydb.mytableè una tabella MyISAM, l'avvio SELECT COUNT(*) FROM mydb.mytable;è proprio come in esecuzione SELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';. Ciò innesca una rapida ricerca del conteggio delle righe nell'intestazione della tabella MyISAM.

InnoDB

Se si mydb.mytabletratta di una tabella InnoDB, si ottiene un sacco di cose in corso. Hai MVCC in corso, che governa quanto segue:

  • ib_logfile0 / ib_logfile1 (Ripristina registri)
  • ibdata1
    • Annulla registri
    • rollback
    • Modifiche al dizionario dei dati
  • Gestione pool buffer
  • Transaction Isolation (4 tipi)
    • Letture ripetibili
    • Leggi impegnato
    • Leggi non confermato
    • Serializable

Chiedere a InnoDB un conteggio delle tabelle richiede la navigazione attraverso queste cose minacciose. In realtà, non si sa mai se SELECT COUNT(*) from mydb.mytableconta solo letture ripetibili o include letture che sono state impegnate e quelle che non sono state confermate.

Potresti provare a stabilizzare un po 'le cose abilitando innodb_stats_on_metadata .

Secondo la documentazione MySQL su innodb_stats_on_meta_data

Quando questa variabile è abilitata (che è l'impostazione predefinita, come prima della creazione della variabile), InnoDB aggiorna le statistiche durante istruzioni di metadati come SHOW TABLE STATUS o SHOW INDEX o quando si accede alle tabelle INFORMATION_SCHEMA TABLES o STATISTICS. (Questi aggiornamenti sono simili a quanto accade per ANALYZE TABLE.) Se disabilitato, InnoDB non aggiorna le statistiche durante queste operazioni. La disabilitazione di questa variabile può migliorare la velocità di accesso per schemi con un numero elevato di tabelle o indici. Può anche migliorare la stabilità dei piani di esecuzione per le query che coinvolgono tabelle InnoDB.

Disabilitarlo può dare o meno un conteggio più stabile in termini di impostazione dei piani EXPLAIN. Può influire sulle prestazioni SELECT COUNT(*) from mydb.mytablein modo positivo, negativo o per nulla. Provalo e guarda !!!


16

Per cominciare, non esiste un "conteggio corrente" da memorizzare in una variabile. Una query simile SELECT COUNT(*) FROM ...è soggetta all'attuale livello di isolamento e a tutte le transazioni in sospeso simultanee. A seconda del livello di isolamento, la query può visualizzare o meno le righe inserite o eliminate da transazioni in sospeso. L'unico modo per rispondere è contare le righe visibili alla transazione corrente.

Si noti che non ho nemmeno toccato l'argomento ancora più spinoso delle transazioni simultanee che iniziano o finiscono durante il conteggio. Per non parlare dei rollback ...


1
Ok, quindi dipende dal livello di isolamento, ha senso. Ma può ancora essere implementato.
Radu Murzea,

@SoboLAN Ci sono molte ragioni per cui non dovrebbe e non può essere, la maggior parte delle quali sono elencate sopra. Lo implementeresti mantenendo un elenco di conteggi per tabella per inizio transazione (qualunque sia l'SCN di Oracle in MySQL)? Gestire tali conteggi sarebbe un enorme sovraccarico: pensate a un database con 100 o 1000 sessioni simultanee che fanno ciascuna grandi quantità di INSERT / DELETE sulla stessa tabella. Impossibile da mantenere.
Philᵀᴹ

L'attuazione di questo è abbastanza difficile. Basti pensare che il conteggio deve essere persistito nel DB, ciò significa che da qualche parte nei metadati, e questo conteggio deve essere mantenuto da ogni transazione che inserisce o elimina una riga. Come vorresti bloccare quei metadati? E come gestiresti i rollback? È tutt'altro che banale. E il risultato sarebbe utilizzabile per un sottoinsieme molto ristretto di query.
Remus Rusanu,

3
@JackDouglas Interessante. Da quello che ho visto in passato le COUNT(*)query raramente sono necessarie nella realtà e di solito sono il risultato dell'inesperienza degli sviluppatori (conta le righe prima di selezionarle!) O di una cattiva progettazione delle app.
Philᵀᴹ

1
@SoboLAN - no, non lo farebbe. Avere un servizio che aggiorna una sorta di tabella delle statistiche a intervalli di tempo predefiniti è molto meglio. Immagina di avere un database di grandi dimensioni e diversi amministratori che interrogano la maggior parte delle tabelle con SELECT COUNT(*), aggiungi un non ottimizzato WHEREalla tabella e avrai alcuni utenti che mettono in ginocchio il db per diversi contatori stat discutibilmente utili.
NB

0

Mentre teoricamente sarebbe possibile mantenere un conteggio accurato del numero di righe per una data tabella con InnoDB, sarebbe a costo di un sacco di blocchi, che influirebbe negativamente sulle prestazioni. Differirebbe anche in base al livello di isolamento.

MyISAM già esegue il blocco a livello di tabella, quindi non ci sono costi aggiuntivi.

Raramente ho bisogno di un conteggio delle righe per una tabella, anche se uso abbastanza COUNT (*). In genere ho una clausola WHERE in allegato. Utilizzando un indice efficiente su un piccolo set di risultati, trovo che siano abbastanza veloci.

Non sono d'accordo sul fatto che i conteggi siano inaccurati. I conteggi rappresentano un'istantanea dei dati e li ho sempre trovati esatti.

In breve, MySQL lascia a te l'implementazione di questo per InnoDB. È possibile memorizzare un conteggio e incrementarlo / decrementarlo dopo ogni query. Tuttavia, la soluzione più semplice è probabilmente passare a MyISAM.


2
E ' non è possibile tenere un conteggio accurato del di righe in un sistema transazionale. Perché ci sono tanti conteggi di riga diversi (e corretti) quante transazioni attive.
a_horse_with_no_name

5
Ho dato un -1 qui per "Anche se, la soluzione più semplice è probabilmente passare a MyISAM". Non consiglierei mai di passare a MyISAM semplicemente per ottenere il conteggio delle righe.
Derek Downey,

@a_horse_with_no_name, quindi accetti che ci sia un conteggio delle righe "corretto" per ogni transazione. Mi sembra possibile.
Marcus Adams,

1
@DTest, non ho mai detto "semplicemente per ottenere il conteggio delle righe".
Marcus Adams,

@a_horse_with_no_name, Non sembra giusto. Sicuramente stiamo contando il numero di righe solo quando le transazioni vengono impegnate, giusto?
Pacerier,
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.