Hi,
Ich erstelle eine Webapp, die sortierte Sets intensiv nutzt. Oft muss ich Teilbereiche von Mengen schneiden, also verwende ich zrevrangebyscore und zinterstore. Sie funktionieren großartig. Da ich jedoch nur Teilbereiche von gegebenen Mengen schneiden muss und keine vollständigen Mengen, muss ich zuerst die Teilbereiche mit zrevrangebyscore abfragen und die Ergebnisse in einem temporären Schlüssel speichern. Dann kann ich zinterstore verwenden, um die temporären Schlüssel zu überschneiden. Anfangs habe ich den ersten Schritt im Speicher in der Client-Anwendung gemacht. Jetzt verwende ich dieses Lua-Skript:
local t = redis.call('zrangebyscore', KEYS[1], ARGV[1], ARGV[2], 'withscores')
local i=1
while(i<=#t) do
redis.call('zadd', KEYS[2], t[i+1], t[i])
i=i+2
end
return #t/2
Dann rufe ich das Skript auf: eval script 2 original_key result_key min max
Also passiert alles in redis. Es wäre jedoch großartig, eine optionale STORE-Option im zrangebyscore-Befehl zu haben, da die Ausführung dieses Skripts je nach Zset-Größe zu Tausenden von zadd-Aufrufen führen könnte. Vielleicht fehlt mir eine bessere Möglichkeit, dies zu tun, wenn dies der Fall ist, weisen Sie mich bitte auf eine bessere Lösung hin. Ich weiß zum Beispiel nicht, ob es möglich/empfohlen ist, zadd mit vielen Score+Member-Paaren innerhalb des lua-Skripts zu verwenden. Dies geschieht unter Verwendung der Variadic-Funktionalität dieses Befehls.
Danke und herzlichen Glückwunsch zum Redis :)
Ich stimme für diesen Vorschlag. Die Verwendung von Zadd zum Durchqueren großer Sets ist zu langsam.
Ich interessiere mich auch für diese Funktion.
+1
+1
Arbeiten Sie an einem Projekt, das diese Funktion stark nutzen könnte.
+1
Ich habe einen Weg gefunden, die Variadic-Version von zadd zu verwenden. Der Trick besteht darin, unpack() zu verwenden.
local t = redis.call('zrangebyscore', KEYS[1], ARGV[1], ARGV[2], 'withscores')
local i = 1
for i=1, #t-1, 2 do
t[i],t[i+1] = t[i+1],t[i] # first we change the order of the elements in the table
end
if #t > 0 then redis.call("zadd", key, unpack(t)) end
Die Funktion unpack() fügt die gesamte Tabelle zum Stack hinzu, so dass es so ist, als würde man redis.call("zadd", key, t[1], t[2], t[3],.......) aufrufen. . Es gibt jedoch Beschränkungen in der Stackgröße, so dass Sie die Fehlermeldung "too many results to unpack" erhalten, wenn die Tabelle zu groß ist. Endlich habe ich diese Funktion, die die variadische Version des Befehls in Slices von 1000 Elementen aufruft
local function massive_redis_command(command, key, t)
local i = 1
local temp = {}
while(i <= #t) do
table.insert(temp, t[i+1])
table.insert(temp, t[i])
if #temp >= 1000 then
redis.call(command, key, unpack(temp))
temp = {}
end
i = i+2
end
if #temp > 0 then
redis.call(command, key, unpack(temp))
end
end
Warum 1000 Elemente in jedem Slice? Nun, ich habe festgestellt, dass im Lua-Quellcode eine DEFAULT_STACK_SIZE auf 1024 gesetzt ist http://www.lua.org/source/4.0.1/src_llimits.h.html
+2
+1
+1
+1
+1
Es scheint ein anständiges Interesse an dieser Funktion zu bestehen, also werde ich es versuchen, wenn die Redis-Entwickler der Meinung sind, dass es eine Ergänzung wert ist. Ich nehme an, es gibt Bedenken, ZRANGEBYSCORE
und ZREVRANGEBYSCORE
zu kompliziert zu machen, aber es könnte so gemacht werden, als ob der Anruf auf die folgenden zwei Arten gültig sein könnte:
Z[REV]RANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
Z[REV]RANGEBYSCORE key min max [STORE] key [LIMIT offset count]
Davon abgesehen würden diese Befehle in Bezug auf den Rückgabetyp variadisch, und ich habe das Gefühl, dass dies verboten ist (zumindest fällt mir kein anderer Befehl ein, der dies tut). Eine zweite Alternative wäre, dies in einem separaten Befehl zu tun (möglicherweise ZRANGEBYSCORESTORE
was Sinn macht, aber wirklich lang ist :smiley:
Danke schön
Mike
+1 für das Hinzufügen von SORT
-ähnlicher STORE
Funktionalität für die Z[REV]RANGE*-Befehlsfamilie.
@michael-grunder ein unbekannter Antworttyp ist problematisch, daher gibt es trotz der syntaktischen Einfachheit von STORE
in meinem Kopf (:)) kein Entkommen, stattdessen die Z*STORE-Äquivalente hinzuzufügen.
@gimenete danke für den Lua-Workaround. Ich habe einen ähnlichen Anwendungsfall, bei dem ich Sequenzen von ZRANGE*
und ZINTERSTORE
ausführen muss und die Kosten für den Roundtrip nicht akzeptabel sind.
Noch ein +1 von mir :)
Aus Neugier @antirez , haben Sie eine allgemeine Syntax zum Speichern des Abfrageergebnisses in einem Schlüssel in Betracht gezogen?
+1 und danke für die massive_redis_command
Funktion
+1, @sunheehnus - super!, wir freuen uns
+1
Auf jeden Fall +1 dazu.
+1
+1
+1
+1
Hallo @sunheehnus , danke für die Bereitstellung eines Patches, der dies implementiert. Momentan überlege ich, ob es eine gute Idee ist, diese Befehle hinzuzufügen oder nicht. Kann ich ein Feedback dazu erhalten, warum Sie sich für die neuen Befehle entschieden haben, anstatt nur eine Option STORE
erstellen? Vielleicht, weil andere Befehle diese Form haben (mit Ausnahme von SORT und einigen, die STORE verwenden)? Vielleicht können wir, wenn wir dies als STORE-Option implementieren, weniger Code schreiben, nur denjenigen, der den STORE optional implementiert, damit es nicht zu Duplizierungen kommt. Ein weiterer Grund, warum Sie diesen Weg eingeschlagen haben, könnte vielleicht Optimierung sein.
Hallo @antirez ,
Ich dachte immer, dass, wenn wir die Option SOTRE
, wie @itamarhaber sagte, sie einen anderen Rückgabetyp (Array und Integer) haben kann, aber ich finde, dass der Befehl SORT
Array zurückgeben kann oder Integer, und ein großer Teil meines Patches ist eine Kopie von ZRANGEBYSCORE
. :-) Vielleicht ist die Option STORE
also ein besserer Weg, um die Codeduplizierung zu vermeiden.
@gimenete
In Redis 3.2.4 ist die Lua-Version 5.1
127.0.0.1:6379> EVAL "return _VERSION" 0
"Lua 5.1"
Auspacken maximale Größe ist 7999
http://www.lua.org/source/5.1/luaconf.h.html#LUAI_MAXCSTACK
LUAI_MAXCSTACK
definiert 8000
+1
Ich schlage einen neuen Befehl vor
ZSTOREBYSCORE Zieltaste min max
Diese Option wäre für Mengenoperationen nützlich. Ich möchte Schnittmengen von Mengen durchführen, aber ich möchte mit einer Teilmenge von einer beginnen, indem ich zuerst nach ihrer Punktzahl filtere und die Punktzahl der zweiten behalte. GEWICHTE wären für die letztere Operation nützlich. Ich bin dafür, nicht viele Daten zurückzugeben, wenn das Ergebnis in Redis in einem Zielschlüssel gespeichert wird.
Mein Anwendungsfall ist, dass ich Joins durchführen möchte, und sortierte Sets in Kombination mit der Fähigkeit von Redis, mit mehreren Schlüsseln zu arbeiten, ermöglichen dies. Ich möchte in der Lage sein, Schnittmengen an sortierten Sets mit der Partitur und nicht mit den Elementen selbst durchzuführen. Aber um die erste Filterung auf dem Index durchzuführen, möchte ich in der Lage sein, die Punktzahl (zstorebyscore) und dann die Schnittpunkte (zinterstore) anzugeben.
+1
+1
@gimenete danke für die Funktion massive_redis_command
.
+1
+1
+1
+1
+1
+1
+1
Ich würde das gerne noch einmal durchgehen, eine schnelle Entscheidung über die API treffen und hoffentlich einen PR für redis 6.2 zusammenführen.
Wenn man sich die 2 PRs ansieht, fügt einer ein neues z[rev]rangebyscorestore
(mit viel Codeduplizierung), und der andere fügt den vorhandenen Befehlen ein STORE
Argument hinzu.
Das Problem beim zweiten Ansatz besteht darin, dass er den bisher schreibgeschützten Befehl in einen Schreibbefehl ändert und auch die Antwortart des Befehls von den Argumenten abhängt.
wir haben in der Tat bereits mehrere andere Befehle dieser Art (SORT und GEORADIUS), aber sie verursachen einige Schmerzen (zB wurde GEORADIUS_RO später hinzugefügt, um das zu mildern).
Ich denke, der richtige Ansatz besteht darin, einen neuen Befehl zu verwenden und sicherzustellen, dass die Implementierung den Code teilt, wie es SUNION und SUNIONSTORE tun.
@redis/core-team oder jemand anderen, bitte teilen Sie Ihr Feedback mit.
+1 für z[rev]rangebyscorestore
als einzelner Befehl, um konsistent mit zunionstore
zu bleiben, da es einfacher ist, zwischen Lese-Schreib- und Nur-Lese-Befehlen zu unterscheiden.
Ich stimme auch zu, dass wir die Definition des Befehls z[rev]rangebyscore nicht ändern sollten, um ihm im Unterbefehl eine Schreibvariante hinzuzufügen. Es ist besser, stattdessen einen expliziten Schreibbefehl z[rev]rangebyscorestore zu erstellen. Auf diese Weise vermeiden wir die Verwirrung, dass ein Befehl "meistens" schreibgeschützt ist, aber "manchmal" auch Schreiboperationen ausführen kann ... Ein weiteres gutes Beispiel in Redis heute, das mir gefallen hat, ist, dass der SUNION-Befehl schreibgeschützt ist, während SUNIONSTORE Befehl ist der äquivalente Schreibbefehl zum Speichern des Ergebnisses.
Starkes +1 zum Vermeiden von Schreiberweiterungen für einen vorhandenen schreibgeschützten Befehl.
Was den neuen Befehl betrifft, denke ich, dass wir in Betracht ziehen sollten, die aktuelle Konvention zu ignorieren und ZRANGESTORE
mit zusätzlichen Argumenten zu verwenden, um die verschiedenen Varianten zu unterstützen ( BYLEX
, BYSCORE
, REV
). Die Gründe dafür wären:
ZREVRANGEBYSCORESTORE
.+1 zu ZRANGESTORE erscheint die Parametrisierung bestehender Befehle besser als die Einführung neuer. Der Vorschlag macht es sowieso eher wie ZUNIONSTORE.
Es scheint, wir haben eine Entscheidung. es erfordert ein wenig Refactoring (viele existierende Befehle wären nur Aliase für eine Funktion, die alle verarbeiten kann).
Will braucht natürlich auch Tests und Dokumentation.
@jonahharris willst du es anpacken?
Ich freue mich. Nächste Woche fange ich an, etwas zusammenzustellen.
@jonahharris irgendwelche Neuigkeiten zu dieser Aufgabe? einige Komplikationen haben oder einfach noch keine Zeit gefunden haben?
Hallo @oranagra. Danke fürs Nachfassen. Während ich einige andere Zeitverpflichtungen hatte, habe ich mir dies kursorisch angesehen und denke, dass der Ansatz machbar ist. Ich hoffe, dass ich diese Woche dazu komme.
Nachdem ich also ein paar verschiedene Wege gespielt habe, neige ich dazu, eine Sache sowohl im Set-Code als auch in meiner vorherigen PR nicht zu mögen, ist die spezielle Logik dafür, ob innerhalb der Funktionen geantwortet oder gespeichert werden soll - es werden viele Verzweigungen erstellt und ist schmerzhaft.
Da es nicht viele verschiedene Arten von Antworten gibt, habe ich alternativ eine spezialisierte Client-Wrapper-Struktur geschrieben, die in der generischen Funktion der obersten Ebene instanziiert wird, die die Callbacks entsprechend setzt - entweder auf ein einfaches Client-Passthrough oder eine interne Version, die verwendet, um das Ergebnis zu speichern. Auf diese Weise ist der Hauptcode von zset viel sauberer. Ebenso habe ich aus Performance-Sicht keine wirkliche Verschlechterung gesehen. Ich werde meinen Arbeitscode bald veröffentlichen, habe mich aber gefragt, was Ihre Meinung zu diesem Ansatz ist. Ähnlich würde es auch für den inter/unionstore funktionieren.
@jonahharris Sie meinen, dass die generische Befehlsimplementierung ein Objekt mit einer Reihe von Rückrufen erhält, das in einem Fall als Hinzufügen von Antworten zum Client implementiert wird, und im anderen Fall als etwas implementiert wird, das ein zset-Objekt erstellt, das schließlich hinzugefügt wird die db?
Solange es keinen Performance-Overhead durch das zweimalige Kopieren der Daten gibt, kann dies meiner Meinung nach ein guter Ansatz sein, dh verwendet Funktionszeiger anstelle von Verzweigungen und kann etwas sauberer sein (insbesondere wenn die Implementierung komplex ist und das "Antworten oder Anhängen an" Objekt"-Code wird viele Male wiederholt.
Einreichen von First-Cut-PR, die Folgendes unterstützt:
ZRANGESTORE destkey ZRANGE key start stop [WITHSCORES]
ZRANGESTORE destkey ZREVRANGE key start stop [WITHSCORES]
ZRANGESTORE destkey ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
ZRANGESTORE destkey ZREVRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
ZRANGESTORE destkey ZRANGEBYLEX key max min [LIMIT offset count]
ZRANGESTORE destkey ZREVRANGEBYLEX key max min [LIMIT offset count]
Designziele
Übersicht der Änderungen
Kurz gesagt wurde ein Ergebnishandler (zrange_result_handler) eingeführt, der das Ergebnis entweder an den Client weiterleitet oder in einem Ziel-Zset speichert – dies wird durch ZRANGE_CONSUMER_TYPE_CLIENT bzw. ZRANGE_CONSUMER_TYPE_INTERNAL bestimmt. Ebenso wurden aus der Perspektive des Refactorings und der Erfüllung der Designziele alle zrange-bezogenen Argumentanalysen und die Ausführung auf oberster Ebene in zrangeGenericCommand konsolidiert.
Wichtige Änderungen
Emission von ZRANGE-bezogenen Ergebnissen
Die generischen ZrangeXXXCommand-Funktionen verwenden jetzt die Funktionszeiger beginEmission, emit und finalizeEmission innerhalb von zrange_result_handler, um die Ergebnismenge zu senden, anstatt die addReply-Aufrufe direkt. Dies sind im Wesentlichen:
Verbleibende Arbeit
Ich bin mit der Benennung einiger davon noch nicht sehr zufrieden und wie Sie für t_zset.c sehen werden, muss ich sie wieder in die Redis-Formatierung einführen - ich arbeite normalerweise nur nicht mit Redis-Code in seinem natürlichen Zustand aufgrund seiner Formatierungsinkonsistenzen und ziemlich willkürlichen Deklarationen/Definitionen, die überall verstreut sind. Ich muss auch meine Testskripts in tatsächliche Tcl-Unit-Tests formalisieren.
Egal... Ich habe einen Tag damit verbracht und wollte es herausbringen, damit die Leute damit spielen und Feedback zum Ansatz bekommen.
Wichtige offene Fragen
Könnte eine falsche Idee sein, aber vielleicht haben Sie, anstatt eine befehlsbezogene Sache zu machen (und nicht nur auf Z-Befehle beschränkt), vielleicht einen globalen STORE
Einer der Anwendungsfälle, auf die ich gehofft hatte, war die Möglichkeit, Set-Operationen durchführen zu können
auf den Partituren einer Teilmenge der zset. Wenn wir die STORE-Option haben, würde ich
wollen einen Weg, der uns diese seltsame Migration von Partituren in Mitglieder von
ein Satz.
Am Mi, 30. September 2020 um 10:01 schrieb tzickel [email protected] :
Könnte eine falsche Idee sein, aber vielleicht anstatt pro Befehl Dinge zu tun (und
nicht nur auf Z-Befehle beschränkt), haben vielleicht einen globalen STORE-Befehl, der ist
eigentlich ein Modifikator für den nächsten Befehl, der darauf folgt (wie CLIENT
REPLY ist), und es kann einfach die Ausgabe des Ergebnisses in die angegebene
key, anstatt ihn an den Client zurückzugeben (und vielleicht einige generische
Infrastruktur kann um diese herum aufgebaut werden, um Befehlen dabei zu helfen) ?—
Sie erhalten dies, weil Sie einen Kommentar abgegeben haben.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/redis/redis/issues/678#issuecomment-701261064 , oder
Abmelden
https://github.com/notifications/unsubscribe-auth/ACBDEAJPMLJYF5D5XXNG353SILXVBANCNFSM4ABH3SYQ
.
--
Integrales z-Quadrat dz
von 1 bis zur Kubikwurzel von 3
mal den Kosinus
von drei pi über 9
entspricht dem Logarithmus der Kubikwurzel von 'e'.
@tzickel Ich denke, dass ein solcher generischer Mechanismus weniger effizient ist als das, was wir tun können, wenn wir einen expliziten Befehl haben, und er wird auch einigen Herausforderungen ausgesetzt sein (einige Kombinationen können komplizierter sein, z. B. ZRANGE wird in einem Set-Typ gespeichert, und ZRANGEWITHSCORE in den Zset-Typ).
Wie auch immer, wir können sagen, dass wir bereits einen Mechanismus für so etwas haben, nämlich Lua-Skripte. IIRC ist einer der Hauptauslöser für die Anforderung eines bestimmten Befehls, dass es effizienter ist als die Verwendung von Skripten.
Mit Skripten können Sie auch kompliziertere oder spezifischere Vorgänge ausführen, z. B. die Entscheidung, die Partituren anstelle der Mitgliedsnamen zu speichern, und sogar einige explizite Bedingungen hinzufügen. Ich denke, das sollte den erwähnten Anwendungsfall @borg286 abdecken . Einen STORE-Befehl oder eine Befehlsvariante für diese vielen sehr spezifischen Anwendungsfälle auszuführen, klingt falsch. Genau deshalb haben wir Skripte.
Hilfreichster Kommentar
Starkes +1 zum Vermeiden von Schreiberweiterungen für einen vorhandenen schreibgeschützten Befehl.
Was den neuen Befehl betrifft, denke ich, dass wir in Betracht ziehen sollten, die aktuelle Konvention zu ignorieren und
ZRANGESTORE
mit zusätzlichen Argumenten zu verwenden, um die verschiedenen Varianten zu unterstützen (BYLEX
,BYSCORE
,REV
). Die Gründe dafür wären:ZREVRANGEBYSCORESTORE
.