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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容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 。


目录
相关文章
|
6月前
|
数据采集 存储 数据可视化
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
582 0
分布式爬虫框架Scrapy-Redis实战指南
|
3月前
|
缓存 监控 NoSQL
Redis 实操要点:Java 最新技术栈的实战解析
本文介绍了基于Spring Boot 3、Redis 7和Lettuce客户端的Redis高级应用实践。内容包括:1)现代Java项目集成Redis的配置方法;2)使用Redisson实现分布式可重入锁与公平锁;3)缓存模式解决方案,包括布隆过滤器防穿透和随机过期时间防雪崩;4)Redis数据结构的高级应用,如HyperLogLog统计UV和GeoHash处理地理位置。文章提供了详细的代码示例,涵盖Redis在分布式系统中的核心应用场景,特别适合需要处理高并发、分布式锁等问题的开发场景。
252 40
|
3月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
951 7
|
6月前
|
缓存 NoSQL Java
基于SpringBoot的Redis开发实战教程
Redis在Spring Boot中的应用非常广泛,其高性能和灵活性使其成为构建高效分布式系统的理想选择。通过深入理解本文的内容,您可以更好地利用Redis的特性,为应用程序提供高效的缓存和消息处理能力。
489 79
|
10月前
|
NoSQL 安全 测试技术
Redis游戏积分排行榜项目中通义灵码的应用实战
Redis游戏积分排行榜项目中通义灵码的应用实战
233 4
|
11月前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:优化百万数据查询的实战经验
【10月更文挑战第13天】 在处理大规模数据集时,传统的关系型数据库如MySQL可能会遇到性能瓶颈。为了提升数据处理的效率,我们可以结合使用MySQL和Redis,利用两者的优势来优化数据查询。本文将分享一次实战经验,探讨如何通过MySQL与Redis的协同工作来优化百万级数据统计。
565 5
|
11月前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
436 2
|
4月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
4月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
681 0
|
4月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
174 32