Redis
 sql >> Database >  >> NoSQL >> Redis

Lo script di eliminazione con caratteri jolly Redis utilizzando EVAL, SCAN e DEL restituisce comandi di scrittura non consentiti dopo comandi non deterministici

AGGIORNAMENTO: quanto segue si applica alle versioni Redis fino alla 3.2. Da quella versione, la replica basata sugli effetti revoca il divieto di non determinismo, quindi tutte le scommesse sono disattivate (o meglio, attivate).

Non puoi (e non dovresti) mescolare lo SCAN famiglia di comandi con qualsiasi comando di scrittura in uno script perché la risposta del primo dipende dalle strutture dati Redis interne che, a loro volta, sono univoche per il processo del server. In altre parole, non è garantito che due processi Redis (ad esempio master e slave) restituiscano le stesse risposte (quindi nel contesto di replica Redis [che non è basato su operazioni ma su istruzioni] ciò lo interromperebbe).

Redis cerca di proteggersi da tali casi bloccando qualsiasi comando di scrittura (come DEL ) se viene eseguito dopo un comando casuale (es. SCAN ma anche TIME , SRANDMEMBER e simili). Sono sicuro che ci sono modi per aggirare il problema, ma vorresti farlo? Ricorda, andrai in un territorio sconosciuto in cui il comportamento del sistema non è definito.

Accetta invece il fatto che non dovresti mischiare letture e scritture casuali e prova a pensare a un approccio diverso per risolvere il tuo problema, ovvero eliminare un gruppo di chiavi secondo uno schema in modo atomico.

Per prima cosa chiediti se puoi rilassare qualcuno dei requisiti. deve essere atomico? Atomicità significa che Redis sarà bloccato per la durata dell'eliminazione (indipendentemente dall'implementazione finale) e che la durata dell'operazione dipende dalla dimensione del lavoro (cioè dal numero di chiavi che vengono eliminate e dal loro contenuto [l'eliminazione di un set di grandi dimensioni è più costoso dell'eliminazione di una stringa breve, ad esempio]).

Se l'atomicità non è un must, periodicamente/pigramente SCAN ed eliminare in piccoli lotti. Se è d'obbligo, capisci che stai fondamentalmente cercando di emulare le CHIAVI KEYS command :) Ma puoi fare di meglio se hai una conoscenza preliminare del modello.

Supponendo che il modello sia noto durante il runtime dell'applicazione, puoi raccogliere le chiavi pertinenti (ad esempio in un set) e quindi utilizzare quella raccolta per attualizzare l'eliminazione in modo atomico e sicuro per la replica, più efficiente rispetto all'analisi dell'intero spazio delle chiavi .

Tuttavia, il problema più "difficile" è se è necessario eseguire la corrispondenza dei modelli ad hoc garantendo l'atomicità. In tal caso, il problema si riduce all'ottenimento di un'istantanea filtrata per modello dello spazio delle chiavi seguita immediatamente da una successione di eliminazioni (sottolineatura:mentre il database è bloccato). In tal caso puoi benissimo usare KEYS all'interno del tuo script Lua e spera per il meglio... (ma sapendo benissimo che potresti ricorrere a SHUTDOWN NOSAVE abbastanza rapidamente :P).

L'ultima ottimizzazione consiste nell'indicizzare lo spazio delle chiavi stesso. Entrambi SCAN e KEYS sono fondamentalmente scansioni di tabelle complete, quindi cosa accadrebbe se dovessimo indicizzare quella tabella? Immagina di mantenere un indice sui nomi delle chiavi che possono essere interrogati durante una transazione:probabilmente puoi utilizzare un set ordinato e intervalli lessicografici (HT @TwBert ) per eliminare la maggior parte delle esigenze di corrispondenza dei modelli. Ma a un costo significativo ... non solo farai una doppia contabilità (memorizzazione dei costi dei nomi di ciascuna chiave nella RAM e nella CPU), saresti costretto ad aggiungere complessità alla tua applicazione. Perché aggiungere complessità? Perché per implementare un tale indice dovresti mantenerlo tu stesso nel livello dell'applicazione (e possibilmente tutti gli altri script Lua), avvolgendo accuratamente ogni operazione di scrittura su Redis in una transazione che aggiorni anche l'indice.

Supponendo che tu abbia fatto tutto questo (e tenendo conto delle ovvie insidie ​​come il potenziale di bug della complessità aggiunta, almeno il carico di scrittura raddoppiato su Redis, RAM e CPU, restrizioni sul ridimensionamento e così via...) puoi darti una pacca sul spalla e congratulati con te stesso per aver utilizzato Redis in un modo per cui non è stato progettato. Sebbene le prossime versioni di Redis possano (o meno) includere soluzioni migliori per questa sfida (@TwBert - vuoi fare un RCP/contrib congiunto e hackerare ancora un po' Redis? ), prima di provare questo ti esorto davvero a ripensare ai requisiti originali e verificare che stai utilizzando Redis correttamente (ovvero progettando il tuo "schema" in base alle tue esigenze di accesso ai dati).