大话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 。


相关实践学习
基于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
目录
相关文章
|
21天前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:优化百万数据查询的实战经验
【10月更文挑战第13天】 在处理大规模数据集时,传统的关系型数据库如MySQL可能会遇到性能瓶颈。为了提升数据处理的效率,我们可以结合使用MySQL和Redis,利用两者的优势来优化数据查询。本文将分享一次实战经验,探讨如何通过MySQL与Redis的协同工作来优化百万级数据统计。
48 5
|
28天前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
69 2
|
2月前
|
缓存 NoSQL 应用服务中间件
Redis实战篇
Redis实战篇
|
2月前
|
NoSQL Linux Redis
linux安装单机版redis详细步骤,及python连接redis案例
这篇文章提供了在Linux系统中安装单机版Redis的详细步骤,并展示了如何配置Redis为systemctl启动,以及使用Python连接Redis进行数据操作的案例。
68 2
|
1月前
|
消息中间件 NoSQL Kafka
大数据-116 - Flink DataStream Sink 原理、概念、常见Sink类型 配置与使用 附带案例1:消费Kafka写到Redis
大数据-116 - Flink DataStream Sink 原理、概念、常见Sink类型 配置与使用 附带案例1:消费Kafka写到Redis
129 0
|
3月前
|
运维 监控 NoSQL
【Redis】哨兵(Sentinel)原理与实战全解~炒鸡简单啊
Redis 的哨兵模式(Sentinel)是一种用于实现高可用性的机制。它通过监控主节点和从节点,并在主节点故障时自动进行切换,确保集群持续提供服务。哨兵模式包括主节点、从节点和哨兵实例,具备监控、通知、自动故障转移等功能,能显著提高系统的稳定性和可靠性。本文详细介绍了哨兵模式的组成、功能、工作机制以及其优势和局限性,并提供了单实例的安装和配置步骤,包括系统优化、安装、配置、启停管理和性能监控等。此外,还介绍了如何配置主从复制和哨兵,确保在故障时能够自动切换并恢复服务。
|
3月前
|
消息中间件 存储 NoSQL
redis实战——go-redis的使用与redis基础数据类型的使用场景(一)
本文档介绍了如何使用 Go 语言中的 `go-redis` 库操作 Redis 数据库
172 0
redis实战——go-redis的使用与redis基础数据类型的使用场景(一)
|
3月前
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
66 0
|
存储 缓存 NoSQL
Redis实战之入门进阶到精通
Redis 是一个远程内存数据库,它不仅性能强劲,而且还具有复制特性以及为解决问题而生的独一无二的数据模型。Redis 提供了 5 种不同类型的数据结构,各式各样的问题都可以很自然地映射到这些数据结构上:Redis 的数据结构致力于帮助用户解决问题,而不会像其他数据库那样,要求用户扭曲问题来适应数据库。除此之外,通过复制、持久化(persistence)和客户端分片(client-side sharding)等特性,用户可以很方便地将 Redis 扩展成一个能够包含数百 GB 数据、每秒处理上百万次请求的系统。
Redis实战之入门进阶到精通
|
存储 NoSQL Java
当Java遇到Redis:Jedis实战入门
Redis是一个开源,高级的键值存储和一个适用的解决方案,用于构建高性能,可扩展的Web应用程序。本文将概要介绍Redis的特性和语法,并以实例代码的形式介绍如何通过Jedis在java语言环境下控制Redis,帮助各位读者快速入门。
1623 0