In diesem Artikel werden hauptsächlich die relevanten Kenntnisse des .NET-Clients zur Implementierung von PipeLine und Transaktionen in Redis vorgestellt. Es hat einen sehr guten Referenzwert, schauen wir es uns mit dem Editor unten an
Vorwort
PipeLine-Funktionen in Redis: Um kurz zu beschreiben, wie man Redis sendet mehrere Befehle gleichzeitig vom Client senden und auf mehrere Befehle gleichzeitig vom Server an den Client reagieren.
Redis verwendet das Client-Server--Modell und den TCP-Server des Anforderungs-/Antwortprotokolls, was bedeutet, dass eine Anforderung die folgenden Schritte ausführen muss: 1. Der Client sendet sie an Der Server fragt den Befehl ab und wartet dann auf eine Antwort vom Server, normalerweise auf blockierende Weise. 2. Der Server verarbeitet den Abfragebefehl und sendet die Antwort an den Client zurück. Auf diese Weise wird die Verbindung über das Netzwerk hergestellt. Wenn es sich um eine lokale Loopback-Schnittstelle handelt, kann es jedoch sehr schnell auf das externe Netzwerk reagieren oder sogar eine Reihe von Schicht-für-Schicht-Weiterleitungen durchführen externes Netzwerk, wird es besonders schmerzhaft sein. Unabhängig von der Netzwerkverzögerung nimmt sie die Gesamtantwortzeit in Anspruch. Wenn auf diese Weise jeweils ein Befehl gesendet wird, beträgt die Netzwerkverzögerung 100 ms, und wir müssen dies tun. Wenn also 1000 Befehle gleichzeitig gesendet werden, ist die Netzwerkverzögerung von 100 * 1000 ms schwer zu tolerieren.
Als Reaktion auf die oben genannten Probleme stellt Redis seit Version 2.6 die Pipeline-Funktion bereit. Dadurch kann der Client neue Anfragen verarbeiten, ohne die alte Antwort zu lesen. Dadurch ist es möglich, mehrere Befehle an den Server zu senden, ohne auf eine Antwort warten zu müssen, bis die Antwort im letzten Schritt gelesen wird. Dies nennt sich PipeLine und ist seit Jahrzehnten eine weit verbreitete Technologie. Beispielsweise unterstützen viele Implementierungen des POP3-Protokolls diese Funktion bereits, wodurch das Herunterladen neuer E-Mails vom Server erheblich beschleunigt wird.
Das Wort „Transaktion“ kommt häufig vor, daher besteht keine Notwendigkeit, zu viel zu sagen. Das Ziel sollte konsistent sein, das heißt, wie man eine Reihe von Operationen in atomare Operationen umwandelt, damit sie nicht erreicht werden können Endpunkt und Rückkehr zum Ausgangspunkt.
Eine kurze Einführung in das Wireshark-Paketerfassungstool
Um allen ein klareres Verständnis der Pipeline zu vermitteln, werden wir in diesem Abschnitt zunächst über Wireshark sprechen Paketerfassungstool zeigt Ihnen den Prozess und die Details des Redis-Befehls an, der vom Client über das TCP-Protokoll an den Server gesendet wird.
Wireshark kann jede vom System gesendete und empfangene Nachricht erfassen. Wir werden hier nur eine kurze Beschreibung einiger Filter geben. Das Bild unten zeigt, wie es aussieht. Nachdem Sie es geöffnet haben, können Sie seine Verwendung erkunden.
Beschreiben Sie kurz einige Filterregeln:
1. IP-Filterung: Ziel-IP-Filterung: ip.dst==172.18.8.11, Quell-IP-Adressfilterung: ip.src==192.168.1.12;
2. Portfilterung: tcp.port==80. Diese Regel filtert sowohl den Quellport als auch den Zielport 80 heraus. Verwenden Sie tcp.dstport==80, um nur Pakete mit einem Zielport von 80 zu filtern, und tcp.srcport==80, um nur Pakete mit einem Quellport von 80 zu filtern;
3. Protokollfilterung: Geben Sie direkt ein Protokollname im Filterfeld. Verfügbar, wie zum Beispiel: http, tcp, udp,...
4 packets, http.request.method= ="POST";
5. Wenn Sie mehrere Bedingungen zum Filtern verwenden, müssen Sie das Verbindungssymbol hinzufügen. Beispielsweise implementiert ip.src==192.168.1.12 und http.request.method=="POST" und tcp.srcport==80
StackExchange.Redis die Redis-Pipeline (Pipeline)
Die beiden oben genannten Bildpipelines sind auf einen Blick klar.
Wenn der Client mehrere Anfragen an den Redis-Server stellt, sieht der normale Modus so aus
Wenn der Client mehrere Anfragen an den Redis-Server stellt, sieht der Pipeline-Modus so aus
Unser Code für den Normalmodus:
public static void GetNoPipelining() { for (var i = 0; i < 3; i++) { var key = "name:" + i; db.StringAppend(key, "张龙豪"); } }
Daten der TCP-Anforderungsnachricht anzeigen
这样你自己做的过程中,可以看到我圈起来的3个tcp请求的key分别为name:0,name:1,name:2这样子。
那么我们使用管道模式
public static void GetPipelining() { var batch = db.CreateBatch(); for (int i = 0; i < 3; i++) { var key = "mename:" + i; batch.StringAppendAsync(key, "张龙豪"); } batch.Execute(); }
再来看下请求
这样很明显就能看出来是1个请求发送出来啦多个命令。那么我们不用createBatch()也是可以实现这样的效果的。
var a = db.StringAppendAsync("zlh:1", "zhanglonghao1"); var b = db.StringAppendAsync("zlh:2", "zhanglonghao2"); var c = db.StringAppendAsync("zlh:3", "zhanglonghao3"); var aa = db.Wait(a); var bb = db.Wait(a); var cc = db.Wait(a);
在接下来我们做一个简单的性能比较。代码如下:
static void Main(string[] args) { Stopwatch watch = new Stopwatch(); Stopwatch watch1 = new Stopwatch(); watch.Start(); GetNoPipelining(); Console.WriteLine("一般循环耗时:" + watch.ElapsedMilliseconds); watch.Stop(); watch1.Start(); GetPipelining(); Console.WriteLine("Pipelining插入耗时:" + watch1.ElapsedMilliseconds); watch1.Stop(); Console.ReadLine(); } public static void GetNoPipelining() { for (var i = 0; i < 5000; i++) { var key = "name:" + i; db.StringAppend(key, "张龙豪"); } } public static void GetPipelining() { var batch = db.CreateBatch(); for (int i = 0; i < 5000; i++) { var key = "mename:" + i; batch.StringAppendAsync(key, "张龙豪"); } batch.Execute(); }
结果如下:
到此我还要说一下StackExchange.Redis的三种命令模式,其中使用2和3的模式发送命令,会默认被封装在管道中,不信的话,你可以做个小demo测试下:
1、sync:同步模式,会直接阻塞调用者,但不会阻塞其他线程。
2、async:异步模式,使用task模型封装。
3、fire-and-forget:发送命令,然后完全不关心最终什么时候完成命令操作。在Fire-and-Forget模式下,所有命令都会立即得到返回值,该值都是该返回值类型的默认值,比如操作返回类型是bool将会立即得到false,因为false = default(bool)。
StackExchange.Redis实现Redis事务(Transactions)
这个看官方文档,我只能说实现的很奇怪吧。我先描述下我的环境,就是准备一个空redis库,然后一步一步往下走,我们写代码看结果,来搞一搞这个事务。
static void Main(string[] args) { var tran = db.CreateTransaction(); tran.AddCondition(Condition.ListIndexNotEqual("zlh:1",0,"zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao"); bool committed = tran.Execute(); Console.WriteLine(committed); Console.ReadLine(); }
执行结果为:true。数据库中结果如下,说明我们插入成功。
即:如果key为:zlh:1的list集合在索引0初的value!=zhanglonghao的话,我们从链表右侧插入一条数据key为zlh:1value为zhanglonghao,成功。因为第一次操作为空库。0处确实不为张龙豪。
数据不清空,继续上代码。
static void Main(string[] args) { var tran = db.CreateTransaction(); tran.AddCondition(Condition.ListIndexNotEqual("zlh:1",0,"zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao1"); bool committed = tran.Execute(); Console.WriteLine(committed); Console.ReadLine(); }
结果为false,数据库没有增减数据。已久与上图的数据保持一致。
原因分析:0处此时为zhanglonghao,所以ListIndexNotEqual("zlh:1",0,"zhanglonghao")为假命题,直接回滚,不执行下面的插入命令。
数据不清空,继续上代码:
static void Main(string[] args) { var tran = db.CreateTransaction(); tran.AddCondition(Condition.ListIndexEqual("zlh:1",0,"zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao1"); bool committed = tran.Execute(); Console.WriteLine(committed); Console.ReadLine(); }
结果为true,数据结果如下,增长一条值为zhanglonghao1的数据:
原因分析:ListIndexEqual("zlh:1",0,"zhanglonghao")为真命题,执行下面的操作,提交事物。
数据不删继续上代码:
static void Main(string[] args) { var tran = db.CreateTransaction(); tran.AddCondition(Condition.ListIndexEqual("zlh:1",0,"zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao2"); tran.AddCondition(Condition.ListIndexNotEqual("zlh:1", 0, "zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao3"); bool committed = tran.Execute(); Console.WriteLine(committed); Console.ReadLine(); }
结果为false,数据库数据已久与上面的保持一致,不增不减。
分析原因:Condition.ListIndexEqual("zlh:1",0,"zhanglonghao")为true,但是到下面的ListIndexNotEqual("zlh:1", 0, "zhanglonghao")为false。故整个事物的操作回滚,不予执行,故数据库没有变化。
到此,我就不写多余的代码啦,但我要说几个注意点:
1、执行命令的操作需为异步操作。
2、在事物中执行的命令,都不会直接看到结果,故此结果也不能用于下面代码做判断,因为当前的异步命令在Execute()之前是不会对数据库产生任何影响的。
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der .NET-Client-Implementierung von PipeLine und Transaktionen in Redis. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!