Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Elixir
Code

Un'introduzione alle tabelle ETS in Elisir

by
Length:LongLanguages:

Italian (Italiano) translation by Loris Pizii (you can also view the original English article)

Quando un programma elisir di crafting, è spesso necessario condividere uno stato. Ad esempio, in uno dei miei articoli precedenti ho mostrato come codificare un server per eseguire vari calcoli e mantenere il risultato in memoria (e più tardi abbiamo visto come fare questa server a prova di proiettile con l'aiuto delle autorità di vigilanza). C'è un problema, però: se si dispone di un unico processo che si prende cura dello stato e molti altri processi che ad essa, le prestazioni potrebbero risentirne seriamente. Questo è semplicemente perché il processo può servire solo una richiesta alla volta.

Tuttavia, ci sono modi per superare questo problema, e oggi ci accingiamo a parlare di uno di loro. Soddisfare Erlang termine deposito tavolini o semplicemente ETS, un deposito veloce in memoria che possa ospitare le tuple di dati arbitrari. Come suggerisce il nome, queste tabelle sono stati inizialmente introdotti in Erlang, ma, come con qualsiasi altro modulo di Erlang, facilmente li possiamo usare in Elisir pure.

In questo articolo potrai:

  • Imparare a creare tavoli ETS e opzioni disponibili al momento della creazione.
  • Imparare a eseguire la lettura, scrittura, eliminazione e alcune altre operazioni.
  • Vedi Tavoli ETS in azione.
  • Informazioni sulle tabelle ETS basate su disco e come differiscono dalle tabelle in memoria.
  • Vedere come convertire ETS e DETS avanti e indietro.

Tutti gli esempi di codice funzionano con Elixir 1.4 e 1.5, che è stato recentemente rilasciato.

Introduzione alle tabelle ETS

Come accennato in precedenza, le tabelle ETS sono archiviazione in memoria che contengono tuple di dati (chiamati righe). Più processi possono accedere alla tabella dal relativo id o un nome rappresentato come un atomo ed eseguire la lettura, scrittura, eliminazione e altre operazioni. Tabelle ETS sono create da un processo separato, così se questo processo è terminato, la tabella viene distrutta. Tuttavia, non c'è nessun meccanismo di automatico garbage collection, quindi la tabella può appendere fuori nella memoria per molto tempo.

Dati della tabella ETS sono rappresentati da una tupla {: chiave, value1, value2, valuen}. Si può facilmente cercare i dati dalla chiave o inserire una nuova riga, ma per impostazione predefinita non può essere due righe con la stessa chiave. Le operazioni basate su chiave sono molto veloci, ma se per qualche motivo è necessario produrre un elenco da una tabella ETS e, diciamo, eseguire complesse manipolazioni dei dati, che è possibile anche.

Cosa c'è di più, ci sono basati su disco ETS tavoli disponibili che memorizzano il contenuto in un file. Naturalmente, essi operano più lentamente, ma in questo modo si ottiene un deposito di file semplice senza alcun problema. In cima a quello, ETS in memoria può essere facilmente convertito in basato su disco e viceversa.

Quindi, penso che è il momento di iniziare il nostro viaggio e vedere come vengono create le tabelle ETS!

Creazione di una tabella ETS

Per creare una tabella ETS, impiegare la funzione di nuovo/2. Come stiamo utilizzando un modulo di Erlang, il suo nome dovrebbe essere scritto come un atomo:

Nota che fino a poco tempo è possibile creare solo fino a 1.400 tabelle per ogni istanza del fascio, ma questo non è il caso più — si è solo limitato alla quantità di memoria disponibile.

Il primo argomento passato alla nuova funzione è il nome della tabella (alias), mentre il secondo contiene un elenco di opzioni. La variabile cool_table contiene ora un numero che identifica la tabella nel sistema:

Si può ora utilizzare questa variabile per eseguire le operazioni successive alla tabella (leggere e scrivere dati, ad esempio).

Opzioni disponibili

Parliamo di opzioni che è possibile specificare quando si crea una tabella. Il prima (e un po ' strana) cosa da notare è che per impostazione predefinita non è possibile utilizzare alias della tabella in qualsiasi modo, e fondamentalmente non ha alcun effetto. Ma ancora, l'alias deve essere passato al momento della creazione della tabella.

Per essere in grado di accedere alla tabella tramite il relativo alias, è necessario fornire un:named_table opzione come questo:

A proposito, se si desidera rinominare la tabella, può essere fatto utilizzando la funzione di Rinomina/2:

Successivamente, come già accennato, una tabella non può contenere più righe con la stessa chiave, e questo è dettato dal tipo. Ci sono quattro tipi di tabella possibili:

  • : impostare — che è quella di default. Vuol dire che non può avere più righe con esattamente le stesse chiavi. Le righe non vengono ri-ordinate in alcun modo particolare.
  • : ordered_set — lo stesso :set, ma le righe sono ordinati secondo i termini.
  • :bag — più righe possono avere la stessa chiave, ma le righe non possono essere ancora completamente identiche.
  • : duplicate_bag — righe possono essere completamente identiche.

C'è una cosa degna di nota per quanto riguarda la: ordered_set tabelle. Come documentazione di Erlang dice, queste tabelle trattano chiavi come uguale quando si confronta uguale, non solo quando essi corrispondono. Cosa significa?

Corrispondenza dei due termini in Erlang solo se hanno lo stesso valore e dello stesso tipo. Così il valore integer 1 corrisponde solo un altro numero intero 1, ma non galleggiante 1.0, poiché ci sono diversi tipi. Due termini sono confronta uguale, tuttavia, se neanche hanno lo stesso valore e il tipo o se entrambi sono valori numerici ed estendere allo stesso valore. Questo significa che 1 e 1.0 sono uguali.

Per fornire il tipo della tabella, è sufficiente aggiungere un elemento all'elenco di opzioni:

Un'altra opzione interessante che è possibile passare è: compresso. Vuol dire che i dati all'interno della tabella (ma non le chiavi) sarà — indovinate — conservati in una forma compatta. Naturalmente, le operazioni che vengono eseguite sulla tavola diventerà più lente.

Successivamente, è possibile controllare quale elemento nella tupla deve essere utilizzato come chiave. Per impostazione predefinita, viene utilizzato il primo elemento (posizione 1), ma questo può essere cambiato facilmente:

Ora gli elementi secondo le tuple saranno trattati come le chiavi.

L'ultimo, ma non l'opzione controlla i diritti di accesso della tabella. Questi diritti dettano quali processi sono in grado di accedere alla tabella:

  • :pubblic — qualsiasi processo può eseguire qualsiasi operazione per la tabella.
  • :protected — il valore predefinito. Solo il processo proprietario può scrivere nella tabella, ma possono leggere tutti i processi.
  • :private — solo il processo proprietario può accedere alla tabella.

Così, per fare un tavolo privato, è necessario scrivere:

Va bene, basta con le chiacchiere sulle opzioni — vediamo alcune operazioni comuni che è possibile eseguire per le tabelle!

Le operazioni di scrittura

Per poter leggere qualcosa dalla tabella, è necessario scrivere alcuni dati ci, quindi iniziamo con l'operazione di quest'ultimo. Utilizzare la funzione di inserimento/2 per inserire dati nella tabella:

Si può anche passare una lista di Tuple come questo:

Si noti che se la tabella include un tipo di:set e una nuova chiave corrisponde a uno esistente, i dati precedenti verranno sovrascritti. Allo stesso modo, se una tabella ha un tipo di: ordered_set e una nuova chiave confronta uguale a quella precedente, i dati verranno sovrascritto, quindi prestare attenzione a questo.

L'operazione di inserimento (anche con più tuple in una sola volta) è garantito per essere atomica e isolato, il che significa che sia tutto viene memorizzato nella tabella o niente affatto. Inoltre, altri processi non sarà in grado di vedere il risultato intermedio dell'operazione. Tutto sommato, questo è abbastanza simile a transazioni SQL.

Se siete preoccupati per la duplicazione chiavi o non si desidera sovrascrivere i dati per errore, utilizzare la funzione di insert_new/2. Esso è simile all'inserto/2 ma mai inserirà duplicazione chiavi e invece restituirà falsa. Questo è il caso per il:bag e: tabelle duplicate_bag pure:

Se viene fornito un elenco di tuple, ogni chiave sarà controllato e l'operazione verrà annullata anche se uno dei tasti è duplicato.

Le operazioni di lettura

Grande, ora abbiamo alcuni dati nella nostra tabella — come abbiamo a prenderle? Il modo più semplice è quello di eseguire la ricerca di una chiave:

Ricordate che per la: ordered_set tabella, la chiave deve confrontare uguale al valore fornito. Per tutti gli altri tipi di tabella, dovrebbe corrispondere. Inoltre, se una tabella è una :bag  o un: ordered_bag, la funzione di ricerca/2 potrebbe restituire un elenco con più elementi:

Invece di andare a prendere un elenco, si può afferrare un elemento nella posizione desiderata utilizzando la funzione lookup_element/3:

In questo codice, stiamo ottenendo la riga sotto la chiave: numero e poi prendendo l'elemento in seconda posizione. Funziona perfettamente anche con:bag o: duplicate_bag:

Se si desidera semplicemente controllare se qualche chiave è presente nella tabella, utilizzare il membro/2, che restituisce true o false:

Si può anche ottenere la prima o l'ultima chiave in una tabella utilizzando prima/1 e ultimo/1 rispettivamente:

Come se non bastasse, è possibile determinare la precedente oppure il tasto next, basato su quello fornito. Se tale chiave non viene trovata,: "$end_of_table" verrà restituito:

Si noti, tuttavia, che l'attraversamento di tabella utilizzando funzioni come prima, prossimo, ultimo o prev non è isolata. Vuol dire che un processo può rimuovere o aggiungere altri dati al tavolo mentre si sta scorrendo a esso. Un modo per superare questo problema è utilizzando safe_fixtable/2, che consente di correggere la tabella e assicura che ogni elemento verrà recuperato solo una volta. La tabella rimane fissa a meno che il processo lo rilascia:

Infine, se si desidera trovare un elemento nella tabella e rimuoverlo, utilizzare la funzione di prendere/2:

Le operazioni di eliminazione

Ok, così ora diciamo che non è più necessario al tavolo e il desiderio di sbarazzarsi di esso. Utilizzare Elimina/1 per che:

Naturalmente, è possibile eliminare una riga (o più righe) dalla relativa chiave pure:

Per cancellare l'intera tabella, utilizzare delete_all_objects/1:

E, infine, per trovare e rimuovere un oggetto specifico, utilizzare delete_object/2:

La tabella di conversione

Una tabella ETS può essere convertita in un elenco in qualsiasi momento utilizzando la funzione di tab2list/1:

Ricordate, tuttavia, che il recupero i dati dalla tabella con i tasti è un'operazione molto veloce, e si dovrebbe attenersi ad esso, se possibile.

Inoltre, può scaricare la tabella in un file utilizzando tab2file/2:

Si noti che il secondo argomento deve essere un charlist (una stringa tra virgolette singole).

Ci sono una manciata di altre operazioni disponibili che possono essere applicati alle tabelle di ETS, e naturalmente non ci accingiamo a discutere tutti. Consiglio vivamente di scrematura attraverso la documentazione di Erlang su ETS per saperne di più.

Rendere persistente lo stato con ETS

Per riassumere i fatti che abbiamo imparato finora, modifichiamo un semplice programma che ho presentato nel mio articolo su GenServer. Si tratta di un modulo denominato CalcServer che consente di eseguire calcoli vari inviando le richieste al server o recupero il risultato:

Attualmente il nostro server non supporta tutte le operazioni matematiche, ma si può estendere come necessario. Inoltre, il mio altro articolo spiega come convertire questo modulo a un'applicazione e approfittare delle autorità di vigilanza di prendersi cura di crash del server.

Quello che vorrei fare ora è aggiungere un'altra caratteristica: la capacità di registrare tutte le operazioni matematiche che sono state eseguite con argomento passato. Queste operazioni saranno essere archiviate in una tabella ETS in modo che saremo in grado di prenderla più tardi.

Prima di tutto, modificare la funzione init affinché un nuovo denominato tavolo privato con un tipo di: duplicate_bag viene creato. Stiamo utilizzando: duplicate_bag perché essere eseguite due operazioni identiche con lo stesso argomento:

Ora aggiustare la callback handle_cast in modo che esso registri l'operazione richiesta, prepara una formula e quindi esegue il calcolo effettivo:

Ecco la funzione privata prepare_and_log:

Noi stiamo registrando l'operazione subito (funzione corrispondente sarà presentata in un momento). Restituire la funzione appropriata o zero se non sappiamo come gestire l'operazione.

Per quanto riguarda la funzione di log, dovremmo supportare una tupla (contenente sia il nome dell'operazione che l'argomento) o un atomo (contenente solo il nome dell'operazione, ad esempio: sqrt):

Successivamente, la funzione calculate, che restituisce un risultato corretto o un messaggio di arresto:

Infine, facciamo presente una nuova funzione di interfaccia per recuperare tutte le operazioni svolte dal loro tipo:

Gestire la chiamata:

Ed eseguire la ricerca effettiva:

Ora prova di tutto:

Il risultato è corretto, perché abbiamo effettuato due:aggiungere operazioni con gli argomenti 1 e 2. Naturalmente, si può estendere ulteriormente questo programma come vedete la misura. Ancora, di non abusare di tabelle ETS e li impiegano quando sta davvero per incrementare le prestazioni — in molti casi, utilizzando immutables è una soluzione migliore.

Disco ETS

Prima di finire questo articolo, volevo dire un paio di parole su tabelle ETS basate su disco o semplicemente DETS.

DETS sono abbastanza simili a ETS: utilizzano tabelle per memorizzare i vari dati sotto forma di Tuple. La differenza, come hai indovinato, è che si basano su archiviazione di file invece di memoria e hanno meno funzionalità. DETS hanno funzioni simili a quelli che abbiamo discusso in precedenza, ma alcune operazioni sono eseguite in modo un po' diverso.

Per aprire una tabella, è necessario utilizzare open_file/1 o open_file/2 — non c'è nessuna funzione di nuovo/2 come nei: modulo di :ets. Poiché non abbiamo ancora alcuna tabella esistente, Atteniamoci al open_file/2, che sta andando a creare un nuovo file per noi:

Il nome del file è uguale al nome della tabella per impostazione predefinita, ma questo può essere cambiato. Il secondo argomento passato alla open_file è la lista delle opzioni scritte in forma di Tuple. Ci sono una manciata di opzioni disponibili, come:accesso o:auto_save. Per esempio, per modificare un nome di file, utilizzare la seguente opzione:

Nota che c'è anche un:digitare l'opzione che si può avere uno dei seguenti valori:

  • :set
  • :bag
  • :duplicate_bag

Questi tipi sono le stesse del sistema ETS. Si noti che DETS non può avere un tipo di:ordered_set.

Non c'è nessun:named_table opzione, così è sempre possibile utilizzare il nome della tabella per accedervi.

Un'altra cosa da sottolineare è che le tabelle DETS devono essere chiuse correttamente:

Se non lo fate, la tabella verrà riparata la prossima volta che viene aperto.

Eseguire la lettura e scrivere le operazioni proprio come hai fatto con ETS:

Tenete a mente, però, che DETS sono più lento di ETS perché elisir sarà necessario accedere al disco che, ovviamente, richiede più tempo.

Si noti che è possibile convertire tabelle ETS e DETS avanti e indietro con facilità. Ad esempio, Let's utilizzare to_ets/2 e copiare il contenuto della nostra tabella DETS in memoria:

Copiare il contenuto di ETS DETS utilizzando to_dets/2:

Per riassumere, ETS basato su disco è un modo semplice per memorizzare il contenuto nel file, ma questo modulo è leggermente meno potente di ETS, e le operazioni sono più lente pure.

Conclusione

In questo articolo, abbiamo parlato di ETS e tabelle ETS basate su disco che ci permettono di memorizzare termini arbitrari nella memoria e nei file rispettivamente. Abbiamo visto come creare tali tabelle, quali sono i tipi disponibili, come eseguire la lettura e le operazioni di scrittura, come distruggere tabelle e come convertire altri tipi. Troverete ulteriori informazioni su ETS nella Guida Elixir e sulla pagina ufficiale di Erlang.

Ancora una volta, non eccedere nell'uso di tabelle ETS e cercare di bastone con immutables se possibile. In alcuni casi, tuttavia, ETS può essere una Spinta bella prestazione, quindi sapere su questa soluzione è utile in ogni caso.

Speriamo che avete goduto questo articolo. Come sempre, grazie per essere rimasta con me e davvero ci vediamo presto!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.