Redis 是一种高性能的内存数据库,广泛应用于各种场景中。以下是 Redis 的常见使用场景:
缓存
String类型
例如:热点数据缓存(例如报表、明星出轨),对象缓存、全页缓存、可以提升热点数据的访问数据。
数据共享分布式
String 类型,因为 Redis 是分布式的独立服务,可以在多个应用之间共享
例如:分布式Session
org.springframework.session
spring-session-data-redis
nt类型,incrby,利用原子性
incrby userid 1000
分库分表的场景,一次性拿一段
String类型的bitcount(1.6.6的bitmap数据结构介绍)
字符是以8位二进制存储的
setk1 a
setbit k1 6 1
setbit k1 7 0
get k1
/* 6 7 代表的a的二进制位的修改
a 对应的ASCII码是97,转换为二进制数据是01100001
b 对应的ASCII码是98,转换为二进制数据是01100010
因为bit非常节省空间(1 MB=8388608 bit),可以用来做大数据量的统计。
*/
例如:在线用户统计,留存用户统计
String 或hash。所有String可以做的hash都可以做
list,双向链表,直接作为timeline就好了。插入有序
自带一个随机获得值
spop myset
sdiffset1 set2
//获取交集(intersection )
sinter set1 set2
//获取并集
sunion set1 set2
String 类型setnx方法,只有不存在时才能添加成功,返回true
public static boolean getLock(String key) {
Long flag = jedis.setnx(key, "1");
if (flag == 1) {
jedis.expire(key, 10);
}
return flag == 1;
}
public static void releaseLock(String key) {
jedis.del(key);
}
代码要执多个redis命令,不加锁的情况下如何保证原子性 --分布式锁
lua脚本:https://segmentfault.com/a/1190000009811453
Redis 中执行 Lua 脚本
Lua脚本功能为Redis开发和运维人员带来如下三个好处:
●Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
●Lua脚本可以帮助开发和运维人员创造出自己定制的命令,并可以将这些命令常驻在Redis内存中,实现复用的效果。
●Lua脚本可以将多条命令一次性打包,有效地减少网络开销。
int类型,incr方法
以访问者的ip和其他信息作为key,访问一次增加一次计数,超过次数则返回false
int类型,increment()方法
例如:文章的阅读量、微博点赞数、允许一定的延迟,先写入Redis再定时同步到数据库
使用场景: 一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等
/**
* 利用redis做计数器
* 可以处理业务上面的的一些访问次数之类的
* 例如:文章的点赞数,阅读量,允许有一点的延迟效果,先保存到redis中,然后在同步到数据库当中
*/
@RequestMapping("hello")
public void count() {
/**
* 判断是否到达次数
*/
Boolean aBoolean = invokeExceededTimes("time_key2",1,3);
if (aBoolean) {
LOGGER.info("可以访问");
}else {
LOGGER.info("请求次数达标了");
}
}
/**
* 判断同一个key在规定时间内访问次数是否到达了最高值
* @param key 键
* @param days 时间
* @param count 一定时间内的访问次数
* @return
*/
public Boolean invokeExceededTimes(String key, int days, int count) {
LOGGER.info("key值:{}",key);
// 判断在redis中是否有key值
Boolean redisKey = stringRedisTemplate.hasKey(key);
if (redisKey) {
// 获取key所对应的value
Integer hasKey =Integer.parseInt((String)stringRedisTemplate.opsForValue().get(key));
if (hasKey >= count) {
return false;
}
// 对value进行加1操作
stringRedisTemplate.opsForValue().increment(key,1);
return true;
}else {
// 如果没有key值,对他进行添加到redis中
stringRedisTemplate.opsForValue().set(key,"1",days,TimeUnit.DAYS);
}
return true;
}
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(SortedSet)也使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的 10 个用户–我们称之为“user_scores”,
我们只需要像下面一样执行即可:
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games 就是一个很好的例子,用 Ruby 实现的,它的排行榜就是使用 Redis 来存储数据的,你可以在这里看到
List提供了两个阻塞的弹出操作:blpop/brpop,可以设置超时时间
blpop:blpop key1 timeout 移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
brpop:brpop key1 timeout 移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
上面的操作。其实就是java的阻塞队列。学习的东西越多。学习成本越低
● 队列:先进先除:rpush blpop,左头右尾,右边进入队列,左边出队列
● 栈:先进后出:rpush brpop
发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 客户端可以订阅任意数量的频道。
客户端向频道中发布消息,多个订阅者可以同时收到
Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得 Redis 能作为一个很好的消息队列平台来使用。Redis 作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。
如果你快速的在 Google 中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用 Redis 创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用 Redis 作为 broker,你可以从这里去查看。
Redis 异步队列
答:一般使用 list 结构作为队列,rpush 生产消息,lpop 消费消息。当 lpop 没有消息的时候, 要适当
sleep 一会再重试。
如果对方追问可不可以不用 sleep 呢?
list 还有个指令叫 blpop,在没有消息的时候,它会阻塞住直到消息到来。如果对方追问能不能生产一
次消费多次呢? 使用 pub/sub 主题订阅者模式, 可以实现1:N 的消息队列。
如果对方追问 pub/sub 有什么缺点?
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如 RabbitMQ 等。
如果对方追问 redis 如何实现延时队列?
我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话, 怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用 sortedset,拿时间戳作为score,消息内容作为 key 调用 zadd 来生产消息,消费者用 zrangebyscore 指令获取 N 秒之前的数据轮询进行处理。到这里, 面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指, 在椅子背后。
redis过期监听
redis的zset
redisson
redis延迟队列 的原理
基于上述原理,Redis延迟队列的工作流程通常如下:
● 将待执行的任务或消息以有序集合的形式存储在Redis中,其中成员为消息内容,分数为消息的执行时间。
● 使用定时任务轮询机制,定期检查有序集合中是否有到期的任务。可以通过Redis的定时任务调度器或外部定时任务程序实现。
● 当检测到有任务的执行时间已到达时,从有序集合中取出该任务,并执行相应的处理逻辑。
通过以上流程,可以实现延迟处理任务或消息的功能。Redis延迟队列通常具有较高的性能和可靠性,并且易于实现和部署。然而,需要注意的是,Redis延迟队列通常只能提供基本的延迟功能,如果需要更复杂的消息队列功能(如消息重试、消息持久化等),可能需要结合其他消息中间件来实现。
Redis延迟队列是一种常用的消息队列模式,用于延迟处理任务或消息。其原理通常基于两个主要组件:有序集合(Sorted Set)和定时任务轮询。
分布式锁
限流
计数器
我们项目中有用到,主要用了3个场景:
1.缓存相关的场景,我们是做在线教育的,内容模板会有很多课程相关,这些数据在DB单表有5.600W;如果走mysql查询会很比较慢,用户体验感比较差,并发也上不去。所以我们做了些接口缓存、课程内容的对象缓存。提升了性能的同时,还解决了本地缓存不一致问题。
2.同时,我们由于它是分布式的,并且可以设置过期时间,也会用来保存用户token。因为token也是有过期时间的,用Redis来保存刚好满足。
3.我们还用它去基于日期+incr自增指令实现了一个分布式ID。因为我们的课程ID比较大,需要分库分表,数据库自增满足不了我们需求。