目录

Redis

对Redis 的了解?

  • 基于内存也可持久化的数据库,采用单线程,避免了不必要的上下文切换。

Redis 一般都有哪些使用场景?

Redis 适合的场景

  • 缓存:减轻 MySQL 的查询压力,提升系统性能;
  • 排行榜:利用 Redis 的 SortSet(有序集合)实现;
  • Session 共享:Session 是保存在服务器的文件中,如果是集群服务,同一个用户过来可能落在不同机器上, 这就会导致用户频繁登陆;采用 Redis 保存 Session 后,无论用户落在那台机器上都能够获取到对应的 Session 信息。
  • 消息队列:除了 Redis 自身的发布/订阅模式,我们也可以利用 List 来实现一个队列机制, 比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的 DB 压力,完全可以用 List 来完成异步解耦;

Redis 不适合的场景

  • 数据量太大
  • 数据访问频率非常低的业务

Redis的数据类型?

  1. string(字符串)
  2. list(列表)
  3. set(集合)
  4. hash(哈希)
  5. sorted set(有序集合)

讲讲Redis主从复制?

  • 当启动一个 slave(死里无) node 的时候,它会发送一个 PSYNC 命令给 master node。 如果这是 slave node 初次连接到 master node,那么会触发一次全量复制。此时 master 会启动一个后台线程,开始生成一份 RDB 快照文件, 同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB 文件生成完毕后, master 会将这个 RDB 发送给 slave, slave 会先写入本地磁盘,然后再从本地磁盘加载到内存中, 接着 master 会将内存中缓存的写命令发送到 slave,slave 也会同步这些数据。 slave node 如果跟 master node 有网络故障,断开了连接,会自动重连,连接之后 master node 仅会复制给 slave 部分缺少的数据。

由于主从延迟导致读取到过期数据怎么处理?

  1. 通过scan命令扫库:当Redis中的key被scan的时候,相当于访问了该key, 同样也会做过期检测,充分发挥Redis惰性删除的策略。 这个方法能大大降低了脏数据读取的概率,但缺点也比较明显,会造成一定的数据库压力,否则影响线上业务的效率。

  2. Redis加入了一个新特性来解决主从不一致导致读取到过期数据问题,增加了key是否过期以及对主从库的判断, 如果key已过期,当前访问的master则返回null;当前访问的是从库,且执行的是只读命令也返回null。

Redis主从架构数据会丢失吗,为什么?

有两种数据丢失的情况:

  1. 异步复制导致的数据丢失:因为master -> slave的复制是异步的, 所以可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了。
  2. 脑裂导致的数据丢失: 某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着,此时哨兵可能就会认为master宕机了, 然后开启选举,将其他slave切换成了master。这个时候,集群里就会有两个master,也就是所谓的脑裂。

讲讲Redis哨兵模式?

  • 哨兵主要是用于实现 redis 集群的高可用,哨兵是 redis 集群机构中非常重要的一个组件,他有
  1. 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
  2. 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  3. 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
  4. 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

讲讲Redis持久化?

  • 持久化有RDB和AOF两种机制
  • RDB:是Redis的默认持久化,按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为 dump.rdb。 通过配置文件中的 save 参数来定义快照的周期。
  • AOF:是将 Redis 执行的每次写命令记录到单独的日志文件中,当重启 Redis 会重新将持久化的日志中文件恢复数据。
  1. AOF 文件比 RDB 更新频率高,优先使用 AOF 还原数据。
  2. AOF 比 RDB 更安全也更大
  3. RDB 性能比 AOF 好
  4. 如果两个都配了优先加载 AOF

怎么保证缓存和数据库数据的一致性?

  1. 设置了合理的键的过期时间
  2. 新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。

Redis是如何使用的?或者说Redis是如何实现分布式锁的?

  • 就是我们要用这个分布式锁的话,就是我们要满足几个条件,首先我们要保证他们要有一个互刺性, 在任意时刻呢,只能有一个客户端,他是只能持有这个锁,然后我们也要避免说是不能发生死锁, 就是即使我们只有一个客户端,有他在持有锁的期间内啊,然后崩溃了然后没有主动解锁的话, 也要保证后续的这些客户端呢,他们也是能够持续可以去加锁的,然后我们还要保证的是说他还有一个容错性, 就是我们这个绝大部分的一个Redis,要保证他这个节点正常运行嘛,然后我们客户端就可以加锁跟解锁,那我们加锁和解锁呢, 必须是一个同一个客服端,这样的话客服端自己呢,他是不能把别人的锁给解了啊,差不多就是这样。

常见的分布式锁有哪些解决方案?

基于关系型数据库,如MySQL

基于关系型数据库实现分布式锁,是依赖数据库的唯一性来实现资源锁定,比如主键和唯一索引等。

缺点:

  • 数据库挂掉,会导致业务系统不可用
  • 没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。
  • 只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错。 没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作。
  • 同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。

基于Redis实现

优点:

  • Redis 锁实现简单,理解逻辑简单,性能好,可以支撑高并发的获取、释放锁操作。

缺点:

  • Redis 容易单点故障,集群部署,并不是强一致性的,锁的不够健壮;
  • key 的过期时间设置多少不明确,只能根据实际情况调整;
  • 需要自己不断去尝试获取锁,比较消耗性能。

基于zookeeper

优点:

  • zookeeper 天生设计定位就是分布式协调,强一致性,锁很健壮。如果获取不到锁, 只需要添加一个监听器就可以了,不用一直轮询,性能消耗较小。

缺点:

  • 在高请求高并发下,系统疯狂的加锁释放锁,最后 zookeeper 承受不住这么大的压力可能会存在宕机的风险。

Redis为什么这么的快?

  • 因为绝大部分Redis请求,都是我们的内存操作,所以他会非常的快,然后因为我们Redis, 他是基于一个内存的一个数据库,那可以跟我们这种磁盘数据库,可以去做一个对比, 比如说我们的MySQL数据库,想我们的MySQL数据库呢,他就是一个关系型数据库,他主要是用于存放我们一个持久化的一个数据, 然后把我们的数据呢,存放在这个硬盘中,然后读取速度就会比较慢,因为每次请求这种,MySQL数据库的时候, 他其实是存在一个I/O操作,如果我们反复啊,频繁啊,去访问这种数据库的话,会在数据库过程中会花费一个时间嘛, 而且我们反复的访问这种数据库的话,会导致我们这个,数据库的一个负载过高,那我们的Redis是基于内存的嘛, 所以他这种内存型的这种缓存数据库,就是用于存储我们这种使用频繁的数据啊,这样可以减少我们去访问,这种数据库的一种次数, 提升我们的一个运行效率,然后因为他是存在采用这个单线程的嘛,然后就避免了不必要的上下文切换,还有我们的一些竞争条件, 还有个特点,Redis这边是非阻塞的IO,就是我们的IO的一个多路互用,就这些。

什么是缓存穿透?怎么解决?

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存, 这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。

解决办法:

  • 缓存空对象:如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存, 但它的过期时间会很短,最长不超过五分钟。
  • 布隆过滤器:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉, 从而避免了对底层存储系统的查询压力。

缓存空对象带来的问题:

  • 空值做了缓存,意味着缓存中存了更多的键,需要更多的内存空间
  • 缓存和存储的数据会有一段时间窗口的不一致,可能会对业务有一定影响

什么是缓存雪崩?该如何解决?

如果缓存集中在一段时间内失效,所有的查询都落在数据库上,造成了缓存雪崩。

解决办法:

  • 加锁排队:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。 比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待;
  • 数据预热:可以通过缓存 reload 机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的 key, 设置不同的过期时间,让缓存失效的时间点尽量均匀;
  • 做二级缓存:Cache1 为原始缓存,Cache2 为拷贝缓存,Cache1 失效时,可以访问 Cache2, Cache1 缓存失效时间设置为短期,Cache2 设置为长期。
  • 在缓存的时候给过期时间加上一个随机值,这样就会大幅度的减少缓存在同一时间过期。

Redis的过期键的删除策略

Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。 Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。

Redis中同时使用了惰性过期和定期过期两种过期策略。

过期策略通常有以下三种:

  • 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。
  • 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。
  • 定期清楚:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。

为什么要用 Redis 而不用 map/guava 做缓存?

  • 缓存分为本地缓存和分布式缓存。以java为例,使用自带的map或者guava实现的是本地缓存, 最主要的特点是轻量以及快速,生命周期随着jvm的销毁而结束,并且在多实例的情况下, 每个实例都需要各自保存一份缓存,缓存不具有一致性。
  • 使用Redis或memcached之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。 缺点是需要保持Redis或memcached服务的高可用,整个程序架构上较为复杂。

对比:

  • Redis 可以用几十 G 内存来做缓存,Map 不行,一般 JVM 也就分几个 G 数据就够大了;
  • Redis 的缓存可以持久化,Map 是内存对象,程序一重启数据就没了;
  • Redis 可以实现分布式的缓存,Map 只能存在创建它的程序里;
  • Redis 可以处理每秒百万级的并发,是专业的缓存服务,Map 只是一个普通的对象;
  • Redis 缓存有过期机制,Map 本身无此功能;Redis 有丰富的 API,Map 就简单太多了;
  • Redis可单独部署,多个项目之间可以共享,本地内存无法共享;
  • Redis有专门的管理工具可以查看缓存数据。

Redis 常见性能问题和解决方案?

  • Master 最好不要做任何持久化工作,
  • 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内;
  • 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…