目录
- 1. 短信登录
- 1-1. 技术点
- redis存储token
- 拦截器刷新token有效期
- 1-2. 业务
- 登录注册
- 2. 商户查询缓存
- 1-1. 技术点
- 缓存更新策略
- 缓存穿透
- 缓存雪崩
- 缓存击穿
- 1-2. 业务
- 查询缓存的商铺信息
- 3. 优惠卷秒杀
- 3-1. 技术点
- 全局唯一ID
- 乐观锁
- 基于Redis实现分布式锁
- 基于Redisson实现分布式锁
- Redisson实现可重入锁的原理
- Redisson实现锁重试和WatchDog机制的原理
- Redisson的multiLock
- Redis消息队列实现异步秒杀
- 3-2. 业务
- 超卖问题
- 一人一单问题
- 一人一单并发安全问题
- 调用lua脚本保证查询和删除锁原子性
- 3-3. 小技巧
- 解决事务失效的3种方法
- 4. 达人探店
- 4-1. 技术点
- 利用Redis的SortedSet结构记录点赞的用户id,实现点赞排行榜
- 数据库返回按 list 集合顺序的元素
- 4-2. 业务
- 发布文章
- 文章点赞
- 点赞排行榜
- 5. 好友关注
- 5-1. 技术点
- 利用 set 求交集获取共同关注好友
- Feed流推送
- 5-2. 业务
- 关注取关
- 共同关注
- 关注推送
- 6. 附近的商户
- 6-1. 技术点
- GEO数据结构的使用
- 6-2. 业务
- 使用GEO查询附近的商户
- 7. 用户签到
- 7-1. 技术点
- 7-2. 业务
- 利用位图记录签到
- 统计连续签到次数
- 8. UV统计
- 8-1. 技术点
- 8-2. 业务
- 利用 HyperLogLog 统计 UV
1. 短信登录
1-1. 技术点
redis存储token
拦截器刷新token有效期
1-2. 业务
登录注册
2. 商户查询缓存
1-1. 技术点
缓存更新策略
缓存穿透
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库
解决方案:
- 缓存空对象
- 布隆过滤器
缓存雪崩
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis)服务宕机,导致大量请求到达数据库,带来巨大压力。
解决方案:
- 给不同的Key的TTL添加随机值
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
缓存击穿
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的ky突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
解决方案:
- 互斥锁
setnx
- 逻辑过期
1-2. 业务
查询缓存的商铺信息
3. 优惠卷秒杀
3-1. 技术点
全局唯一ID
全局唯一D生成策略:
- UUID
- Redis自增
- snowflake算法
- 数据库自增
Redis自增ID策略:
- 每天一个key,方便统计订单量
- ID构造是时间戳+计数器
乐观锁
版本号实现方案:在修改数据的时候,判断版本号是否和之前查询到的版本号一致。
cas实现方案:不再使用版本号,而是用查询出来的数据代替版本号。根据查询出来的数据再来判断是否可以进行修改,如果一致则可以修改。
基于Redis实现分布式锁
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。
分布式锁的实现方案:
基于Redis的分布式锁实现思路:
- 利用set nx ex获取锁,并设置过期时间,保存线程标示
- 释放锁时先判断线程标示是否与自己一致,一致则删除锁。(编写lua脚本保证2个操作的原子性)
特性:
- 利用set nxi满足互斥性
- 利用set ex保证故障时锁依然能释放,避免死锁,提高安全性
- 利用Redis集群保证高可用和高并发特性
基于Redisson实现分布式锁
Redisson是一个基于Redis实现的分布式工具,它提供了一系列的分布式服务、分布式数据结构、分布式锁等。
Redisson分布式锁原理:
-
可重入:利用hash结构记录线程id和重入次数
-
可重试:利用信号量和发布订阅功能实现等待、唤醒,获取锁失败的重试机制
-
超时续约:利用watchDog(就是一个定时任务),每隔一段时间(releaseTime / 3),重置锁的超时时间
Redisson实现可重入锁的原理
其实现原理和 JDK 的 ReentrantLock
类似。
实现原理:
1、尝试获取锁时
- 首先判断该锁是否存在,如果存在,再判断该锁是否是自己的,不是自己的则获取锁失败,如果是自己的,则将该锁的计数 + 1 并重置锁的有效期。利用hash结构记录线程id和可重入次数。
- 如果锁不存在,则成功获取锁,锁的计数 + 1,设置锁的有效期。
2、尝试释放锁时
- 首先判断该锁是否是自己的,如果不是自己的,则不用释放。
- 如果是自己获取的锁,则将该锁的计数 - 1。之后判断锁的计数是否为 0,如果为0,则释放锁,不为0, 则重置锁的有效期。
分别用 lua
脚本来编写上面尝试获取锁和释放锁的语句,保证原子性。
Redisson实现锁重试和WatchDog机制的原理
锁重试
- 尝试获取锁时,首先判断锁的 ttl 是否过期,如果过期了,则获取锁成功。如果获取锁时没有设置锁的过期时间,则会开启 WatchDog(一个定时任务,定时刷新锁的过期时间)。
- 尝试获取锁时,锁还没有过期,则会订阅并等待锁的释放信息。
- 当释放锁时,会发布释放信息,并取消 WatchDog
WatchDog
- 一个定时任务,定时刷新锁的过期时间
Redisson的multiLock
- 原理:多个独立的Redis节点,必须在所有节点都获取重入锁,才算获取锁成功
- 缺陷:运维成本高、实现复杂
Redis消息队列实现异步秒杀
消息队列(Message Queue),字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色:
- 消息队列:存储和管理消息,也被称为消息代理(Message Broker)
- 生产者:发送消息到消息队列
- 消费者:从消息队列获取消息并处理消息
Redis提供了三种不同的方式来实现消息队列:
- list数据结构:基于List结构模拟消息队列
- PubSub:基本的点对点消息模型
- Stream数据结构:比较完善的消息队列模型
基于List的消息队列有哪些优缺点?
优点
- 利用Redis存储,不受限于JVM内存上限
- 基于Redis的持久化机制,数据安全性有保证
- 可以满足消息有序性
缺点:
- 无法避免消息丢失
- 只支持单消费者
基于PubSub的消息队列
基于PubSub的消息队列有哪些优缺点?
优点:
- 采用发布订阅模型,支持多生产、多消费
缺点:
- 不支持数据持久化
- 无法避免消息丢失
- 消息堆积有上限,超出时数据丢失
3-2. 业务
超卖问题
乐观锁实现方案:
- 版本号,修改数据的时候判断和之前查询的版本号是否一致。
- CAS法,不再使用版本号,而是用查询出来的数据代替版本号。根据查询出来的数据再来判断是否可以进行修改,如果一致则可以修改。
一人一单问题
一人一单并发安全问题
调用lua脚本保证查询和删除锁原子性
调用RedisTemplate
的execute
方法来执行lua脚本
3-3. 小技巧
解决事务失效的3种方法
4. 达人探店
4-1. 技术点
利用Redis的SortedSet结构记录点赞的用户id,实现点赞排行榜
数据库返回按 list 集合顺序的元素
4-2. 业务
发布文章
文章点赞
只允许一人点一次赞
利用Redis的SortedSet结构记录点赞的用户id
点赞排行榜
将用户点赞的时间毫秒值作为 SortedSet 的 score,之后按 score 的顺序返回。
5. 好友关注
5-1. 技术点
利用 set 求交集获取共同关注好友
Feed流推送
Feed流产品有两种常见模式:
-
Timeline:不做内容筛选,简单的按照内容发布时间排序,常用于好友或关注。例如朋友圈
- 优点:信息全面,不会有缺失。并且实现也相对简单
- 缺点:信息噪音较多,用户不一定感兴趣,内容获取效率低
-
智能排序:利用智能算法屏蔽掉违规的、用户不感兴趣的内容。推送用户感兴趣信息来吸引用户
-
优点:投喂用户感兴趣信息,用户粘度很高,容易沉迷
-
缺点:如果算法不精准,可能起到反作用
-
5-2. 业务
关注取关
共同关注
关注推送
关注推送也叫做Feed
流,直译为投喂。为用户持续的提供“沉浸式”的体验,通过无限下拉刷新获取新的信息。
6. 附近的商户
6-1. 技术点
GEO数据结构的使用
6-2. 业务
使用GEO查询附近的商户
7. 用户签到
7-1. 技术点
位图(BitMap)
7-2. 业务
利用位图记录签到
统计连续签到次数
8. UV统计
8-1. 技术点
- UV: 全称Unique Visitor,也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站,只记录1次。
- PV: 全称Page View,也叫页面访问量或点击量,用户每访问网站的一个页面,记录1次PV,用户多次打开页面,则记录多次PV。往往用来衡量网站的流量。