第四节:Geo类型介绍以及Redis批量操作、事务、分布式锁
一. 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的事务不会回滚,事务也不是原子性操作的。
三. 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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
2017-11-28 第四节:MVC中AOP思想的体现(四种过滤器)并结合项目案例说明过滤器的实际用法