詳解.NET用戶端實現Redis中的管道PipeLine與事物Transactions

來源:互聯網
上載者:User
本文主要介紹了.NET用戶端實現Redis中的管道(PipeLine)與事物(Transactions)的相關知識。具有很好的參考價值,下面跟著小編一起來看下吧

序言

Redis中的管道(PipeLine)特性:簡述一下就是,Redis如何從用戶端一次發送多個命令,服務端到用戶端如何一次性響應多個命令。

Redis使用的是用戶端-伺服器模型和請求/響應協議的TCP伺服器,這就意味著一個請求要有以下步驟才能完成:1、用戶端向伺服器發送查詢命令,然後通常以阻塞的方式等待伺服器相應。2、伺服器處理查詢命令,並將相應發送回用戶端。這樣便會通過網路連接,如果是本地迴環介面那麼就能特別迅速的響應,但是如果走外網,甚至外網再做一系列的層層轉寄,那就顯的格外蛋疼。無論網路延時是多少,那麼都將佔用整體響應的時間。這樣一來如果一次發送1個命令,網路延時為100ms,我們不得不做。那麼如果1次發1000個命令,那麼網路延時100*1000ms就很難容忍啦。

針對與上面的問題,Redis在2.6版本以後就都提供啦管道(Pipeline)功能。他可以使用戶端在沒有讀取舊的響應時,處理新的請求。這樣便可以向伺服器發送多個命令,而不必等待回覆,直到最後一個步驟中讀取回覆。這被稱為管線(PipeLine),並且是幾十年來廣泛使用的技術。例如,許多POP3協議實現已經支援此功能,大大加快了從伺服器下載新電子郵件的過程。

那麼事務這個詞彙,經常遇到,就不多唧唧啦,目標要一致就好,便是一組操作怎麼做成原子性操作,使他去不了終點,回到原點。

簡述wireshark抓包工具

為啦讓大家對管線有更形象的感觀,這一節我們先說說Wireshark抓包工具,他會讓你看到用戶端到伺服器通過tcp協議發送的redis命令的過程與詳細。

wireshark能夠撲捉到系統發送和接受的每一個報文,我們這裡只做一些過濾的簡述。就是他的樣子,你開啟後可以可以摸索下他的用法。

簡述幾個過濾規則:

1、ip過濾:目標ip過濾:ip.dst==172.18.8.11,源ip地址過濾:ip.src==192.168.1.12;

2、連接埠過濾:tcp.port==80,這條規則是把源連接埠和目的連接埠為80的都過濾出來。使用tcp.dstport==80隻過濾目的連接埠為80的,tcp.srcport==80隻過濾源連接埠為80的包;

3、協議過濾:直接在fiter框中輸入協議名稱即可,如:http,tcp,udp,...

4、http模式過濾:過濾get包,http.request.method=="GET",過濾post包,http.request.method=="POST";

5、如果使用多條件過濾,則需要加串連符號,and。比如 ip.src==192.168.1.12 and http.request.method=="POST" and tcp.srcport==80

StackExchange.Redis實現Redis管線(Pipeline)

上兩張圖片管線便一目瞭然啦。

用戶端對redis伺服器進行多次請求的話,一般普通模式是這樣子的

用戶端對redis伺服器進行多次請求的話,管道模式是這樣子的

一般模式我們上代碼:


public static void GetNoPipelining()  {   for (var i = 0; i < 3; i++)   {    var key = "name:" + i;    db.StringAppend(key, "張龍豪");   }  }


查看tcp請求報文的data

這樣你自己做的過程中,可以看到我圈起來的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()之前是不會對資料庫產生任何影響的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.