面试题答案
一键面试缓存表存储引擎选择
- Redis:
- 优势:
- 高性能:基于内存存储,读写速度极快,能满足高并发查询需求。例如,简单的键值对查询操作可以在微秒级完成,非常适合处理大量的在线交易系统查询请求。
- 丰富的数据结构:支持字符串、哈希、列表、集合、有序集合等多种数据结构。对于交易系统中不同类型的数据,如用户交易记录(可使用哈希结构)、热门商品排序(可使用有序集合)等都能很好地存储和操作。
- 持久化机制:提供RDB(快照)和AOF(追加式日志)两种持久化方式,可在保证高性能的同时,确保数据在故障后恢复。
- 优势:
- Memcached:
- 优势:
- 简单高效:设计简单,专注于缓存,在纯内存缓存场景下性能卓越。它的内存管理机制高效,能够快速分配和回收内存空间,对于高并发的简单键值对查询有很好的支持。
- 分布式:支持分布式部署,可以通过增加节点来扩展缓存容量,以应对不断增长的查询负载。
- 优势:
索引优化
- 合理设计键:
- 业务相关性:键应与业务紧密相关且具有唯一性。例如在交易系统中,以订单号作为缓存键,可以快速定位到具体订单的相关信息,避免键冲突导致的数据覆盖。
- 简洁性:避免过长或复杂的键,减少存储和查询开销。比如,对于用户相关的缓存,可以采用“user:用户ID:数据类型”这样简洁且有逻辑性的键格式。
- 复合索引(对于支持复合索引的存储引擎,如Redis的哈希结构变相支持复合索引):
- 场景应用:如果经常需要根据多个条件进行查询,可使用复合索引。例如,在商品查询中,经常需要根据商品类别和价格范围查询热门商品,可以将商品类别和价格相关信息组合在一个哈希键内,通过哈希结构的特性实现快速定位。
锁机制处理
- 乐观锁:
- 原理:乐观锁假设在大部分情况下,数据的并发修改不会发生冲突。在读取数据时不锁定数据,在更新数据时,检查数据在读取后是否被其他进程修改。
- 应用场景:适用于读多写少的场景,如交易系统中的商品浏览信息缓存。例如,在缓存商品浏览量时,每次读取浏览量数据不进行锁定,在更新浏览量时,通过版本号或时间戳等机制检查数据是否被修改,若未被修改则更新成功。
- 悲观锁:
- 原理:悲观锁假设数据在并发操作时一定会发生冲突,在读取数据时就锁定数据,其他进程无法同时访问该数据。
- 应用场景:适用于写多读少且数据一致性要求极高的场景,如交易系统中的资金账户缓存。例如,在对用户账户余额进行操作时,先获取锁,确保只有一个操作能对余额进行修改,避免出现余额不一致的问题。
避免缓存雪崩和缓存穿透
- 避免缓存雪崩:
- 设置不同过期时间:
- 原理:将缓存数据的过期时间分散设置,避免大量缓存数据在同一时间过期,导致瞬间大量请求直接穿透到数据库。
- 实现方式:在设置缓存过期时间时,采用随机时间加上固定时间的方式。例如,固定时间为30分钟,随机时间在0到10分钟之间,这样每个缓存项的过期时间就会分布在30到40分钟之间,减少了同时过期的可能性。
- 使用二级缓存:
- 原理:一级缓存失效时,先从二级缓存获取数据,避免直接访问数据库。
- 实现方式:可以使用不同存储引擎作为二级缓存,如一级缓存使用Redis,二级缓存使用Memcached。当Redis中的缓存失效时,先从Memcached中查找数据,若Memcached中也没有,则再访问数据库,并将数据依次更新到二级和一级缓存中。
- 设置不同过期时间:
- 避免缓存穿透:
- 布隆过滤器:
- 原理:布隆过滤器是一种概率型数据结构,它可以高效地判断一个元素是否在一个集合中。在缓存中使用布隆过滤器,先判断查询的数据是否可能存在于缓存或数据库中,若不存在则直接返回,避免无效查询穿透到数据库。
- 实现方式:在系统初始化时,将数据库中已有的数据(如商品ID、用户ID等)批量添加到布隆过滤器中。当有查询请求时,先通过布隆过滤器判断数据是否存在,若不存在则直接返回,大大减少无效查询对数据库的压力。
- 空值缓存:
- 原理:对于查询后数据库中不存在的数据,也进行缓存,不过设置较短的过期时间,避免下次同样的无效查询穿透到数据库。
- 实现方式:当查询数据在数据库中不存在时,将该查询的键值对(值可为特殊标识表示不存在)缓存起来,设置较短的过期时间,如1分钟。下次再有相同查询时,直接从缓存中获取,知道该数据不存在,而不需要访问数据库。
- 布隆过滤器: