Transizione da Redis a Redis-cluster

Transizione da Redis a Redis-cluster

Quando si entra in un prodotto che si sviluppa da oltre dieci anni, non sorprende trovare tecnologie obsolete. Ma cosa succede se tra sei mesi devi sostenere un carico dieci volte maggiore e i costi delle interruzioni aumentano di centinaia di volte? In questo caso, hai bisogno di un esperto in Highload davvero capace. Ma in assenza di uno, il problema è stato affidato a me. Nella prima parte dell'articolo, racconterò come siamo passati da Redis a Redis-cluster e nella seconda parte darò consigli su come iniziare a utilizzare il cluster e a cosa prestare attenzione durante l'uso.

Scelta della tecnologia

È così male un Redis separato (redis standalone) in configurazione 1 master e N slave? Perché lo definisco una tecnologia obsoleta?

No, Redis non è così male... Tuttavia, ci sono alcune carenze che non possono essere ignorate.

  • In primo luogo, Redis non supporta meccanismi di ripristino in caso di caduta del master. Per risolvere questo problema, abbiamo utilizzato una configurazione con il failover automatico degli VIP su un nuovo master, cambiando il ruolo di uno degli slave e attivando gli altri. Questo meccanismo funzionava, ma non si poteva definire una soluzione affidabile. In primo luogo, si verificavano falsi allarmi e, in secondo luogo, era monouso, e dopo il suo attivamento erano necessarie azioni manuali per riattivarlo.

  • In secondo luogo, avere un solo master portava a un problema di sharding. Era necessario creare più cluster indipendenti '1 master e N slave', distribuendo manualmente i database su queste macchine e sperando che domani uno dei database non crescesse a tal punto da dover essere spostato su un'istanza separata.

Quali sono le opzioni disponibili?

  • La soluzione più costosa e completa è Redis-Enterprise. Si tratta di una soluzione confezionata con supporto tecnico completo. Anche se sembra ideale dal punto di vista tecnico, per noi non è stata adeguata per motivi ideologici.
  • Redis-cluster. Support for failover and sharding is included out of the box. The interface is almost indistinguishable from the standard version. It looks promising; we will discuss potential pitfalls later.
  • Tarantool, Memcache, Aerospike, and others. All these tools do essentially the same thing. However, each has its own drawbacks. We decided not to put all our eggs in one basket. We use Memcache and Tarantool for other tasks, and, to give you a heads up, we have encountered more issues with them in our experience.

Usage specifics

Let's take a look at the tasks we have historically solved with Redis and the functionalities we utilized:

  • Cache before requests to remote services like 2GIS | Golang

    GET SET MGET MSET "SELECT DB"

  • Cache before MYSQL | PHP

    GET SET MGET MSET SCAN "KEY BY PATTERN" "SELECT DB"

  • Main storage for the session handling and drivers' coordinates service | Golang

    GET SET MGET MSET "SELECT DB" "ADD GEO KEY" "GET GEO KEY" SCAN

As you can see, there's no advanced mathematics involved. So what's the complexity? Let's break down each method individually.

Metodo
Descrizione
Features of Redis-cluster
Soluzione

GET SET
Write/read a key

MGET MSET
Write/read multiple keys
Le chiavi saranno distribuite su diverse nodi. Le librerie pronte sanno eseguire operazioni multi solo all'interno di un singolo nodo.
Sostituire MGET con un pipeline di N operazioni GET.

SELEZIONA DB
Selezionare il database con cui lavoreremo.
Non supporta più database.
Conservare tutto in un solo database. Aggiungere prefissi alle chiavi.

SCAN
Scorrere tutte le chiavi nel database.
Poiché abbiamo un solo database, scorrere tutte le chiavi nel cluster è troppo costoso.
Mantenere l'invariante all'interno di una singola chiave e fare HSCAN su questa chiave. Oppure rinunciare completamente.

GEO
Operazioni con geochiave.
La geochiave non è sharded.

KEY BY PATTERN
Ricerca della chiave per modello.
Poiché abbiamo un solo database, cercheremo tra tutte le chiavi nel cluster. Troppo costoso.
Rinunciare o mantenere l'invariante, come nel caso di SCAN.

Redis vs Redis-cluster

Cosa perdiamo e cosa guadagniamo passando al cluster?

  • Svantaggi: perdiamo la funzionalità di più database.
    • Se vogliamo memorizzare in un unico cluster dati logicamente non correlati, sarà necessario creare 'soluzioni di ripiego' con prefissi.
    • Perdiamo tutte le operazioni 'per database', come SCAN, DBSIZE, CLEAR DB, ecc.
    • Le operazioni multiple sono diventate significativamente più complesse da implementare, poiché potrebbe essere necessario fare riferimento a più nodi.
  • Vantaggi:
    • Resilienza tramite failover del master.
    • Sharding lato Redis.
    • Trasferimento atomico dei dati tra nodi e senza tempi di inattività.
    • Aggiunta e ridistribuzione delle risorse e dei carichi senza interruzioni.

Concluderei che, se non è necessario garantire un alto livello di resilienza, non vale la pena migrare a un cluster, poiché potrebbe non essere un compito banale. Ma se si deve scegliere tra una versione standalone e una cluster, è meglio optare per il cluster, poiché non è in alcun modo inferiore e allevierà parte del vostro mal di testa.

Preparazione per la migrazione

Iniziamo con i requisiti per la migrazione:

  • Deve essere senza soluzione di continuità. Un'interruzione completa del servizio per 5 minuti non è accettabile.
  • Deve essere il più sicura e graduale possibile. Vogliamo avere un certo controllo sulla situazione. Non desideriamo caricare tutto in una volta e pregare sul pulsante di rollback.
  • Minime perdite di dati durante la migrazione. Comprendiamo che sarà molto difficile eseguire una migrazione atomica, quindi ammettiamo una certa desincronizzazione tra i dati nel Redis normale e in quello cluster.

Manutenzione del cluster

Prima di procedere alla migrazione, è importante considerare se possiamo mantenere il cluster:

  • Grafici. Utilizziamo Prometheus e Grafana per il monitoraggio del carico della CPU, della memoria utilizzata, del numero di clienti, delle operazioni GET, SET, AUTH, ecc.
  • Esperienza. Immagina che domani sarà sotto la tua responsabilità un enorme cluster. Se si guasta, nessuno tranne te sarà in grado di ripararlo. Se inizia a rallentare, tutti si rivolgeranno a te. Se è necessario aggiungere risorse o ridistribuire il carico, sarà di nuovo su di te. Per non diventare grigi a 25 anni, è preferibile prevedere queste situazioni e testare in anticipo come si comporterà la tecnologia in determinate condizioni. Ne parleremo più dettagliatamente nella sezione 'Esperienza'.
  • Monitoraggio e notifiche. Quando il cluster si guasta, vogliamo essere i primi a saperlo. Qui ci siamo limitati a notificare che tutti i nodi restituiscono le stesse informazioni sullo stato del cluster (sì, a volte non è così). Altri problemi possono essere notati più rapidamente tramite le notifiche dei servizi client di Redis.

Trasferimento

Come procederemo con il trasferimento:

  • Innanzitutto, è necessario preparare la libreria per lavorare con il cluster. Come base per la versione in Go, abbiamo utilizzato go-redis e abbiamo apportato alcune modifiche. Abbiamo implementato metodi Multi tramite pipeline e abbiamo anche apportato alcune modifiche alle regole di ripetizione delle richieste. Con la versione per PHP ci sono stati più problemi, ma alla fine abbiamo scelto php-redis. Recentemente hanno implementato il supporto per i cluster, e a nostro avviso funziona bene.
  • Successivamente, è necessario distribuire il cluster stesso. Questo si fa letteralmente con due comandi sulla base di un file di configurazione. Discuteremo ulteriormente la configurazione di seguito.
  • Per una migrazione graduale utilizziamo la modalità dry. Poiché abbiamo due versioni della libreria con lo stesso interfaccia (una per la versione standard, l'altra per il cluster), è semplice creare un wrapper che lavori con una versione specifica e al contempo duplichi tutte le richieste nel cluster, confronti le risposte e registri le discrepanze nei log (nel nostro caso su NewRelic). Così, anche se durante il deployment la versione del cluster fallisse, il nostro production non ne risentirebbe.
  • Dopo aver rilasciato il cluster in modalità dry, possiamo tranquillamente osservare il grafico delle discrepanze nelle risposte. Se la percentuale di errori tende lentamente ma costantemente verso una piccola costante, significa che va tutto bene. Perché ci sono comunque delle discrepanze? Perché la registrazione nella versione separata avviene leggermente prima che nel cluster e, a causa dei micro-lag, i dati possono discostarsi. Resta solo da guardare i log delle discrepanze e, se tutte sono spiegabili con la non-atomicità della registrazione, si può andare avanti.
  • Ora è possibile invertire il dry-mode. Scriveremo e leggeremo dal cluster, duplicando in una versione separata. Perché? Nel corso della prossima settimana vogliamo monitorare il funzionamento del cluster. Se dovessimo riscontrare problemi durante i picchi di carico o se ci fosse qualcosa che abbiamo trascurato, abbiamo sempre un rollback d'emergenza sul codice precedente e sui dati attuali grazie al dry-mode.
  • Resta da disattivare il dry-mode e smontare la versione separata.

Esperienza

Iniziamo con una breve panoramica sul funzionamento del cluster.

Per prima cosa, Redis è un archivio chiave-valore. Le chiavi sono stringhe arbitrarie. I valori possono essere numeri, stringhe o intere strutture. Ce ne sono molte, ma per comprendere la struttura generale questo non è fondamentale.
Il livello di astrazione successivo alle chiavi sono gli slot (SLOTS). Ogni chiave appartiene a uno dei 16.383 slot. All'interno di ciascuno slot possono esserci un numero illimitato di chiavi. In questo modo, tutte le chiavi sono suddivise in 16.383 insiemi non sovrapposti.
Transizione da Redis a Redis-cluster

In un cluster devono esserci N master node. Ogni nodo può essere visto come un'istanza separata di Redis, che conosce tutto sugli altri nodi all'interno del cluster. Ogni master node contiene un certo numero di slot. Ogni slot appartiene a una sola master node. Tutti gli slot devono essere distribuiti tra i nodi. Se alcuni slot non sono distribuiti, le chiavi memorizzate in essi non saranno accessibili. Ha senso avviare ogni master node su una macchina logica o fisica separata. Inoltre, è importante ricordare che ogni nodo funziona solo su un core, e se desideri avviare più istanze di Redis su una sola macchina logica, assicurati che funzionino su core diversi (non abbiamo provato, ma in teoria dovrebbe funzionare). Fondamentalmente, le master node forniscono una normale partizione, e un numero maggiore di master node consente di scalare le richieste di lettura e scrittura.

Una volta che tutte le chiavi sono distribuite tra i slot e gli slot sono distribuiti tra i master node, è possibile aggiungere un numero arbitrario di slave node a ciascun master node. All'interno di ciascuna di queste coppie "master-slave", funzionerà la replica normale. Gli slave servono per scalare le richieste di lettura e per il failover in caso di malfunzionamento del master.
Transizione da Redis a Redis-cluster

Ora parliamo delle operazioni che è meglio sapere fare.

Ci riferiremo al sistema tramite Redis-CLI. Poiché Redis non ha un unico punto di accesso, le operazioni seguenti possono essere eseguite su qualsiasi nodo. In ogni punto, sottolineo la possibilità di eseguire l'operazione sotto carico.

  • La prima e più importante operazione di cui avremo bisogno è: cluster nodes. Restituisce lo stato del cluster, mostra un elenco dei nodi, i loro ruoli, la distribuzione degli slot, ecc. Ulteriori informazioni possono essere ottenute con cluster info e cluster slots.
  • È importante sapere come aggiungere e rimuovere le nodi. A questo scopo, ci sono le operazioni cluster meet e cluster forget. Si noti che cluster forget deve essere applicato a OGNI nodo, sia ai master che ai replica. Al contrario, è sufficiente invocare cluster meet su un solo nodo. Questa distinzione può essere disorientante, quindi è meglio conoscerla prima di mettere in produzione il cluster. L'aggiunta di un nodo viene eseguita in modo sicuro in produzione e non influisce sul funzionamento del cluster (cosa logica). Tuttavia, se si intende rimuovere un nodo dal cluster, è fondamentale assicurarsi che non ci siano slot rimasti (altrimenti si rischia di perdere l'accesso a tutte le chiavi su quel nodo). Inoltre, non rimuovere un master che ha slave, altrimenti si attiverà una votazione non necessaria per un nuovo master. Se non ci sono più slot sui nodi, è un problema minore, ma perché complicarsi la vita, se si può prima rimuovere gli slave.
  • Se è necessario scambiare forzatamente master e slave, si può usare il comando cluster failover. Chiamandolo in azione, è importante tenere presente che durante l'esecuzione dell'operazione il master sarà inattivo. Di solito, lo switch avviene in meno di un secondo, ma non in modo atomico. Puoi aspettarti che parte delle richieste al master durante questo tempo terminerà con un errore.
  • Prima di rimuovere un nodo dal cluster, non devono rimanere slot. È meglio ridistribuirli usando il comando cluster reshard. Gli slot verranno trasferiti da un master all'altro. L'intera operazione può richiedere alcuni minuti, a seconda della quantità di dati da trasferire, ma il processo di trasferimento è sicuro e non influisce sul funzionamento del cluster. Pertanto, è possibile trasferire tutti i dati da un nodo all'altro sotto carico, senza preoccuparsi della loro disponibilità. Tuttavia, ci sono alcune sottigliezze. In primo luogo, il trasferimento dei dati comporta un certo carico sul nodo destinatario e su quello mittente. Se il nodo destinatario è già molto carico di CPU, non è consigliabile sovraccaricarlo anche con l'accettazione di nuovi dati. In secondo luogo, non appena non ci saranno più slot sul master mittente, tutti i suoi slave passeranno immediatamente al master su cui questi slot sono stati trasferiti. E il problema è che tutti questi slave vorranno sincronizzare i dati contemporaneamente. E sarai fortunato se si tratta di una sincronizzazione parziale e non totale. Tieni presente ciò e combina le operazioni di trasferimento degli slot con la disattivazione/trasferimento degli slave. Oppure spera di avere un margine di sicurezza sufficiente.
  • Cosa fare se durante il trasferimento ti accorgi di aver perso dei slot? Spero che questo problema non ti riguardi, ma in caso contrario, c'è l'operazione cluster fix. Essa redistribuirà gli slot tra i nodi in modo casuale. Ti consiglio di verificarne il funzionamento, rimuovendo in precedenza il nodo con gli slot distribuiti dal cluster. Dato che i dati negli slot non distribuiti non sono comunque disponibili, ora è tardi per preoccuparsi dei problemi di accessibilità di quegli slot. D'altra parte, l'operazione non influisce sugli slot distribuiti.
  • Un'altra operazione utile è monitor. Essa consente di vedere in tempo reale l'intero elenco delle richieste che arrivano al nodo. Inoltre, è possibile utilizzare grep per verificare se è presente il traffico desiderato.

Vale la pena menzionare anche la procedura di failover del master. In breve, sì, esiste, e, a mio avviso, funziona molto bene. Tuttavia, non bisogna pensare che scollegando la spina da una macchina con nodo master, Redis commuti immediatamente e i client non notino la perdita. Dalla mia esperienza, il passaggio avviene in pochi secondi. Durante questo tempo, parte dei dati sarà inaccessibile: viene rilevata l'inaccessibilità del master, i nodi votano per un nuovo master, gli slave si commutano, i dati si sincronizzano. Il modo migliore per assicurarsi personalmente che lo schema funzioni è condurre esercitazioni locali. Avvia un cluster sul tuo laptop, fornisci un carico minimo, simula un guasto (ad esempio, bloccando le porte) e valuta la velocità di commutazione. A mio avviso, solo giocando in questo modo per un giorno o due si può essere certi del funzionamento della tecnologia. Oppure, si può sperare che il software utilizzato dalla metà di Internet funzioni sicuramente.

Configurazione

Spesso, la configurazione è la prima cosa necessaria per iniziare a utilizzare lo strumento. E quando tutto funziona, non si desidera toccare la configurazione. Ci vuole un certo impegno per costringersi a tornare alle impostazioni e rivederle attentamente. Nella mia esperienza, abbiamo avuto almeno due grandi fallimenti a causa di scarsa attenzione alla configurazione. Prestate particolare attenzione ai seguenti punti:

  • timeout 0
    Il tempo dopo il quale le connessioni inattive vengono chiuse (in secondi). 0 — non vengono chiuse
    Non tutte le nostre librerie riuscivano a chiudere correttamente le connessioni. Disattivando questa impostazione, rischiamo di raggiungere il limite massimo di clienti. D'altra parte, se esiste un problema simile, la disconnessione automatica delle connessioni perse lo maschererà e potremmo non accorgercene. Inoltre, non dovremmo attivare questa impostazione quando si usano le connessioni persistenti.
  • Save x y & appendonly yes
    Salvataggio dello snapshot RDB.
    Discuteremo in dettaglio dei problemi RDB/AOF più avanti.
  • stop-writes-on-bgsave-error no & slave-serve-stale-data yes
    Se attivato, quando si verifica un guasto all'RDB snapshot, il master smetterà di ricevere richieste di modifica. Se la connessione con il master viene persa, lo slave può continuare a rispondere alle richieste (sì). Oppure smetterà di rispondere (no)
    Non ci soddisfa una situazione in cui Redis si trasforma in una zucca.
  • repl-ping-slave-period 5
    Dopo questo intervallo di tempo, inizieremo a preoccuparci del fatto che il master sia guasto e che sia il momento di eseguire la procedura di failover.
    Dovremo trovare manualmente un equilibrio tra falsi allarmi e avvio del failover. Nella nostra esperienza, sono 5 secondi.
  • repl-backlog-size 1024mb & epl-backlog-ttl 0
    Esattamente quanti dati possiamo memorizzare nel buffer per una replica che si è disconnessa. Se il buffer finisce, sarà necessario completare la sincronizzazione.
    L'esperienza suggerisce che è meglio impostare un valore maggiore. Le ragioni per cui la replica può iniziare a ritardare sono molte. Se è in ritardo, molto probabilmente il tuo master sta già facendo fatica a gestire e la completa sincronizzazione sarà l'ultima goccia.
  • maxclients 10000
    Il numero massimo di clienti simultanei.
    Dalla nostra esperienza, è meglio impostare un valore più alto. Redis gestisce benissimo 10.000 connessioni. Assicurati solo che ci siano abbastanza socket nel sistema.
  • maxmemory-policy volatile-ttl
    La regola che determina quali chiavi vengono rimosse al raggiungimento del limite di memoria disponibile.
    Qui è importante non solo la regola, ma comprendere come avverrà. Redis merita elogi per la sua capacità di operare normalmente al raggiungimento del limite di memoria.

Problemi RDB e AOF

Sebbene Redis memorizzi tutte le informazioni in memoria, esiste anche un meccanismo per salvare i dati su disco. In particolare, tre meccanismi:

  • RDB-snapshot — uno snapshot completo di tutti i dati. Viene impostato tramite la configurazione SAVE X Y e viene letto come «Salvare uno snapshot completo di tutti i dati ogni X secondi, se almeno Y chiavi sono state modificate».
  • Append-only file — un elenco delle operazioni nell'ordine in cui vengono eseguite. Aggiunge le nuove operazioni nel file ogni X secondi o ogni Y operazioni.
  • RDB e AOF — combinazione delle due precedenti.

Tutti i metodi hanno i loro vantaggi e svantaggi; non elencherò tutti, ma voglio mettere in evidenza alcuni aspetti che, a mio avviso, non sono ovvi.

Innanzitutto, per salvare lo snapshot RDB è necessario chiamare FORK. Se ci sono molti dati, questo può bloccare l'intero Redis per un periodo che va da alcune millisecondi a un secondo. Inoltre, il sistema necessita di allocare memoria per tale snapshot, il che porta alla necessità di mantenere una doppia scorta di memoria RAM sulla macchina logica: se a Redis sono allocati 8 GB, allora sulla macchina virtuale deve essere disponibile un totale di 16 GB.

In secondo luogo, ci sono problemi con la sincronizzazione parziale. In modalità AOF, durante il ricollegamento di uno slave, può essere eseguita una sincronizzazione completa invece di una parziale. Non sono riuscito a capire perché ciò accada. Ma è importante ricordarsene.

Questi due punti ci fanno già riflettere su quanto siano necessari questi dati su disco, considerando che tutto è già duplicato dai server secondari. I dati possono andare persi solo se tutti i server secondari falliscono, e questo è un problema di livello «incendio nel DC». Come compromesso, potremmo considerare di conservare i dati solo sui server secondari, ma in questo caso bisogna assicurarsi che questi server non diventino mai un master durante un ripristino di emergenza (esiste un'impostazione di priorità dei server secondari nella loro configurazione). Ogni volta valutiamo se sia necessario conservare i dati su disco e nella maggior parte dei casi rispondiamo «no».

Conclusione

In conclusione, spero di aver fornito una panoramica generale sul funzionamento di redis-cluster a coloro che non ne hanno mai sentito parlare, e di aver messo in evidenza alcuni aspetti non ovvi per chi già lo utilizza da tempo.
Grazie per il tempo dedicato e, come al solito, sono benvenuti commenti in merito.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster