MariaDB
 sql >> Database >  >> RDS >> MariaDB

Miglioramento delle prestazioni del back-end Parte 2/3:Utilizzo degli indici di database

Gli indici del database sono una preoccupazione degli sviluppatori. Hanno il potenziale per migliorare le prestazioni delle funzionalità di ricerca e filtro che utilizzano una query SQL nel back-end. Nella seconda parte di questa serie di articoli, mostrerò l'impatto che un indice di database ha nell'accelerare i filtri utilizzando un'applicazione Web Java sviluppata con Spring Boot e Vaadin.

Leggi la parte 1 di questa serie se vuoi imparare come funziona l'applicazione di esempio che useremo qui. Puoi trovare il codice su GitHub. Inoltre, e se preferisci, ho registrato una versione video di questo articolo:

Il requisito

Abbiamo una pagina web con una griglia che mostra un elenco di libri da un database MariaDB:

Vogliamo aggiungere un filtro per consentire agli utenti di questa pagina di vedere quali libri sono stati pubblicati in una determinata data.

Implementazione della query e del servizio di repository

Dobbiamo apportare alcune modifiche al back-end per supportare il filtraggio dei dati entro la data di pubblicazione. Nella classe repository, possiamo aggiungere il seguente metodo:

@Repository
public interface BookRepository extends JpaRepository<Book, Integer> {

    Page<Book> findByPublishDate(LocalDate publishDate, Pageable pageable);

}

Questo utilizza il caricamento lento come abbiamo visto nella parte 1 di questa serie di articoli. Non è necessario implementare questo metodo:Spring Data lo creerà per noi in fase di esecuzione.

Dobbiamo anche aggiungere un nuovo metodo alla classe di servizio (che è la classe utilizzata dall'interfaccia utente per eseguire la logica aziendale). Ecco come:

@Service
public class BookService {

    private final BookRepository repository;

    ...

    public Stream<Book> findAll(LocalDate publishDate, int page, int pageSize) {
        return repository.findByPublishDate(publishDate, PageRequest.of(page, pageSize)).stream();
    }

}

Aggiunta di un filtro alla pagina Web

Con il back-end che supporta il filtraggio dei libri per data di pubblicazione, possiamo aggiungere un selettore di data all'implementazione della pagina web (o della vista):

@Route("")
public class BooksView extends VerticalLayout {

    public BooksView(BookService service) {

        ...

        var filter = new DatePicker("Filter by publish date");
        filter.addValueChangeListener(event ->
                grid.setItems(query ->
                        service.findAll(filter.getValue(), query.getPage(), query.getPageSize())
                )
        );

        add(filter, grid);
        setSizeFull();
    }

    ...
}

Questo codice crea semplicemente un nuovo DatePicker oggetto che ascolta le modifiche nel suo valore (tramite un listener di modifica del valore). Quando il valore cambia, utilizziamo la classe di servizio per ottenere i libri pubblicati nella data selezionata dall'utente. I libri corrispondenti vengono quindi impostati come elementi della Grid .

Testare la query lenta

Abbiamo implementato il filtro; tuttavia, è estremamente lento se hai, ad esempio, 200 mila righe nella tabella. Provalo! Ho scritto un articolo che spiega come generare dati demo realistici per applicazioni Java. Con questo numero di righe, l'applicazione ha impiegato diversi secondi per mostrare i dati sulla pagina Web della mia macchina (MacBook Pro 2,3 GHz Quad-Core Intel Core i5). Questo rovina completamente l'esperienza dell'utente.

Analisi delle query con "Spiega query"

Se hai abilitato la registrazione delle query, puoi trovare la query generata da Hibernate nel registro del server. Copialo, sostituisci i punti interrogativi con i valori effettivi ed eseguilo in un client di database SQL. In effetti, posso farti risparmiare un po' di tempo. Ecco una versione semplificata della query:

SELECT id, author, image_data, pages, publish_date, title
FROM book
WHERE publish_date = '2021-09-02';

MariaDB include EXPLAIN dichiarazione che ci fornisce informazioni utili su come il motore stima che eseguirà la query. Per usarlo, aggiungi semplicemente EXPLAIN prima della domanda:

EXPLAIN SELECT id, author, image_data, pages, publish_date, title
FROM book
WHERE publish_date = '2021-09-02';

Ecco il risultato:

La documentazione contiene tutto ciò che devi sapere al riguardo, ma il bit importante è il valore nel tipo colonna:TUTTI . Questo valore ci dice che il motore stima che dovrà recuperare o leggere tutte le righe della tabella. Non è una buona cosa.

Creazione di un indice

Fortunatamente, possiamo risolvere facilmente questo problema creando un indice sulla colonna che stiamo usando per filtrare i dati:publish_date . Ecco come:

CREATE INDEX book\_publish\_date_index ON book(publish_date);

Un indice di database è una struttura di dati creata dal motore, solitamente un b-tree (b per bilanciato ), e ciò accelera il processo di ricerca di una determinata riga in una tabella, ovvero la ricerca di una riga dato il valore nella colonna su cui è costruito l'indice. Il processo è più veloce grazie alla natura dei b-tree:mantengono i dati ordinati riducendo la complessità temporale da O(N) a O(log(N)) e in alcuni casi anche O(log(1)).

Testare il miglioramento

Con l'indice creato, possiamo eseguire nuovamente l'istruzione EXPLAIN e vedere che la colonna del tipo mostra il valore ref invece di TUTTI :

Il rif value significa che il motore utilizzerà l'indice quando eseguiamo la query. È importante controllarlo quando aggiungi indici alle tue query più complesse. Usa sempre il EXPLAIN dichiarazione per ricontrollare che stai guadagnando in termini di prestazioni quando introduci un indice.

Se provi l'applicazione web nel browser e selezioni un'altra data nel selettore della data (non è necessario riavviare il server), vedrai un'enorme differenza! Ad esempio, i dati vengono recuperati in meno di un secondo sulla mia macchina, in contrasto con diversi secondi prima della creazione dell'indice!