En av de saker som ofta förvirrar användare som är bekanta med andra databaser när de provar Redis, är bristen på insyn i databasen:Det finns ingen uppsättning tabeller eller samlingar till se, bara ett vanligt, platt nyckelutrymme som (potentiellt) kan ha miljontals nycklar. Möjligheten att iterera billigt över detta nyckelutrymme blir därför mycket viktigt för att bekanta sig med databasens innehåll.
Att iterera över Redis-nyckelutrymmet har andra viktiga användningsfall som också kommer att tänka på är:
- sopsamling eller städning av nycklar som matchar ett visst mönster
- dataförflyttning och schemaändringar eller flytta en viss uppsättning nycklar till en annan datastruktur
- felsökning, datasampling, datafixar eller hitta och fixa alla nycklar som förstördes av en nyligen genomförd ändring
I det här inlägget kommer vi att gräva djupt i olika viktiga rymditerationsalternativ som är tillgängliga i Redis.
O(N) Iteratorer:NYCKLAR
Kommandot Redis KEYS returnerar alla nycklar i databasen som matchar ett mönster (eller alla nycklar i nyckelutrymmet). Liknande kommandon för att hämta alla fält lagrade i en hash är HGETALL och för alla att hämta medlemmarna i en SMEMBERS. Själva nycklarna i Redis lagras i en ordbok (aka en hashtabell). Kommandot KEYS fungerar genom att iterera över denna ordbok och skicka ut allt som matchar mönstret som ett enda Array-svar. De andra kommandona fungerar på liknande sätt.
Utförandet av en sådan operation beror på storleken på samlingen, dvs O(N). Därför avråds användningen av KEYS kraftigt i produktionsmiljöer med ett stort antal nycklar. Redis är enkelgängad, blockeras under denna iteration och blockerar därmed andra operationer. Därför bör KEYS endast användas för felsökning och andra speciella tillfällen där prestanda inte är ett problem (som när databasen har kopplats till offline för att tillämpa en datafix). En annan viktig sak att komma ihåg med denna algoritm är att den skickar ut alla matchande nycklar tillsammans som ett enda svar. Detta kan vara extremt praktiskt när nyckelutrymmet är litet men kommer att skapa flera problem på ett stort nyckelutrymme. KEYS är dock ett favoritkommando bland utvecklare i sina egna dev-miljöer.
Nycklar i aktion:
127.0.0.1:6379[1]> MSET en 1 två 2 tre 3 fyra 4OK# Alla nycklar127.0.0.1:6379[1]> nycklar *1) "fyra"2) "tre"3) " två"4) "ett"# nycklar som börjar med bokstaven 't'127.0.0.1:6379[1]> tangenter t*1) "tre"2) "två"# nycklar som har ett 'ee' i sig127. 0.0.1:6379[1]> nycklar *ee*1) "tre"
Markörbaserade iteratorer:SCAN
SCAN och dess systerkommandon, SSCAN (för uppsättningar), HSCAN (för hash) och ZSCAN (för sorterade uppsättningar) tillhandahåller den markörbaserade metoden för att iterera över Redis-datastrukturer. De har varit tillgängliga i Redis sedan 2.8.0.
Nycklar returneras i inkrementella iterationer med konstant tidsgaranti för varje iteration. En markör (ett heltal är det här fallet) returneras när iterationerna initieras och en uppdaterad markör returneras och varje iteration. En iterationscykel börjar när markören är inställd på 0 i SCAN-begäran och avslutas när markören som returneras av servern är 0. På grund av nyanser av Redis-arkitekturen och marköralgoritmimplementeringen är här några egenheter med detta tillvägagångssätt:
- En fullständig iteration hämtar alltid alla element som fanns i samlingen från början till slutet av en fullständig iteration.
- En fullständig iteration returnerar aldrig något element som INTE fanns i samlingen från början till slutet av en fullständig iteration.
- Ett givet element kan returneras flera gånger. Det är upp till applikationen att hantera fallet med duplicerade element
- Element som inte ständigt fanns i samlingen under en fullständig iteration, kan returneras eller inte:det är odefinierat.
- Ett antal element som returneras under varje räkning varierar och kan också vara 0. Men iterationen är inte klar förrän servern returnerar markörvärdet 0.
- COUNT alternativet kan användas för att begränsa antalet element som returneras i varje iteration. Standardvärdet är 10. Det anses dock bara vara ett förslag och tillämpas inte i alla fall. COUNT-värdet kan ändras under varje iterationsanrop.
- MATCH alternativet tillåter specificering av mönster som kommandot KEYS tillåter.
- Markörimplementeringen är helt tillståndslös på serversidan. Det tillåter (potentiellt) oändliga iterationer att starta parallellt. Det finns heller inga krav för att säkerställa att en iteration fortsätter till slutet och kan stoppas när som helst.
Trots dess särdrag är SCAN ett mycket användbart kommando och det rätta kommandot för att välja mellanslagsupprepningar för de flesta användningsfall.
SCAN är ett mycket användbart kommando och det rätta kommandot att välja för nyckelmellanslagsupprepningar i #RedisClick To TweetScan in Action
127.0.0.1:6379[1]> flushdbOK127.0.0.1:6379[1]> nycklar *(tom lista eller uppsättning)127.0.0.1:6379[1]> debug fyller i 33OK127.0.0.1:6379[ 1]> skanna 0 COUNT 51) "4"2) 1) "key:1" 2) "key:9" 3) "key:13" 4) "key:29" 5) "key:23"127.0. 0.1:6379[1]> skanna 4 1) "42"2) 1) "key:24" 2) "key:28" 3) "key:18" 4) "key:16" 5) "key:12 " 6) "key:2" 7) "key:6" 8) "key:31" 9) "key:27" 10) "key:19"127.0.0.1:6379[1]> skanna 421) "9 "2) 1) "key:3" 2) "key:4" 3) "key:20" 4) "key:8" 5) "key:32" 6) "key:5" 7) "key:26" 8) "key:10" 9) "key:21" 10) "key:14"127.0.0.1:6379[1]> skanna 9 COUNT 1001) "0"2) 1) "key:25" 2 ) "key:30" 3) "key:22" 4) "key:17" 5) "key:15" 6) "key:0" 7) "key:11" 8) "key:7"Under huven
Algorithmen som SCAN (och dess systerkommandon) använder för att skanna igenom är spännande och leder till några av egenskaperna hos kommandot som vi beskrev ovan. Antirez beskrev det på hög nivå i sitt blogginlägg och det förklaras (något bättre) i kommentarerna ovanför implementeringen (funktion dictScan). Att beskriva det i detaljer kommer att göra det här inlägget för långt så jag kommer att ge tillräckligt med beskrivning för att göra det tydligt.
- De flesta Redis-datastrukturer representeras internt som ordböcker (åtminstone delvis när det gäller sorterade uppsättningar). De är implementerade som kraftfulla hashtabeller i två storlekar med kedja för kollisioner. Utmaningen med att skriva en markörbaserad iterativ algoritm här är att kunna hantera tillväxt och krympning av hashen utan att offra Redis-principerna om enkelhet (i API) och hastighet.
- SCAN skannar i princip ett gäng hash-buckets varje iteration och returnerar de element som matchar mönstret i dessa. Eftersom det bara tittar på en fast lista med hinkar kan det hända att iterationer inte returnerar några värden alls.
- Markören som returneras är i princip en förskjutning till hashtabellen som itereras. Det handlar om tillväxt och krympning av hashtabeller (d.v.s. rehashing) genom smart manipulation av bitar på högre nivåer av offseten samtidigt som offseten ökar tillsammans med hashtabellens egenskaper. Implikationer från detta tillvägagångssätt är att nya element som läggs till under iterationen kan eller inte kan returneras. Markören själv skulle dock inte behöva starta om vid en förändring i storleken på hashtabellen.
- En given hink måste besökas bara en gång och alla dess nycklar måste returneras på en gång. Detta är återigen för att säkerställa att storleksändring av hash (d.v.s. omhasning) inte komplicerar iterationsförloppet. Detta leder dock till att argumentet COUNT inte är strikt genomförbart.
- Eftersom ovanstående tillvägagångssätt är helt tillståndslöst på serversidan, innebär det i princip att iterationer kan stoppas eller ett stort antal iterationer kan startas parallellt utan ökad minnesanvändning.
Sammanfattning
På en hög nivå finns två alternativ att iterera över Redis-nyckelutrymmet:
- Använd KNAPPAR när prestanda inte är ett problem eller när tangentutrymmet är rimligt stort.
- Använd SCAN vid alla andra tillfällen.
Visste du att vi nu stöder hosting för Redis™*? Få fullständigt hanterad hosting för Redis™ i säkerheten för ditt eget molnkonto och utnyttja AWS/Azure-krediter för dina Redis™-distributioner.