Skip to content

问:Redis支持哪些数据类型?分别适用于什么场景?

五种基础数据类型:

  1. 字符串(String):例如JSON字符串缓存,例如锁。
  2. 列表(List):例如消息。
  3. 哈希(Hash):相比将整个对象JSON序列化成字符串,哈希对字段分开存储更高效。
  4. 集合(Set):去重效果,交集/并集,例如共同好友功能
  5. 有序集合(Sorted Set):排序,例如排行榜

问:为什么Redis的读写性能比MySQL高?

  1. 内存 vs 磁盘:内存的访问速度碾压磁盘。
  2. 单线程 vs 多线程:单线程避免锁竞争,I/O多路复用支撑高并发(网络I/O和命令处理解耦)。
  3. 数据结构优化:内存友好的数据结构 vs 磁盘优化的B+树。
  4. 功能取舍:Redis牺牲复杂功能(事务、复杂查询)换取极简高效。

问:如何保证缓存与数据库的数据一致性?是否使用过延迟双删?

  1. Cache Aside(旁路缓存)机制:最常用的缓存模式,读的时候先缓存,没有才查库,写的时候先写库,然后删缓存。优点是简单有效,缺点是并发场景下可能因延迟导致短暂不一致(比如A更新数据库后删缓存,B在A删缓存前读到旧数据并回填)。
  2. 延迟双删:为了解决数据库有主从架构的情况下,主从延迟或并发导致的脏数据问题。主要操作是先删一次缓存,然后写库,然后延迟一段时间(比如主从同步耗时+业务处理时间),再次删除缓存。
  3. 分布式锁:在更新数据库和缓存时加锁,确保同一时间只有一个线程操作数据。
  4. 版本号控制:在缓存中存储数据版本号(如时间戳),更新数据库时版本号递增,读操作校验版本号是否匹配。

问:如何用Redis实现分布式锁?需要注意哪些问题(如锁续期、原子性)?

我们使用Redisson客户端实现分布式锁。

  1. 加锁:不存在才加锁。设置一个代表当前线程的唯一ID(防误删)。设置过期时间。
  2. 删除锁:需要用Lua脚本执行“判断值+删除”两步操作,确保原子性。
  3. 锁续期:比如设置锁的过期时间为10秒,但业务可能需要15秒才能完成。这时候需要启动一个后台线程,每隔一段时间(比如3秒)检查锁是否还在持有,如果还在,就延长过期时间(比如重置为10秒)。Redisson库的分布式锁就内置了这个机制。

问:Redis分布式锁有哪些坑?如何解决锁续期、脑裂等问题?

  1. 锁续期问题:业务没执行完,锁过期了。(使用锁续期)
  2. 脑裂问题(主从切换导致锁丢失):RedLock算法向多个独立Redis实例申请锁,超过半数成功才算获取锁。即使某个实例崩溃,其他实例仍能保证锁的唯一性。
  3. 锁误删:设置一个代表当前线程的唯一ID。
  4. 同一线程多次加锁失败:Redisson的可重入锁。

问:持久化的对比与选择?

RDB(Redis Database)

核心机制:

  • 定时快照:RDB通过生成某个时间点的数据快照(Snapshot)来保存数据。比如每隔1小时,或者当满足“N秒内修改了M个键”的条件时触发快照。
  • 默认方式:是Redis的默认持久化方式,生成的RDB文件是二进制的,文件名类似dump.rdb。

优点:

  • 性能高:生成快照时通过子进程处理,对主线程影响小。
  • 恢复快:RDB文件紧凑,重启时加载速度远快于AOF。
  • 适合备份:文件小,方便传输到远程服务器或归档。

缺点:

  • 数据丢失风险:如果Redis宕机,最后一次快照后的数据会丢失。比如每1小时保存一次,宕机可能丢1小时的数据。
  • 大数据量时耗时:生成快照时如果数据量很大,可能会导致主线程短暂阻塞(尤其在虚拟机环境中)。

AOF(Append-Only File)

核心机制:

  • 日志追加:AOF记录每一个写操作命令(如SET、DEL),以文本形式追加到文件末尾。
  • 重写机制:为了避免日志文件过大,Redis会定期重写AOF文件(比如BGREWRITEAOF),删除冗余命令,合并为最小指令集。

优点:

  • 数据更安全:根据同步策略(appendfsync),最多丢失1秒数据(默认配置为everysec)。
  • 可读性强:AOF文件是文本格式,方便人工查看或修复。

缺点:

  • 文件体积大:AOF文件通常比RDB大,尤其在未重写时。
  • 恢复慢:重启时需要逐条执行AOF中的命令,耗时长。
  • 写入性能略低:如果配置为always(每次写都同步到磁盘),性能会明显下降。

两种持久化如何选择

回答:

  1. 纯缓存场景:如果Redis仅用作缓存,允许数据丢失,可以关闭持久化,或者只用RDB。
  2. 数据持久化要求高:比如订单、支付系统,必须用AOF,并配置appendfsync everysec(平衡性能与安全)。
  3. 混合使用:同时开启RDB和AOF。这样既保留AOF的数据安全性,又能用RDB做冷备或快速恢复。

总结:

  • RDB:快照式备份,速度快、文件小,但可能丢数据。
  • AOF:日志式记录,数据安全、文件大,但恢复慢。
  • 最佳实践:两者结合,用AOF保数据,用RDB做备份。

问:内存淘汰策略:

Redis的内存淘汰策略主要有以下几种:

  1. noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
  2. allkeys-lru:从所有键中挑选最近最少使用的键进行淘汰。
  3. allkeys-random:从所有键中随机淘汰键。
  4. volatile-lru:从设置了过期时间的键中挑选最近最少使用的键进行淘汰。
  5. volatile-random:从设置了过期时间的键中随机淘汰键。
  6. volatile-ttl:从设置了过期时间的键中挑选即将过期的键进行淘汰。
  • 常用的有:有过期时间的最少使用的淘汰、当内存不足不允许写入、即将过期的淘汰、随机淘汰。
  • 实际场景:核心数据(如订单锁)用noeviction单独部署,缓存数据用volatile-lru。

问:Lua脚本

Lua脚本主要是解决原子性问题。比如有个需求是库存足够才扣减,需要先检查库存,再扣减。如果用普通命令,可能存在竞态条件。用lua脚本可以保证库存不会被扣减为负数。

重点注意:

  1. 避免长脚本,用SCRIPT KILL设置超时;
  2. 禁用全局变量,防止内存泄漏;
  3. 优先使用Redis命令而不是Lua函数,比如用redis.call('GET', key)而不是Lua的循环遍历。

问:Redis的批处理(Pipeline)

客户端将多个命令一次性发送到服务器,而不需要等待每个命令的响应。

重点注意:

  1. 不要在一个Pipeline里塞太多命令,避免单次请求过大;
  2. Pipeline不保证事务性,中间命令失败不会回滚;
  3. 需要根据业务场景评估是否真能提升性能,比如低延迟环境下收益可能不明显。

页脚:版权前显示的信息