先给大家附上其他几篇文章,感兴趣的自行开车导航
Redis过期策略和持久化机制全面揭秘,教你如何合理配置
【深入浅出Redis 一】从版本特性到数据类型到线程模型,带你了解Redis的核心特性和应用场景!
一次redis OOM问题分析解决,rdbtools安装分析redis内存
Redis管道及缓存问题解析
一、什么是Redis管道
Redis管道(pipeline)是一种在网络层面上实现的批量操作机制,它可以将多个命令一次性发送给Redis服务器,而不需要等待每个命令的响应。这样可以减少客户端和服务器之间的网络通信次数,提高命令执行效率。
Redis管道的原理是基于TCP协议的请求/响应模型,即客户端发送一个请求后,必须等待服务器的响应才能发送下一个请求。如果客户端有多个请求要发送,那么就会产生多次网络往返延迟(RTT),影响性能。
而通过管道技术,客户端可以将多个请求打包成一个数据包发送给服务器,服务器也会将多个响应打包成一个数据包返回给客户端,从而减少网络延迟。并且有些场景也避免了事务问题,比如设置key和设置时间
二、如何使用Redis管道
使用Redis管道非常简单,只需要在发送命令之前调用pipeline()方法,创建一个管道对象,然后将命令添加到管道对象中,最后调用execute()方法执行所有命令并返回结果列表。下面是一个java示例:
Jedis jedis = new Jedis("localhost");
Pipeline p = jedis.pipelined();
p.set("foo","bar"); p.get("foo");
List<Object> results = p.syncAndReturnAll();
需要注意的是,使用管道并不保证原子性,即如果有其他客户端在管道执行过程中修改了数据,那么可能会影响管道中的命令结果。如果需要保证原子性,可以使用事务(transaction)或Lua脚本。
什么是缓存雪崩、缓存穿透、缓存击穿
Redis作为目前使用最广泛的缓存,相信大家都不陌生。但是使用缓存并没有这么简单,还要考虑缓存雪崩、缓存击穿、缓存穿透的问题,这些问题都可能导致大量请求直接打在数据库上,造成数据库压力过大甚至宕机。
那么这些问题分别是什么意思呢?
缓存雪崩
缓存雪崩是指当某一个时刻出现大规模的缓存失效的情况(比如Redis宕机或者大量key同时过期),导致大量请求直接打在数据库上,造成数据库压力巨大甚至宕机。这就像一场雪崩一样,一旦发生就很难控制。
缓存穿透
缓存穿透是指当用户请求的数据在缓存和数据库中都不存在时(比如请求一个不存在的id),导致每次请求都会到数据库中查询,从而给数据库造成压力。这就像穿透了缓存一样,直接访问数据库。
缓存击穿
缓存击穿是指当一个热点key在缓存中过期时,同时有大量请求过来查询这个key,导致大量请求直接打在数据库上,造成数据库压力剧增。这就像击穿了缓存一样,直接访问数据库。
四、如何解决缓存问题
说到底就是最终落到数据库了,如果有大量请求数据库就会被群殴致死,针对上面三种缓存问题,我们可以采用不同的解决方案,下面分别介绍。
解决缓存雪崩
解决缓存雪崩的关键在于避免大规模的key同时失效,有几种方法:
- 设置不同的过期时间。避免缓存设置相同的过期时间,可以在设置过期时间时增加一个随机值,使得过期时间均匀分布。(推荐)
- 数据预热。对于即将到来的大流量请求,可以提前将数据加载到缓存中,并设置不同的过期时间。
- 保证Redis服务高可用。为了防止Redis宕机导致缓存雪崩的问题,可以搭建Redis集群或哨兵模式,提高Redis的容灾性。
- 使用熔断机制。当流量到达一定的阈值时,可以直接返回“系统拥挤”之类的提示,防止过多的请求打在数据库上。
- 提高数据库的容灾能力。可以使用分库分表、读写分离、负载均衡等策略,提高数据库的承载能力和稳定性。
解决缓存穿透
解决缓存穿透的关键在于避免查询不存在的数据,有以下几种方法:
- 参数校验。对用户请求的参数进行校验,对于明显错误或非法的参数,直接拦截返回。
- 缓存空值。对于查询为空的数据,也可以将其缓存在Redis中,并设置一个较短的过期时间(比如30秒),这样可以防止短时间内大量重复查询。
- 使用布隆过滤器。布隆过滤器是一种数据结构,利用极小的内存,可以判断大量数据“一定不存在或者可能存在”。对于缓存穿透,我们可以将所有可能存在的key哈希到一个足够大的布隆过滤器中,用户请求时先判断key是否在布隆过滤器中,如果不在就直接返回。(推荐)
解决缓存击穿
解决缓存击穿的关键在于避免热点key失效时造成大量请求打在数据库上,有以下几种方法:
- 设置热点数据永不过期。对于某些需要频繁访问的数据,可以将其设置为永不过期的key,当然这种方式比较粗暴,对于某些业务场景是不适合的。
- 定时更新。对于某些有固定过期时间的热点数据,可以在其快要过期前通过定时任务去更新它,并重新设置过期时间。
- 使用互斥锁。当缓存失效时,只有拿到锁的请求才能去查询数据库并更新缓存,其他请求则等待或重试。这样可以降低同时打在数据库上的请求数量。
缓存和数据库双写一致性问题
在使用Redis作为缓存时,我们通常需要在数据发生变化时,同时更新缓存和数据库,以保证数据的一致性。但是由于网络延迟或者其他原因,可能会导致缓存和数据库的数据不一致。
例如,如果先更新缓存再更新数据库,那么如果缓存更新成功而数据库更新失败,就会导致缓存中的数据是脏数据。
如果先更新数据库再更新缓存,那么如果数据库更新成功而缓存更新失败,就会导致缓存中的数据是旧数据。
那么如何解决这个问题呢?
解决方案
针对这个问题,有几种解决方案:
- 先删除缓存再更新数据库。这种方式可以保证数据库中的数据是最新的,但是可能会导致短时间内的缓存不命中,增加数据库压力。为了避免这个问题,可以在删除缓存后延迟一段时间(比如1秒)再更新数据库,这样可以减少其他请求查询到空缓存的概率。
- 先更新数据库再删除缓存。这种方式可以保证缓存中的数据是最新的,但是可能会导致删除缓存失败,导致缓存过期。为了避免这个问题,可以在删除缓存后延迟一段时间(比如1秒)再次删除缓存,减少其他请求查询到过期缓存的概率。
- 使用消息队列。将更新操作放入消息队列中,保证顺序执行,并且可以重试失败的操作。这样可以保证最终一致性,但是可能会增加系统复杂度和延迟。
- 使用分布式锁。在更新操作前获取一个分布式锁,保证同一时刻只有一个请求能够更新数据,并且在更新完成后释放锁。这样可以保证强一致性,但是可能会降低并发性能和可用性
开发过程中看起来我们都是先处理数据再删缓存的
其他问题
- 缓存穿透、击穿、雪崩的监控和预警。为了及时发现和处理缓存问题,需要对Redis的性能和状态进行监控和预警,比如监控Redis的QPS、响应时间、内存使用率、命中率等指标,当发现异常时及时报警并处理。
- 缓存的合理使用。不是所有的数据都适合放入缓存中,需要根据数据的访问频率、更新频率、大小等因素进行权衡。一般来说,适合放入缓存的数据应该满足以下条件:
- 访问频率高,更新频率低。这样可以提高缓存命中率,减少数据库压力。
- 数据量小,计算量大。这样可以减少网络传输开销,提高计算效率。
- 数据一致性要求不高。这样可以容忍缓存和数据库之间的短暂不一致。
业务场景举例
下面举几个使用Redis作为缓存的业务场景:
- 商品详情页。商品详情页是电商网站中最常访问的页面之一,每个商品都有一个唯一的id作为key,商品的基本信息、库存、评价等作为value。这些数据一般不会频繁变化,但是访问量很大,所以适合放入Redis中作为缓存。当用户访问某个商品详情页时,先从Redis中查询,如果命中则直接返回,如果不命中则从数据库中查询,并将结果放入Redis中,并设置一个过期时间(比如1小时)。当商品信息发生变化时(比如库存变化),需要同时更新数据库和缓存。
- 热门排行榜。热门排行榜是展示某个维度下最受欢迎的内容的列表,比如电影排行榜、音乐排行榜、新闻排行榜等。这些数据一般会根据某个指标(比如评分、播放量、点击量等)进行排序,并定期更新。这些数据适合放入Redis中作为缓存,因为它们访问量大,计算量大,数据量小。可以使用Redis的有序集合(sorted set)来实现排行榜功能,每个元素对应一个内容id,分数对应一个指标值。当用户访问某个排行榜时,先从Redis中查询,如果命中则直接返回,如果不命中则从数据库或其他服务中查询,并将结果放入Redis中,并设置一个过期时间(比如10分钟)。当内容的指标值发生变化时(比如评分变化),需要同时更新数据库和缓存。
- 验证码。验证码是一种用于防止机器人或恶意用户的一种安全机制,一般在用户注册、登录、下单等场景中使用。验证码一般有一个有效期(比如5分钟),并且只能使用一次。这些数据适合放入Redis中作为缓存,因为它们访问频率高,更新频率高,数据量小,数据一致性要求不高。可以使用Redis的字符串(string)来实现验证码功能,每个key对应一个用户id或手机号,value对应一个验证码。当用户请求发送验证码时,生成一个随机的验证码,并将其存入Redis中,并设置一个过期时间(比如5分钟)。当用户输入验证码时,先从Redis中查询,如果命中则验证是否正确,如果不命中则提示验证码已过期或不存在。当验证码验证成功或失败时,需要删除Redis中的key。
今天就介绍到这里,感谢大家
感兴趣的自行开车导航
Redis过期策略和持久化机制全面揭秘,教你如何合理配置
【深入浅出Redis 一】从版本特性到数据类型到线程模型,带你了解Redis的核心特性和应用场景!
一次redis OOM问题分析解决,rdbtools安装分析redis内存