Redis面试题(二)实战

1、十万订单每秒热点数据架构如何优化?
  1. 使用缓存:使用Redis等缓存技术,将热点数据存储在内存中,可以大大提高数据的读取速度。
  2. 数据库优化:对数据库进行分库分表,通过分布式数据库中间件如ShardingSphere进行读写分离和数据分片,可以有效提高数据库的处理能力。
  3. 使用消息队列:使用RabbitMQ、Kafka等消息队列技术,将订单处理流程异步化,可以有效缓解系统压力。
  4. 服务拆分:将服务拆分为多个微服务,每个微服务负责处理特定的业务,可以提高系统的处理能力。
  5. 使用负载均衡:通过Nginx等负载均衡技术,将请求分发到多个服务器,可以提高系统的处理能力。
  6. 使用热点数据预热:通过预先加载热点数据,可以提高系统的响应速度。(在系统启动或者业务高峰前,将可能成为热点的数据加载到缓存中,可以通过定时任务实现。)
2、Redis集群崩溃时,如何保证秒杀系统高可用?
  1. 主从复制:Redis支持主从复制,即一个主节点和多个从节点。当主节点出现问题时,可以立即切换到从节点,保证服务的可用性。
  2. 哨兵模式:Redis的哨兵模式可以监控主节点的状态,当主节点出现问题时,哨兵会自动将其中一个从节点提升为主节点,保证服务的可用性。
  3. 持久化:Redis支持RDB和AOF两种持久化方式,可以将内存中的数据保存到磁盘,即使Redis崩溃,也可以通过持久化文件恢复数据。
  4. 限流和降级:在系统设计时,可以引入限流和降级机制,当Redis出现问题时,可以限制部分请求,或者将部分请求降级处理,保证系统可用性。

备份Redis:可以定期对Redis数据进行备份,当Redis崩溃,可以通过备份数据进行恢复。

3、Redis主从切换导致库存同步异常以及超卖问题

        Redis主从切换可能会导致数据不一致,从而引发库存内部异常和超卖问题。

  1. 使用Redis事务:Redis支持事务操作,可以将库减存和检查库存的操作放在一个事务中,保证这两个操作的原子性。
  2. 使用Lua脚本:Redis支持Lua脚本,可以将建库存和检查库存的操作写在一个Lua脚本中,由于Redis会单线程执行Lua脚本,因此可以保证这两个操作的原子性。
  3. 使用分布式锁:可以使用Redis的分布式锁,保证同一时间只有一个请求可以操作库存,避免超卖。
  4. 使用乐观锁:在更新库存时,可以使用乐观锁,即在更新库存前先检查库存是否有变化,如果有变化则放弃更新,否则进行更新。(乐观锁在进行数据操作前不会加锁,而是在进行数据更新时才会检查数据是否被其他事务修改过,如果被修改过则放弃本次操作;优点:1、减少锁竞争,如果使用悲观锁,可能导致大量的请求阻塞;2、避免死锁:乐观锁在数据操作时不会加锁;3、提高系统响应速度:由于乐观锁在操作时不会加锁,因此可以提高响应速度)
  5. 数据库双写一致性:在更新库存时,同时更新数据库和Redis,当两者数据不一致时,可以通过数据库的数据进行修复。
4、秒杀链路中Redis与MQ如何保证事务一致性?

        在秒杀链路中,Redis用于缓存商品,MQ用于异步处理秒杀订单,为了保证事务一致性,可以采取以下策略:

  1. 使用分布式事务:例如可以使用XA协议的分布式事务,或者使用基于两阶段提交的分布式事务,保证Redis和MQ的操作在一个事务中。
  2. 使用最终一致性:首先更新Redis中的库存,然后发送MQ消息。如果发送MQ消息失败,可以通过补偿机制重新发送。同时,消费者在消费MQ消息处理订单时,如果发现库存不足,可以发送一个补偿消息回滚Redis中的库存。
  3. 使用TCC模式,在Try阶段预减Redis中的库存,然后发送MQ消息,在Confirm阶段确认减库存,如果在任何阶段失败,都可以在Cance阶段回滚操作。
  4. 使用基于本地消息表的可靠消息服务:在数据库中创建一个本地消息表,用于存储待发送的MQ消息。在更新库存的同时,将待发的消息写入本地消息表,然后通过一个定时任务或者后台线程,将本地消息表中的消息发送到MQ。
5、线上MQ百万秒杀订单积压如何优化?
  1. 增加消费者数量:增加消费者的数量可以提高消息的消费速度,减少消息积压。
  2. 优化消费者处理速度:优化消费者处理消息的逻辑,提高速度。
  3. 消息分区:如果MQ支持分区,可以将消息分布到多个分区,然后使用多个消费者并行消费不同的分区,提高消费速度。
  4. 使用优先级队列:如果MQ支持优先级,可以将重要的消息放入高优先级的队列,保证重要消息能先处理。
  5. 消息过滤:如果MQ支持消息过滤,可以在消费者端设置过滤条件,只消费符合条件的消息,减少无效的消息处理。
  6. 消息压缩:如果消息体较大,可以考虑使用压缩技术,减少网络传输和存储的开销。
6、如何用Redis高效实现12306的复杂售票业务?

        12306的售票业务设计到车次查询、余票查询、订单创建、支付等多个环节。

  1. 车次和余票信息缓存:将车次信息和余票信息缓存到Redis中,用户查询时,先从Redis中获取数据。
  2. 使用Redis事务:在创建订单和减少余票的操作中,可以使用Redis的事务功能,保证者两个操作的原子性。
  3. 使用Redis的发布订阅功能:当余票信息发生变化时,可以使用Redis的发布订阅功能,通知相关的服务进行处理。
  4. 使用Redis的过期事件:在用户创建订单后,可以设置一个过期时间,如果用户在规定时间内没有完成支付,Redis的过期事件会自动触发,可以用来自动取消订单并恢复余票。
  5. 使用Redis的分布式锁:在处理并发请求时,例如多个用户同时抢一张票,可以使用Redis的分布式锁,保证同一时间只有一个请求可以操作余票。
7、高并发场景缓存穿透&失效&雪崩如何解决?
  1. 缓存穿透:缓存穿透是指一个不存在的数据,由于缓存中没有,每次都要去数据库查询,导致数据库压力增大,解决的方法有:
    1. 缓存空对象:即使数据库中没有,也将空接结果进行缓存,但需要设置较短的过期时间。
    2. 使用布隆过滤器:将所有可能存在的数据哈西放到一个足够大的布隆过滤器中,一个元素是否存在可以通过布隆过滤器来判断,避免了对数据库的无效查询。
  2. 缓存失效:如果缓存频繁失效,会导致大量的数据直接请求访问数据库,解决方案有:
    1. 设置合理的过期策略:如LRU算法、LFU算法等。
    2. 缓存预热:在业务高峰之前,预先将可能会被访问的热点数据加载到缓存中。
    3. 缓存刷新:在数据过期前,异步地对缓存数据进行刷新,可以避免缓存失效。
    4. 双层缓存:可以设置两层缓存,一层是热点缓存,一层是冷数据缓存,热点缓存失效后,可以从冷数据缓存中获取数据,避免直接访问数据库。
  3. 缓存雪崩:缓存雪崩指在某一个时间段内,缓存中的大量数据同时失效,导致大量的请求直接访问数据库,解决方案有:
    1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
    2. 对热点数据设置永不过期。
    3. 使用分布式锁,或者队列,控制缓存的读写,保证缓存的单线程写,避免并发重建缓存。
8、Redis集群架构如何抗住12306与双11的洪峰流量?

        对于12306和双11这样的高并发场景,Redis集群可以采用以下策略来抗住洪峰流量:

  1. 水平扩展:通过增加 Redis节点的数据,分散请求,提高系统处理能力。
  2. 主从复制:每个主节点配置多个从节点,读请求主要由从节点处理,分担主节点压力。
  3. 数据分片:Redis集群支持数据分片,将数据分布在多个节点上,分散单个节点的压力。
  4. 使用哨兵模式:保证系统可用性。
  5. 缓存策略优化:合理设置缓存过期时间。
  6. 限流和降级:保证系统稳定性。
9、Redis缓存与数据库双写不一致如何解决?
  1. 读写分离:读请求只查缓存,写请求先更新数据库,然后删除缓存数据。下次读请求时,如果缓存不存在,再从数据库中加载数据到缓存。这样可以保证缓存和数据库的数据一致。
  2. 使用队列:更新操作不直接操作数据库和缓存,而是先写入到一个队列中,然后由一个后台线程或者进程从队列中取出操作并执行,保证更新数据库和缓存的操作是串行的。
  3. 使用分布式事务:如果数据库支持分布式事务,可以将更新数据库和缓存的操作放在一个分布式事务中。
  4. 最终一致性:接收缓存和数据库的数据短时间内不一致,但需保证最终会达到一致。例如,可以使用定时任务,定期检查数据库和缓存的数据,如果发现不一致,进行修复。
10、双十一亿级用户日活统计如何用Redis快速计算?

        对于双十一这样的亿级用户日活统计,可以使用Redis的HyperLogLog数据结构来进行快速计算。HyperLogLog是一种用于统计唯一值的数量算法,它可以使用固定且很小的内存空间来统计唯一值的数量,虽然结果可能会有一定的误差,但在大数据量的场景下,这个误差通常可以接受。

  1. 当用户访问网站时,将用户的唯一标识添加到HyperLogLog中:PFADD user:20211111 user_id
  2. 在需要统计日活用户数时,使用PFCOUNT命令获取HyperLogLog中的唯一值数量,即日活用户数:PFCOUNT user:20211111
  3. 如果需要统计多日的日活用户数,可以使用PFMERGE命令将多个HyperLogLog合并成一个,然后使用PFCOUNT命令唯一值数量。
11、双十一电商推荐系统如何用Redis实现?
  1. 使用Sorted Set存储用户的购买行为:可以将用户的购买行为(如购买的商品ID)作为成员,购买的次数作为分数,存储到Sorted Set中。在推荐时,可以根据分数(即购买次数)的高低,推荐用户可能感兴趣的商品:ZADD user:1:items 1 item_id; ZRANGE user:1:items 0 -1 WITHSCORES。
  2. 使用Hash存储商品的信息:可以将商品的信息(如商品的名称、价格、描述等)存储到Hash中。在推荐时,可以根据商品ID从Hash中获取商品的信息:HMSET item:1 name "item_name" price "item_price"; HGETALL item:1。
  3. 使用Set存储用户的浏览历史:可以将用户浏览过的商品ID存储到Set中。在推荐时,可以从Set中随机去除一些商品ID,作为推荐商品:SADD user:1:viewed item_id ;SRANDMEMBER user:1:viewed。
  4. 使用发布订阅功能实现实时推荐:当用户的购买行为发生变化时,可以发布一个消息,订阅者(如推荐系统)接受到消息后,可以实时更新推荐的商品:PUBLISH user:1 "buy item_id"
12、日均百亿级微信红包系统如何架构

        对于日军百亿级的微信红包系统,需要考虑的关键因素包括高并发、高可用、数据一致性等。

  1. 分布式架构:由于单台服务器无法承受如此大的并发量,因此需要采用分布式架构,将请求分发到多台服务器上。
  2. 使用Redis做缓存:将用户的红包信息缓存在Redis中,当用户抢红包时,先从Redis中扣减,然后异步地更新数据库,这样可以大大减少数据库的压力。
  3. 使用MQ做异步处理:当用户抢到红包后,将抢红包的信息发送到MQ,然后由服务消费MQ的消息,更新数据库和用户的账户余额。
  4. 使用分布式锁防止超发:在用户抢红包时,可以使用Redis的分布式锁,保证同一时间只有一个请求可以操作红包,避免红包的超发。
  5. 使用分布式事务保证数据一致性:在更新用户的账户余额和红包信息时,需要保证两个操作的原子性,可以使用分布式事务来实现。
  6. 使用分库分表解决数据瓶颈:由于红包的数据量非常大,单个数据库可能无法承受,可以采用分库分表的策略,将数据分布在多个数据库和表中。
  7. 使用高可用架构保证服务可用性:可以使用主从复制和负载均衡等技术,保证当某个节点出现问题时,服务仍可以正常提供。
13、类似微信的社交App朋友圈关注模型如何设计实现
  1. 使用Redis的Set存储用户的关注关系:每个用户都有一个Set,存储他关注的人的ID。当用户A关注用户B时,将B的ID添加到A的Set中:SADD user:1:following user_id
  2. 使用Redis的List存储用户的朋友圈动态:每个用户都有一个List,存储他朋友圈动态。当用户发布一条动态时,将动态的ID添加到他的List中,并将动态的ID添加到他所有粉丝的List中:LPUSH user:1:timeline post_id
  3. 使用Redis的Hash存储动态内容:每条动态都有一个Hash,存储动态的内容,如文字、图片等:HMSET post:1 content "post_content"
  4. 使用Redis的Sorted Set存储用户的朋友圈动态的排序:每个用户都有一个Sorted Set,存储他的朋友圈动态的排序。分数可以是动态的发布时间,成员是动态的ID。在获取朋友圈动态时,可以根据分数(即发布时间)的倒序获取:
    1. ZADD user:1:timeline timestamp post_id
    2. ZREVRANGE user:1:timeline 0 -1
14、美团单车如何基于Redis快速找到附近的车。

        美团单车可以使用Redis的地理空间索引功能来快速找到附近的车。

  1. 使用Redis的GEO数据结构存储车辆位置:每辆车都有一个经纬度坐标,可以使用GEOADD命令将车辆的位置信息添加到Redis中:
    1. GEOADD bikes location_longitude location_latitude bike_id
  2. 使用GEORADIUS命令查询附近的车:当用户需要找车时,使用GEORADIUS命令,根据用户的位置和指定的所有半径,查询附近的车。
    1. GEORADIUS bikes user_longitude user_latitude radius km WITHDIST WITHCOORD

        以上命令中,bikes是存储车辆位置信息的GEO数据结构,location_longitude和location_latitude是车辆的经纬度坐标,bike_id是车辆的ID,user_longitude和user_latitude是用户的经纬度坐标,radius是搜索半径。

        以上操作都可以在Redis的命令行客户的中执行,也可以通过Redis的客户端库在应用程序中执行。

15、Redis分布式锁主从架构锁失效问题如何解决?

        在Redis的主从架构中,使用分布式锁可能会出现锁失效的问题。主要原因是Redis的主从复制是异步的,当主节点上获取到锁后,如果锁的信息还没有复制到从节点,主节点就宕机了,那么从节点提升为主节点后,这把锁就不存在了。

  1. Redlock算法:Redlock算法是Redis官方推荐的分布式锁解决方案。它需要至少5个Redis节点,获取锁时需要同时向大多数节点写入锁信息,只有当大多数节点都写入成功,才算获取到锁。这样可以避免单个节点宕机导致的锁失效问题。
  2. 使用Redis的Sentinel模式或Cluster模式:在Sentinel模式或Cluster模式中,Redis提供了故障转移的功能,当主节点宕机时,可以自动选举出新的主节点,保证服务可用。
  3. 设置合理的锁超时时间:在获取锁时,可以设置一个合理的超时时间,防止因为节点宕机导致的锁无法释放。但需要注意,锁的超时时间不宜设置的过短,以免任务还没执行完,锁就过期了。
16、 超大并发的分布式锁架构该如何设计?
  1. 使用Redis实现分布式锁:Redis的SETNX命令可以用来实现分布式锁,它可以保证在多个客户端之间的原子性。同时,可以使用EXPIPE命令为锁设置一个过期时间,防止因为客户端崩溃导致的锁无法释放。
  2. 使用Zookeeper实现分布式锁:Zookeeper的临时顺序节点可以用来实现分布式锁,它可以保证在多个客户端之间的原子性和顺序性。同时,Zookeeper的watcher机制可以用来实现锁的自动释放和等待通知。
  3. 使用Redlock算法:Redlock算法是官方推荐的分布式锁解决方案。
  4. 使用数据库实现分布式锁:可以使用数据库的行锁或表锁来实现分布式锁,但由于数据库的并发能力有限,这种方式通常只适用于并发量较小的场景。
  5. 使用分布式协调服务Etcd或Consul实现分布式锁:Etcd和Consul都提供了分布式锁的功能,它们可以保证在多个客户端之间的原子性和顺序性。
17、Redis底层ZSet跳表时如何设计与实现的?

        Redis的有序集合(ZSet)是通过跳表(SkipList)和哈希表(HashTable)两种数据结构共同实现的。

        跳表是一种可以进行快速查找的数据结构,它通过维护多级索引来提高查询效率,查找的时间复杂度可以达到O(logN)。跳表中的每个节点包含了两个部分:成员(member)和分值(score),其中分值用于排序,成员用于存储具体的值。

        在Redis的ZSet中,跳表主要用于维护元素的顺序,即通过分值进行排序。当我们需要获取分值最高(或最低)的元素,或者需要获取某个分值范围内的所有元素时,可以通过跳表快速完成。

        哈希表则用于支持快速的元素查找,即通过成员查找元素。当我们需要判断一个元素是否存在,或者需要获取或更新一个元素的分值时,可以通过哈希表快速完成。

        这种设计使得Redis的ZSet可以在快速满足查找的同时,还能支持有序的元素存储和访问,非常适合用于实现排行榜等功能。

        具体的实现细节,包括跳表的节点结构,插入和删除算法等,可以参考Redis的源代码,其中有详细的注释和说明。

18、Redis底层ZSet实现压缩列表和跳表如何选择?

        Redis有序集合(ZSet)在底层可以使用两种数据结构来存储数据:压缩列表(ziplist)和跳表(skiplist)。这两种数据结构的选择取决于两个配置的参考:zset-max-ziplist-entries和zset-max-ziplist-value。

  1. zset-max-ziplist-entries:这个参数用来设置压缩列表可以包含的元素的最大数量。如果一个ZSet的元素数量超过这个值,那么就会使用跳表来存储数据。
  2. zset-max-ziplist-value:这个参数用来设置压缩列表中元素的最大大小。如果一个ZSet中任何元素的大小超过这个值,那么就会使用跳表来存储数据。

        压缩列表和跳表各有优缺点。压缩列表在存储小型ZSet(即元素数量和大小都比较小)时,可以节省内存空间,而且由于数据存储在连续的内存区域,所以访问速度也较快。但是,压缩列表在处理大型ZSet时,插入和删除操作的效率较低。

        跳表则在处理大型ZSet时,具有较好的性能,因为它的插入和删除操作的时间复杂度为O(logN)。但是,跳表占用的内存空间比压缩列表要多。

        因此,Redis通过这两个参数,动态地选择最合适的数据结构,以在内存使用和性能之间取得平衡。

19、Redis6.0多线程模型比单线程优化在哪里了?

        在Redis6.0中,引入了多线程模型,主要用于处理网络I/O,以提高Redis的性能。具体来说,这个多线程模型主要优化了以下几个方面:

  1. 并发网络I/O:在之前的版本中,Redis使用单线程来处理所有的网络I/O,包括读取客户端的请求和向客户端发送响应。这意味着,如果有大量的网络I/O需要处理,Redis可能会成为瓶颈。在Redis6.0中,引入了多线程模型,可以使用多个线程并发地处理网络I/O,从而提高处理速度
  2. 减少主线程的负担:在多线程模型中,主线程主负责处理命令的执行,而网络I/O的处理则由其他线程完成。这样,主线程可以专注于命令的执行,从而提高执行效率。
  3. 提高资源利用率:在多核CPU的环境中,多线程模型可以更好地利用CPU资源。每个线程可以在一个单独的CPU核心上运行,从而提高整体的处理能力。

        需要注意的是,虽然Redis6.0引入了多线程模型,但是Redis的命令执行仍然是单线程的。着是因为Redis的设计目标是简单和高效,而多线程的命令执行可能会引入复杂的同步问题,从而影响Redis的性能和稳定性。因此,Redis6.0的多线程模型主要用于优化网络I/O,而不是命令执行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值