1.讲一讲用户输入手机号和获取验证码之后是怎么操作的
实则是在问为何用Redis而非直接入库
考察点:
1.高并发场景下的系统设计能力:是否理解数据库与缓存的职责划分。
2.缓存应用场景:是否知道用 Redis 应对高频、短时存储需求(如验证码有效期通常 5-10 分钟)。
3.防刷机制:是否考虑到频繁请求对数据库的压力(如恶意用户每秒请求 1000 次验证码,直接入库会导致数据库 IO 爆炸)。
对应项目场景:
用户登录 / 注册模块的 “发送验证码” 功能。例如黑马点评的手机验证码登录场景,需支持高并发请求,且验证码具有时效性。
答案核心:
1.Redis的优势:内存读写速度快,支持过期时间(expire args),原子操作(set key value NX EX 120),适合存储存储,高频访问的数据。
2.数据库的职责:存储用户的核心数据(手机号,密码等),避免验证码这种临时数据污染数据库。
3.防刷策略:结合Redis的INCR命令限制手机号每分钟获取验证码的次数,超出则返回“请求频繁”。
2.验证码验证成功之后是否应该删除验证码数据? 还是说设置过期时间就行?
考察点:
1.安全意识:是否意识到为删除的验证码会被重复利用(如暴力破解)。
2.缓存管理原则:验证通过后,是否需要立即清除,避免冗余存储。
对应项目场景:
验证码校验逻辑(如黑马点评中,用户输入验证码后点击 “登录” 的接口)。
答案核心:
必须删除Redis数据:
1.防止重放攻击:若仅依赖过期时间,恶意用户可能在有效期内用同一验证码反复尝试登录。
2.减少内存占用:及时删除无效数据,避免 Redis 存储膨胀。
实现方法:
验证通过后,立即用DEL key来删除验证码。若使用 Spring Cache 等框架,可通过@CacheEvict
注解自动清理。
3.用cache aside 是否有可能redis里面长期存着脏数据
考察点:
1.缓存与数据库一致性:是否理解缓存更新策略的缺陷(如先删缓存再更新数据库的漏洞)。
2.并发能力场景分析:能否拆解读/写操作的时序问题,是否知道脏数据的产生条件。
对应项目场景:
任何涉及 “缓存 + 数据库” 的读写场景,如黑马点评的用户信息查询(先查 Redis,再查数据库)。
场景解析:
线程A读数据:Redis无缓存,从MySQL读取数据(v1);
线程B写数据:更新MySQL为v2,并且删除Redis缓存(此时Redis无数据);
线程A写数据:将v1写入Redis,导致脏数据产生。
问题本质是读操作的并发时序超过写操作,导致旧数据被缓存。这是 “先删缓存再更新数据库” 策略的典型漏洞。
解决方案:
1.延迟双删:写操作流程:更新数据库 → 删除 Redis → 延迟一段时间(如 1 秒)后再次删除 Redis(确保读操作已完成)。适用场景:读多写少,且允许短时间脏数据的场景(如商品详情页)。
2.消息队列异步处理:写操作时,将 “删除缓存” 请求发送到消息队列,异步执行(避免阻塞主线程),并通过重试机制保证可靠性。
3.写穿透(Write Through):强制写操作先更新数据库,再更新缓存(而非删除缓存),确保缓存与数据库实时一致,但会增加写操作耗时。
4.设置redis过期时间的原子性问题
考察点:
1.Redis 命令特性:是否了解单线程模型下的原子操作机制。
2.并发场景下的正确性:如何避免 “设置值” 和 “设置过期时间” 的分步操作导致的竞态条件。
对应项目场景:
需要原子性设置值和过期时间的场景,如验证码存储、分布式锁(SET key value NX PX 3000)。
答案核心:
1.原子性原理:Redis 是单线程模型,所有命令按顺序执行。SET key value NX EX120 是单条命令,服务端会原子性完成 “设置值” 和 “设置过期时间”,无需担心多线程并发问题。
2.对比非原子操作:若用SET key value + EXPIRE key 120分步执行,可能在执行EXPIRE前进程崩溃,导致 key 永久存在(内存泄漏)。