一. Geo類型
1. 類型說明
Geo 是 Redis 3.2 版本后新增的數據類型,用來保存興趣點(POI,point of interest)的坐標信息。可以實現計算兩 POI 之間的距離、獲取一個點周邊指定距離的 POI。
2. 常用Api
(1).GeoAdd:添加POI點
(2).GeoDistance:獲取兩點之間的最短距離
(3).GeoPosition:獲取某個點的坐標
(4).GeoRadius:獲取某個點(不一定是POI)周邊xx米以外的點
(5).GeoRemove:刪除某個點
代碼分享:
1 //1. 添加所有商店的地理位置 2 db.GeoAdd("ShopsGeo", new GeoEntry(116.34039, 39.94218, "name1")); 3 db.GeoAdd("ShopsGeo", new GeoEntry(116.340934, 39.942221, "name2")); 4 db.GeoAdd("ShopsGeo", new GeoEntry(116.341082, 39.941025, "name3")); 5 db.GeoAdd("ShopsGeo", new GeoEntry(116.340848, 39.937758, "name4")); 6 db.GeoAdd("ShopsGeo", new GeoEntry(116.342982, 39.937325, "name5")); 7 db.GeoAdd("ShopsGeo", new GeoEntry(116.340866, 39.936827, "name6")); 8 9 //2. 計算商店name1和name2之間的距離(單位m) 10 double? dist = db.GeoDistance("ShopsGeo", "name1", "name5", GeoUnit.Meters); 11 12 //3. 獲取name1商店的坐標 13 GeoPosition? pos = db.GeoPosition("ShopsGeo", "name1"); 14 15 //4. 獲取一個 name2 周邊的200內的點: 16 GeoRadiusResult[] results = db.GeoRadius("ShopsGeo", "name2", 200, GeoUnit.Meters); 17 foreach (GeoRadiusResult result in results) 18 { 19 Console.WriteLine("Id=" + result.Member + ",位置" + result.Position + ",距離" + result.Distance); 20 } 21 22 //5. 獲取一個坐標(116.34092, 39.94223)(這個坐標不一定是 POI)周邊的 POI: 23 GeoRadiusResult[] results2 = db.GeoRadius("ShopsGeo", 116.34092, 39.94223, 200, GeoUnit.Meters); 24 foreach (GeoRadiusResult result in results2) 25 { 26 Console.WriteLine("Id=" + result.Member + ",位置" + result.Position + ",距離" + result.Distance); 27 } 28 29 //6. 刪除 30 bool d1 = db.GeoRemove("ShopsGeo", "name2");
3. 案例
地圖上點相關的操作,方圓xx米內有多少個商店,某兩個商店間的距離
二. Redis批量操作和事務
1. 批量操作
Batch會把所需要執行的命令打包成一條請求發到Redis,然后一起等待返回結果。這樣批量操作的速度就大大提升。 利用CreateBatch和Execute方法,僅支持異步方法哦。
1 public static void BatchDemo(IDatabase db) 2 { 3 IBatch batch = db.CreateBatch(); 4 batch.StringSetAsync("keen1", "111"); 5 batch.StringSetAsync("keen2", "222"); 6 batch.Execute(); 7 8 }
2. 事務
Redis的操作都是原子性單線程的,如果一次性操作很多,基本上每個方法都支持批量操作,但是如果操作的數據類型不同,可以使用Redis的事務進行包裹。 CreateTransaction和Execute方法
1 public static void TransDemo(IDatabase db) 2 { 3 var trans = db.CreateTransaction(); 4 trans.StringSetAsync("keen1", "111"); 5 trans.StringSetAsync("keen2", 222); 6 bool result = trans.Execute(); 7 }
3. 區別
批量操作假設里面有一個出錯,不會整體回滾,而事務要么都成功,要么都失敗。
三. Redis分布式鎖
1. 背景
在傳統的單體項目中,即部署到單個IIS上,針對並發問題,比如進銷存中的出庫和入庫問題,多個人同時操作,屬於一個IIS進程中多個線程並發操作的問題,這個時候可以引入線程鎖lock/Monitor等,輕松解決這類問題。但是隨着業務量的逐漸增大,比如"秒殺業務",肯定是集群,這個時候線程鎖已經沒用了,必須引入分布式鎖。
常見的分布式鎖有:數據庫、zookeeper、redis。
2. 技術分析
秒殺業務集群同時訪問DB,很容易出現超買超賣問題,如下圖:
分析:
解決方案:
在秒殺服務集群和DB之間引入Redis(或者Redis集群),無論是Redis單體還是集群,分布式鎖都是上一個解鎖了下一個才繼續加鎖,引入Redis集群的目的是防止Redis崩潰,而不是加快速度,這樣保證了最終到DB上上的操作是按順序依次進行的,從而解決了超賣問題。如下圖:
3. 代碼實戰
StackExchange.Redis中加鎖和解鎖的api分別是:LockTake和LockRelease。 bool LockTake(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags = CommandFlags.None); 三個參數的含義分別是:
(1). 鎖名
(2). 誰加的的鎖
(3). 超時時間,過期自動釋放,防止死鎖。
代碼如下:(加鎖的時候要循環獲取鎖,直到獲取為止)
1 /// <summary> 2 /// 分布式鎖 業務 3 /// </summary> 4 /// <param name="db"></param> 5 public static void DfsLockDemo(IDatabase db) 6 { 7 8 Lock(db); 9 try 10 { 11 Console.WriteLine("業務執行中......"); 12 Thread.Sleep(8000); 13 Console.WriteLine("業務執行完畢"); 14 } 15 catch (Exception) 16 { 17 //釋放鎖 18 UnLock(db); 19 } 20 finally 21 { 22 //釋放鎖 23 UnLock(db); 24 } 25 } 26 27 /// <summary> 28 /// 加鎖 29 /// </summary> 30 /// <param name="db"></param> 31 public static void Lock(IDatabase db) 32 { 33 RedisValue token = Environment.MachineName; 34 while (true) 35 { 36 bool flag = db.LockTake("myLock", token, TimeSpan.FromSeconds(10)); //10秒后自動釋放 37 if (flag) 38 { 39 //表示獲取成功,跳出while 40 break; 41 } 42 else 43 { 44 Console.WriteLine("獲取失敗,繼續獲取"); 45 Thread.Sleep(200); 46 47 } 48 } 49 } 50 /// <summary> 51 /// 解鎖 52 /// </summary> 53 /// <param name="db"></param> 54 public static void UnLock(IDatabase db) 55 { 56 RedisValue token = Environment.MachineName; 57 db.LockRelease("myLock", token); 58 }
模擬兩個項目運行效果:
秒殺案例其它思路或者詳細解決方案見: 第六節:秒殺業務/超買超賣的幾種解決思路
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。