作者:苏三说技术
链接:https://juejin.cn/post/7325132133168971813
前言
Redis是一种非常优秀的基于KV的键值对缓存数据库,有非常不错的性能和稳定性,无论是在工作中,还是在面试中,都经常会出现。因此,有必要深入研究并在工作中使用它。今天,我将与大家分享我在实际工作中如何使用Redis
的。
1. 统计访问次数
官方网站首页需统计访问次数,使用Redis可简化操作。
定义Key值为OFFICIAL_INDEX_VISIT_COUNT
,使用incr
命令给value
值加1
。
incr OFFICIAL_INDEX_VISIT_COUNT
可以用incrby
命令加1,如下一次性加5:
incrby OFFICIAL_INDEX_VISIT_COUNT 5
2. 获取分类树菜单
在很多网站都有分类树的功能,菜单的树展示形式,如果没有静态html页面的话就需要调用接口,树一般都会递归去实现获取,为了性能考虑一般我们都会将菜单树的数据缓存,将树的json
数据缓存到Redis中,后面再读取菜单的时候就不用重新计算,直接读取Redis即可。不过,如果是菜单树经常变动的场景不适合此。
所以我们可以使用定时将菜单树的数据异步缓存到Redis中。
定义一个key,比如:MALL_CATEGORY_TREE
。
然后接口中直接使用 MALL_CATEGORY_TREE
这个key从缓存中获取数据即可。
可以直接用key/value
字符串保存数据。
不过需要注意的是,如果分类树的数据非常多可能会出现大key的问题。
3. 做分布式锁
使用Redis做分布式锁应该是Redis的使用者最经常听说的一个场景了,Redis实现的分布式锁相对于其他的分布式锁,性能更好,相信大家也听过什么Redis+Lua王炸组合什么的。
我们使用下面这段代码可以加锁:
try{String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);if ("OK".equals(result)) {return true;}return false;
} finally {unlock(lockKey);
}
但上面这段代码在有些场景下,会有一些问题,释放锁可能会释放了别人的锁。所以优化的点还有很多,比如区分谁加的锁,就释放自己的锁,不能释放别人的锁,可以在锁信息里加入线程ID来区分。
说实话Redis分布式锁虽说很常用,但坑也挺多的,如果用不好的话,很容易踩坑。
4. 做排行榜
网站的排行版,微博的排版行、各种社交APP的排行榜,商城的销量排行榜,游戏的巅峰排名等等。
通常情况下,我们可以使用Sorted Set
保存排行榜的数据。
使用ZADD
可以添加排行榜的数据,使用ZRANGE
可以获取排行榜的数据。
例如:
ZADD rank:score 100 "奈飞"
ZADD rank:score 90 "youtobe"
ZADD rank:score 80 "小破站"
ZRANGE rank:score 0 -1 WITHSCORES
返回数据:
1) "奈飞"
2) "100"
3) "youtobe"
4) "90"
5) "小破站"
6) "80"
5. 记录用户登录状态
系统登录态缓存。用户登录系统之后,缓存用户登录信息。
使用Redis保存用户登录状态,有个好处是它可以设置一个过期时间,这样可以实现用户Token的到期已经后续的续期操作等。
比如:该时间可以设置成30分钟。
jedis.set(userId, userInfo, 1800);
在Redis内部有专门的job,会将过期的数据删除,也有获取数据时实时删除的逻辑。
后面本文更新Redis过期策略,欢迎关注。
6. 限流
使用Redis也可以实现限流
。
最简单的,保存用户IP,限制该IP一分钟内的访问次数,超过该次数限制该IP访问,设置过期时间,一天之后到期允许该IP继续访问。
7. 位统计
在网站的统计页面中,统计一周连续登录用户或者一个月内登录过的用户。
如果使用关系型数据库的话很麻烦,但是使用Redis的Bitmap可以很方便的实现类似功能。
bitmap
是二进制的 byte
数组,也可以简单理解成是一个普通字符串。它将二进制数据存储在byte数组中以达到存储数据的目的。
保存数据命令使用setbit
,语法:
setbit key offset value
具体示例:
setbit user:view:2024-04-01 123456 1
往bitmap
数组中设置了用户 id=123456
的登录状态为1
,标记2024-04-01
已登录。
然后通过命令getbit
获取数据,语法:
getbit key offset
具体示例:
getbit user:view:2024-04-01 123456
如果获取的值是1,说明这一天登录了。
如果我们想统计一周内连续登录的用户,只需要遍历用户id,根据日期中数组中去查询状态即可。
8. 缓存加速
这个和上面分类的菜单树缓存差不多,都是把需要使用频繁访问的数据缓存在Redis中,通过缓存读取策略进行控制缓存的读取存入。
1、查询订单,缓存中存在,直接返回数据。
2、缓存中不存在,查询数据库,如果数据库中数据存在,将数据保存到缓存中,返回数据。
3、如果数据库数据不存在,返回用户数据不存在。
流程图如下:
但使用缓存加速的业务场景,需要注意一下,可能会出现:缓存击穿、穿透和雪崩等问题,感兴趣的小伙伴,可以看看我的另一篇文章《美团面试拷打:Redis 缓存穿透、缓存击穿、缓存雪崩区别和解决方案》,里面有非常详细的介绍。
9. 做消息队列
我们说起队列经常想到是:Kafka
、RabbitMQ
、RocketMQ
等这些分布式消息队列。
Redis 实现消息队列是使用的PubSub功能,PubSub(发布订阅)是 Redis2.0 版本引入的消息传递模型。
顾名思义,消费者可以订阅一个或多个channel,生产者向对应channel发送消息后,所有订阅者都能收到相关消息。对应channel发送消息后,所有订阅者都能收到相关消息。
在 java
代码中可以实现MessageListener
接口,来消费队列中的消息。
@Slf4j
@Component
public class RedisMessageListenerListener implements MessageListener {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Overridepublic void onMessage(Message message, byte[] pattern) {String channel = new String(pattern);RedisSerializer<?> valueSerializer = redisTemplate.getValueSerializer();Object deserialize = valueSerializer.deserialize(message.getBody());if (deserialize == null) return;String md5DigestAsHex = DigestUtils.md5DigestAsHex(deserialize.toString().getBytes(StandardCharsets.UTF_8));Boolean result = redisTemplate.opsForValue().setIfAbsent(md5DigestAsHex, "1", 20, TimeUnit.SECONDS);if (Boolean.TRUE.equals(result)) {log.info("接收的结果:{}", deserialize.toString());} else {log.info("其他服务处理中");}}
}
10. 生成全局ID
在有些需要生成全局ID的业务场景,其实也可以使用Redis。
可以使用 incrby
命令,利用原子性操作,可以执行下面这个命令:
incrby userid 10000
在分库分表的场景,对于有些批量操作,我们可以从Redis中,一次性拿一批id出来,然后给业务系统使用。
如果这篇文章对您有所帮助,或者有所启发的话,帮忙点个关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
wx 搜索《醉鱼Java》,回复面试、获取2024面试资料