大话Redis系列--实战案例总结(下)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 大话Redis系列--实战案例总结(下)

承接之前的文章,今晚我们一起聊聊关于Redis内部比较有趣的技术实现案例。在上一篇文章的最后我们留了一个尾巴,今天继续和大家分享这些场景下redis是怎么应用的。


微博、微信、陌陌

<附近的人>

微信<摇一摇><抢红包>

滴滴打车、摩拜单车<附近的车> 美团和饿了么<附近的餐馆>

布隆过滤器

附近的xxx案例


定位功能显示周边功能


这类功能在常见的互联网App如某团等应用上一般都能看到相关案例。

相关截图如下所示:


网络异常,图片无法展示
|


这让我想起了自己曾经遇到过的一个类似的需求,会员拿着手机到达指定地点打卡,即可领取相应奖励红包,但是要求到达指定地点范围200米内。(当然成为这类会员也是有一定门槛的,不然早就被羊毛党薅爆了🐶 )


当初的做法是将指定地点的坐标存储到MySQL的一张表中,然后从网上搜了一个根据经纬度计算距离的util类,外加接入一个腾讯地图手机定位的api简单粗暴地上线了。当时的场景还是比较简单的,所以也没有考虑到使用Redis来实现。


回归正题:


假设现在有个业务场景需要后台返回给前端一个指定地理位置方圆500m内所有自行车的坐标,你会怎么设计呢?


Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增。


Redis GEO 操作方法有


geoadd:添加地理位置的坐标。
geopos:获取地理位置的坐标。
geodist:计算两个位置之间的距离。
georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
geohash:返回一个或多个位置对象的 geohash 值。
复制代码


我们可以提前在redis中存储好对应的单车地理位置:


命令格式:


GEOADD key longitude latitude member [longitude latitude member ...]
复制代码


实操命令:


> geoadd shenzhen 21.123123 22.812312 car-10012
1
复制代码


给定坐标的经纬度之后,将对应的自行车id记录到redis中。

查看指定单车的经纬度信息:


> geopos shenzhen car-10012 car-10013 car-10014 car-10015
21.12312287092208862
22.81231173620882657
21.12312287092208862
22.81231173620882657
21.12312287092208862
22.81231427092998132
21.12312287092208862
22.81231427092998132
复制代码


计算两辆单车之间的距离


> geodist shenzhen car-10012 car-10015
0.2819
复制代码


根据用户定位的经纬度查询指定方圆内存在的单车(这一功能就有点类似于我上边截图的效果),例如查询方圆500m内的自行车


> georadius shenzhen 21.12312287092208862 22.81231173620882657 500 m
car-10012
car-10013
car-10014
car-10015
复制代码


基于geo相关命令,我们还可以对定位功能做各式各样的扩展功能。


其实从这个案例我们也大概可以推测出类似的其他功能是如何实现的,例如摇一摇,附近的餐馆,附近的人等等。


抢红包案例


业务流程分析


如下图所示:


网络异常,图片无法展示
|


新建红包


在 DB、Redis 分别新增一条记录


抢红包(并发)


请求Redis,红包剩余个数,大于0才可以拆,等会0时,提示用户,红包已抢完


拆红包(并发)


Redis 中数据类型的 String 特性的原子递减(DECR key)和减少指定值(DECRBY key decrement)。请求 Redis ,当剩余红包个数大于 0,红包个数原子递减,随机获取红包。计算金额,当最后一个红包时,最后一个红包金额=总金额-总已抢红包金额


更新数据库


查看红包记录

查询 DB 即可

数据库设计


红包流水表


CREATE TABLE `red_packet_info` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `red_packet_id` bigint(11) NOT NULL DEFAULT 0 COMMENT '红包id,采⽤
timestamp+5位随机数',
 `total_amount` int(11) NOT NULL DEFAULT 0 COMMENT '红包总⾦额,单位分',
 `total_packet` int(11) NOT NULL DEFAULT 0 COMMENT '红包总个数',
 `remaining_amount` int(11) NOT NULL DEFAULT 0 COMMENT '剩余红包⾦额,单位
分',
 `remaining_packet` int(11) NOT NULL DEFAULT 0 COMMENT '剩余红包个数',
 `uid` int(20) NOT NULL DEFAULT 0 COMMENT '新建红包⽤户的⽤户标识',
 `create_time` timestamp COMMENT '创建时间',
 `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP COMMENT '更新时间',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='红包信息
表,新建⼀个红包插⼊⼀条记录';
复制代码


红包记录表


CREATE TABLE `red_packet_record` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `amount` int(11) NOT NULL DEFAULT '0' COMMENT '抢到红包的⾦额',
 `nick_name` varchar(32) NOT NULL DEFAULT '0' COMMENT '抢到红包的⽤户的⽤户
名',
 `img_url` varchar(255) NOT NULL DEFAULT '0' COMMENT '抢到红包的⽤户的头像',
 `uid` int(20) NOT NULL DEFAULT '0' COMMENT '抢到红包⽤户的⽤户标识',
 `red_packet_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '红包id,采⽤
timestamp+5位随机数',
 `create_time` timestamp COMMENT '创建时间',
 `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP COMMENT '更新时间',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='抢红包记
录表,抢⼀个红包插⼊⼀条记录';
复制代码


发红包 API


发红包接口开发

新增一条红包记录

往 mysql 里面添加一条红包记录

往 redis 里面添加一条红包数量记录

往redis里面添加一条红包金额记录


网络异常,图片无法展示
|


往db中就单纯存入一条记录,Service层和Mapper层,就简单的一条sql语句,主要是提供思路。


抢红包功能属于原子减操作


  • 当大小小于 0 时原子减失败
  • 当红包个数为0时,后面进来的用户全部抢红包失败,并不会进入拆红包环节
  • 将红包ID的请求放入请求队列中,如果发现超过红包的个数,直接返回
  • 抢到红包不一定能拆成功


抢红包算法拆解

网络异常,图片无法展示
|


通过上图算法得出,靠前面的人,手气最佳几率小,手气最佳,往往在后面

发 100 元,共 10 个红包,那么平均值是 10 元一个,那么发出来的红包金额在 0.01~20 元之间波动


当前面 4 个红包总共被领了 30 元时,剩下 70 元,总共 6 个红包,那么这 6 个红包的金额在 0.01~23.3 元之间波动。


企业微信消息是否已阅读


redis中的布隆过滤器实际上可用的案例非常多,这里我就不打算细说了,典型应用如:在1亿个手机号中查询是否存在对应手机号码,一亿个用户白名单记录表等等。


最近在工作中频繁会使用企业微信和别人沟通消息,假如让我们自己来实现企业微信消息的未读通知改如何实现呢?


对于消息后台可以分为离线和已发送消息。所谓的离线消息就是指用户还处于未读状态的消息,通常这部分的数据都会存储在离线消息库中。因此当用户首次访问企业微信的时候应该会触发一个机制:判断当前用户是否存在离线消息信息,如果存在则从离线消息库中拉去。


推测企业微信的用户群体如此庞大,这种判断应该也是利用一个类似于布隆过滤器之类的组件存放每个具有离线消息的用户id,然后请求后台的时候到其中去查找。(当然这只是我的一个简单猜测)


在Redis里有一个叫做bitmap的数据结构,使用技巧如下:


setbit指令


语法:setbit key offset value


127.0.0.1:6379> setbit bitmap-01 999 0
(integer) 0
127.0.0.1:6379> setbit bitmap-01 999 1
(integer) 0
127.0.0.1:6379> setbit bitmap-01 1003 1
(integer) 0
127.0.0.1:6379> setbit bitmap-01 1003 0
(integer) 1
复制代码


getbit指令


语法:getbit key offset


127.0.0.1:6379> setbit bm 0 1
(integer) 0
127.0.0.1:6379> getbit bm 0
(integer) 1
复制代码


bitcount指令


语法:bitcount key [start] [end] ,这里的start和end值为可选项

返回值:被设置为 1 的位的数量


127.0.0.1:6379> bitcount user18 
(integer) 4
复制代码


bitop指令


语法:bitop operation destkey key [key …]

operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种:


BITOP AND destkey key [key ...] ,对一个或多个 key 求逻辑并,并将结果保存到 destkey 。
BITOP OR destkey key [key ...] ,对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
BITOP XOR destkey key [key ...] ,对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
BITOP NOT destkey key ,对给定 key 求逻辑非,并将结果保存到 destkey 。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
8天前
|
监控 NoSQL 算法
探秘Redis分布式锁:实战与注意事项
本文介绍了Redis分区容错中的分布式锁概念,包括利用Watch实现乐观锁和使用setnx防止库存超卖。乐观锁通过Watch命令监控键值变化,在事务中执行修改,若键值被改变则事务失败。Java代码示例展示了具体实现。setnx命令用于库存操作,确保无超卖,通过设置锁并检查库存来更新。文章还讨论了分布式锁存在的问题,如客户端阻塞、时钟漂移和单点故障,并提出了RedLock算法来提高可靠性。Redisson作为生产环境的分布式锁实现,提供了可重入锁、读写锁等高级功能。最后,文章对比了Redis、Zookeeper和etcd的分布式锁特性。
108 16
探秘Redis分布式锁:实战与注意事项
|
11天前
|
缓存 NoSQL Java
Redis7的10大应用场景和案例解析
你在项目中使用 Redis 实现了什么应用场景,欢迎一起跟 V 哥讨论。同时也做个小调查,朋多少兄弟是需要了解 Redis 核心源码的,人多的话,下一篇 V 哥写 Redis7的源码分析,人少的话就算了,感谢。
|
11天前
|
存储 NoSQL Java
Spring Boot与Redis:整合与实战
【4月更文挑战第29天】Redis,作为一个高性能的键值存储数据库,广泛应用于缓存、消息队列、会话存储等多种场景中。在Spring Boot应用中整合Redis可以显著提高数据处理的效率和应用的响应速度。
26 0
|
14天前
|
存储 缓存 NoSQL
node实战——koa给邮件发送验证码并缓存到redis服务(node后端储备知识)
node实战——koa给邮件发送验证码并缓存到redis服务(node后端储备知识)
19 0
|
15天前
|
存储 缓存 NoSQL
Redis入门到通关之Redis缓存数据实战
Redis入门到通关之Redis缓存数据实战
21 0
|
2月前
|
存储 NoSQL 前端开发
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群指令分析—实战篇)
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群指令分析—实战篇)
12 0
|
2月前
|
存储 监控 NoSQL
【Redis技术专区】「优化案例」谈谈使用Redis慢查询日志以及Redis慢查询分析指南
【Redis技术专区】「优化案例」谈谈使用Redis慢查询日志以及Redis慢查询分析指南
35 0
|
2月前
|
缓存 NoSQL 前端开发
【Redis技术专区】「实战案例」谈谈使用Redis缓存时高效的批量删除的几种方案
【Redis技术专区】「实战案例」谈谈使用Redis缓存时高效的批量删除的几种方案
47 0
|
1月前
|
存储 NoSQL 算法
09- Redis分片集群中数据是怎么存储和读取的 ?
Redis分片集群使用哈希槽分区算法,包含16384个槽(0-16383)。数据存储时,通过CRC16算法对key计算并模16383,确定槽位,进而分配至对应节点。读取时,根据槽位找到相应节点直接操作。
65 12
|
1天前
|
NoSQL 算法 Java
深入浅出Redis(八):Redis的集群模式
深入浅出Redis(八):Redis的集群模式