为什么要用redis做一层缓存,相比直接查mysql有什么优势?
首先介绍Mysql自带缓存机制的问题:
MySQL 的缓存机制存在一些限制和问题,它自身带的缓存功能Query Cache只能缓存完全相同的查询语句,对于稍有不同的查询语句,即使查询的是相同的数据,也无法命中缓存。而且数据库中数据发生变化时,也不会自动更新;此外Query Cache 的缓存命中率也会受到并发访问的影响,当多个并发查询同时访问同一个数据时,可能会导致缓存的命中率下降。
然后介绍相比起MySQL ,redis缓存的一些优势:
1.数据类型丰富
Redis 支持多种数据类型,包括字符串、哈希、列表、集合和有序集合等。多种数据类型可以保证它能实现更多功能(例如排序)
2.可以内存管理
Redis 将所有数据存储在内存中,因此读写速度非常快。同时,Redis 也提供了一些内存管理的功能,例如可以设置数据的过期时间,当数据过期时会自动从内存中删除。避免浪费内存资源。
3.支持高并发
Redis 是单线程的,通过使用事件驱动的方式处理客户端请求。单线程模型,可以避免了多线程之间的竞争,省去了多线程切换带来的时间和性能上的开销,也不会导致死锁
4.支持持久化
Redis 提供了两种持久化方式,分别是 RDB(Redis Database)和 AOF(Append Only File)。RDB 是将内存中的数据定期保存到磁盘上,而 AOF 则是将每个写操作追加到文件中。这两种方式可以保证数据的持久化,即使 Redis 重启或崩溃,也可以通过加载持久化文件来恢复数据。
如何保证缓存与数据库的操作的同时成功或失败?
1.双写模式(Cache-Aside Pattern)
先更新数据库,再删除缓存
如果缓存删除失败,可以通过重试机制或异步队列处理
2. 事务消息
使用支持事务的消息队列(如RocketMQ),将缓存操作作为事务的一部分。只有数据库操作成功后,才会提交缓存操作
3. 数据库binlog监听
使用Canal、Debezium等工具监听数据库binlog,将变更事件同步到缓存,确保最终一致性。
缓存穿透、缓存击穿、缓存雪崩,什么是缓存xx?如何解决这个问题?这几个解决方案各自的优势和缺点分别是什么?
缓存穿透
介绍:
指客户端请求的数据在缓存和数据库中都不存在,缓存永远不会生效,请求都会打到数据库。
解决方案:
方案 | 介绍 | 优点 | 缺点 |
缓存空对象 | 对查询结果为null的key也进行缓存 | 实现简单 | 可能缓存大量无效数据 |
布隆过滤器 | 底层是一个二进制数组,进行哈希映射。(数据key先访问布隆滤波器,不存在直接返回,存在进入redis) | 空间效率高 | 存在误判的情况。(哈希冲突) 存在数据更新问题。(数据库更新要同步更新布隆过滤器) |
数据校验 | 加强用户id校验;做好数据的基础格式校验 | 轻量级 | 防御范围有限 |
热点参数限流 | 能力有限 |
缓存雪崩
介绍:
同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
解决方案:
方案 | 介绍 | 优点 | 缺点 |
错开过期时间 | 给不同的Key的TTL(过期时间)添加随机值 | 简单 | 无法应对宕机 |
多级缓存 | 本地缓存+分布式缓存 | 可靠性高 | 成本高 |
熔断降级 | 缓存不可用时启用降级策略 | 保护系统 | 影响用户体验(因为一直返回默认信息) |
集群部署 | Redis集群保证高可用 |
缓存击穿
介绍:
热点Key问题的特殊情况,一个被高并发访问并且缓存重建业务较复杂的key突然失效了(过期时间),无数请求都无法从缓存中获取数值转而都开始访问数据库。
解决方案:
方案 | 介绍 | 优点 | 缺点 |
互斥锁 | 线程并行访问会崩溃的话就把访问变成串行,tryLock+double check实现 | 没有额外内存消耗 保证一致性 实现简单 | 可能性能瓶颈 可能死锁 |
逻辑过期 | 将过期时间设置到redis的value中,线程1查缓存,通过value判断出来已过期,获得锁,然后开启新线程进行缓存数据重建以及重新设置过期时间。期间有其他线程访问缓存数据只会返回过期数据。 | 线程无需等待 性能较好 | 不保证一致性 有额外内存消耗(新线程) 实现较复杂 |