sql >> Database >  >> NoSQL >> Redis

Redis jokerteken verwijderscript met EVAL, SCAN en DEL retourneert Schrijfcommando's niet toegestaan ​​na niet-deterministische commando's

UPDATE: het onderstaande is van toepassing op Redis-versies tot 3.2. Vanaf die versie heft op effect gebaseerde replicatie het verbod op niet-determinisme op, zodat alle weddenschappen zijn uitgeschakeld (of beter gezegd, aan).

Je kunt (en mag) de SCAN . niet mixen familie van opdrachten met een schrijfopdracht in een script, omdat het antwoord van de eerste afhankelijk is van de interne Redis-gegevensstructuren die op hun beurt uniek zijn voor het serverproces. Anders gezegd, twee Redis-processen (bijv. master en slave) zullen niet gegarandeerd dezelfde antwoorden retourneren (dus in de Redis-replicatiecontext [die niet op bewerkingen maar op instructies is gebaseerd] zou dit het breken).

Redis probeert zichzelf tegen dergelijke gevallen te beschermen door elk schrijfcommando te blokkeren (zoals DEL ) als het wordt uitgevoerd na een willekeurig commando (bijv. SCAN maar ook TIME , SRANDMEMBER en vergelijkbaar). Ik weet zeker dat er manieren zijn om dat te omzeilen, maar zou je dat willen doen? Onthoud dat je een onbekend terrein betreedt waar het gedrag van het systeem niet is gedefinieerd.

Accepteer in plaats daarvan het feit dat je geen willekeurige lees- en schrijfbewerkingen moet mixen en probeer een andere benadering te bedenken om je probleem op te lossen, namelijk het verwijderen van een aantal sleutels volgens een patroon op een atomaire manier.

Vraag jezelf eerst af of je een van de vereisten kunt versoepelen. Moet het atomair zijn? Atomiciteit betekent dat Redis wordt geblokkeerd voor de duur van de verwijdering (ongeacht de uiteindelijke implementatie) en dat de lengte van de bewerking afhangt van de grootte van de taak (d.w.z. het aantal sleutels dat wordt verwijderd en hun inhoud [het verwijderen van een grote set is duurder dan bijvoorbeeld het verwijderen van een korte string]).

Als atomiciteit geen must is, periodiek/lui SCAN en verwijder in kleine hoeveelheden. Als het een must is, begrijp dan dat je in feite probeert de kwaadaardige KEYS na te bootsen commando :) Maar je kunt het beter doen als je voorkennis van het patroon hebt.

Ervan uitgaande dat het patroon bekend is tijdens de runtime van uw toepassing, kunt u de relevante sleutels verzamelen (bijvoorbeeld in een set) en die verzameling vervolgens gebruiken om de verwijdering op een atomaire en replicatieveilige manier te actualiseren, wat efficiënter is in vergelijking met het doorlopen van de volledige sleutelruimte .

Het meest "moeilijke" probleem is echter of u ad-hoc patroonafstemming moet uitvoeren en tegelijkertijd de atomiciteit moet garanderen. Als dat zo is, komt het probleem neer op het verkrijgen van een gefilterde-patroon-snapshot van de sleutelruimte, onmiddellijk gevolgd door een opeenvolging van verwijderingen (opnieuw benadrukkend:terwijl de database is geblokkeerd). In dat geval kun je heel goed KEYS . gebruiken binnen uw Lua-script en hoop op het beste... (maar heel goed wetende dat u uw toevlucht kunt nemen tot SHUTDOWN NOSAVE vrij snel :P).

De laatste optimalisatie is om de keyspace zelf te indexeren. Beide SCAN en KEYS zijn in feite volledige tabelscans, dus wat als we die tabel zouden indexeren? Stelt u zich eens voor dat u een index houdt op de namen van sleutels die tijdens een transactie kunnen worden opgevraagd - u kunt waarschijnlijk een gesorteerde set en lexicografische bereiken gebruiken (HT @TwBert ) om de meeste behoeften op het gebied van patroonafstemming weg te nemen. Maar tegen een aanzienlijke prijs... u zult niet alleen dubbele boekhouding doen (de naam van elke sleutel opslaan in RAM en CPU), u zou ook genoodzaakt zijn om uw applicatie ingewikkelder te maken. Waarom complexiteit toevoegen? Omdat je om zo'n index te implementeren deze zelf in de applicatielaag (en mogelijk al je andere Lua-scripts) moet onderhouden, waarbij je elke schrijfbewerking zorgvuldig in Redis verpakt in een transactie die ook de index bijwerkt.

Ervan uitgaande dat je dat allemaal hebt gedaan (en rekening houdend met de voor de hand liggende valkuilen zoals de extra complexiteit van het potentieel voor bugs, ten minste verdubbelde schrijfbelasting op Redis, RAM &CPU, beperkingen op schalen, enzovoort...), kun je jezelf een klopje geven op de schouder en feliciteer jezelf voor het gebruik van Redis op een manier waarvoor het niet is ontworpen. Hoewel aankomende versies van Redis wellicht (of misschien niet) betere oplossingen voor deze uitdaging bevatten (@TwBert - wil je een gezamenlijke RCP/bijdrage doen en Redis opnieuw een beetje hacken? ), raad ik u dringend aan om, voordat u dit probeert, de oorspronkelijke vereisten te heroverwegen en te controleren of u Redis correct gebruikt (d.w.z. uw "schema" ontwerpen volgens uw gegevenstoegangsbehoeften).




  1. Mongo DB vindt alle records met de hoogste waarde, afhankelijk van een sleutelveld

  2. blpop stopt na een tijdje met het verwerken van de wachtrij

  3. MongoDB $atan2

  4. Een nieuwe set maken van een bereik van een gesorteerde set in Redis