面试题答案
一键面试设计结合方案
- 确定适合缓存的数据
- 高频读取数据:通过对业务系统的日志分析或性能监控,找出那些被频繁读取的HBase表数据行或列族。例如,电商系统中热门商品的基本信息(如名称、价格、图片链接等),这些数据会被大量用户频繁查看。
- 相对静态数据:数据变化频率较低的部分适合缓存。如一些配置信息、字典数据等。像游戏中的道具类型、属性等数据,不会经常变动。
- 聚合数据:经过复杂计算得到的聚合结果,如按天统计的网站访问量、订单总数等。这类数据计算成本高,缓存后可以避免重复计算。
- 缓存更新策略
- 读写时更新:
- 写后更新:在HBase数据写入成功后,立即同步更新Redis缓存。这种方式能保证数据一致性,但如果写入HBase频繁,可能会导致Redis更新压力较大。例如在订单创建时,写入HBase订单表成功后,更新Redis中与订单相关的缓存(如用户订单总数等)。
- 写前更新:先更新Redis缓存,再写入HBase。这样可以减少写入HBase成功但更新Redis失败带来的数据不一致问题,但可能存在写入HBase失败时Redis已更新的情况。比如在商品信息修改时,先更新Redis中的商品缓存,再更新HBase中的商品表。
- 定时更新:根据数据的重要性和变化频率设定不同的更新周期。对于变化不频繁但重要的数据,如商品的库存信息,设置较短的更新周期(如每5分钟);对于相对更静态的数据,如商品分类信息,设置较长的更新周期(如每天一次)。利用定时任务定期从HBase读取最新数据并更新到Redis。
- 读写时更新:
- 数据一致性维护
- 双写一致性:采用事务机制,将HBase和Redis的写入操作封装在一个事务中,确保要么都成功,要么都失败。例如使用分布式事务框架(如Seata)来保证数据一致性。在业务逻辑层,先获取事务上下文,然后依次执行HBase和Redis的写操作,最后提交事务。
- 缓存失效机制:当HBase数据发生变化时,使对应的Redis缓存失效。可以通过监听HBase的RegionServer的日志或利用HBase的协处理器,在数据更新时发送消息通知Redis删除相应的缓存。例如在电商库存更新时,通过HBase协处理器发送消息给Redis,让其删除库存相关的缓存。
可能遇到的挑战及应对措施
- 缓存穿透
- 挑战:查询不存在的数据时,每次都会穿透到HBase,增加HBase负载。比如恶意请求大量不存在的商品ID。
- 应对措施:使用布隆过滤器(Bloom Filter)。在Redis之前增加布隆过滤器,当查询数据时,先通过布隆过滤器判断数据是否存在。如果布隆过滤器判断不存在,则直接返回,不再查询HBase。同时,将查询过不存在的数据缓存到Redis中,设置较短的过期时间,避免后续重复查询穿透到HBase。
- 缓存雪崩
- 挑战:大量缓存同时过期,导致请求瞬间都落到HBase上,可能使HBase服务瘫痪。例如缓存设置了相同的过期时间,在过期时刻大量请求同时到来。
- 应对措施:分散缓存过期时间,在设置缓存过期时间时,增加一个随机值。比如原本设置过期时间为1小时,可以改为1小时加上0 - 10分钟的随机值。同时,采用多级缓存架构,如在应用层和Redis之间增加本地缓存(如Guava Cache),在Redis缓存失效时,先从本地缓存获取数据,减少对HBase的直接请求。
- 数据一致性问题在高并发下的复杂性
- 挑战:在高并发场景下,即使采用双写一致性和缓存失效机制,仍可能出现数据不一致的情况。例如,在写操作时,HBase写入成功但Redis更新失败,同时其他读操作可能读取到旧的缓存数据。
- 应对措施:引入版本号机制。在HBase表中增加版本号字段,每次数据更新时版本号递增。在读取数据时,先从Redis获取数据及对应的版本号,然后与HBase中的版本号比较。如果Redis中的版本号低于HBase,则从HBase读取最新数据并更新Redis。同时,对重要数据的写操作进行排队处理,减少并发带来的一致性问题。