Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!

redis

  • redis
    • reids的常用数据类型
      • 1.String
      • 2.Hash
      • 3.List
      • 5.Sorted Set(ZSet)
      • 6.其他
    • 发布(pub)订阅模式(sub)模式
      • 作用
    • Redis的持久化机制 - RDB
      • 1. 什么是RDB
      • 2. 备份与恢复
      • 3. RDB优劣势
      • 4. RDB的配置
      • 5. RDB总结
    • Redis的持久化机制 - AOF
      • 1. 引子
      • 2. AOF特点
      • 3. AOF优势与劣势
      • 4. AOF的配置
      • 5. 思考:到底采用RDB还是AOF呢?
    • redis的主从(读写分离)架构
      • 1. 概念图
      • 2. 主从原理
      • 3. 相关配置解析
      • 4. 总结
    • Redis缓存过期机制
      • 1. 定期删除(主动)
      • 2. 惰性删除 (被动)
      • 3. 内存淘汰管理机制
        • 6种内存淘汰策略
        • 淘汰策略的选择
    • Redis 的哨兵模式
      • 1. 什么是哨兵
      • 2. 示例图
      • 3. 配置哨兵监控master
      • 4. 启动哨兵 x 3
      • 5. Master数据同步问题
      • 6. 结论
    • redis 集群
      • 1. 概述
      • 2. 配置文件
    • redis的 一致性哈希算法
      • 1.前景
      • 2.基本概念
      • 3.容错性和可扩展性
      • 4. 数据倾斜问题
    • 缓存击穿
      • 解决方案
    • redis的缓存穿透
      • 1.概念
      • 2.危害
      • 3.解决方案
        • 1.缓存空对象
        • 2.布隆过滤器
    • redis的缓存雪崩
      • 1.概念
      • 2.解决方案
    • redis的面试题

redis

reids的常用数据类型

1.String

最基本的数据类型,二进制安全

使用

set name “String”

get name ( 返回"String" )

setnx rekey data:设置已经存在的key,不会覆盖

set key value ex time:设置带过期时间的数据

expire key:设置过期时间

ttl:查看剩余时间,-1永不过期,-2过期

append key:合并字符串

strlen key:字符串长度

incr key:累加1

decr key:类减1

incrby key num:累加给定数值

decrby key num:累减给定数值

getrange key start end:截取数据,end=-1 代表到最后

setrange key start newdata:从start位置开始替换数据

mset:连续设值

mget:连续取值

msetnx:连续设置,如果存在则不设置

其他

select index:切换数据库,总共默认16个

flushdb:删除当前下边db中的数据

flushall:删除所有db中的数据

用途

redis中最基本的数据类型,使用范围相当广泛

2.Hash

类似map,适合用于存储对象

hmest lilei name “LiLei” age 18 sex “男”

hget lilei age(返回18)

使用

hset key property value:

-> hset user name username

-> 创建一个user对象,这个对象中包含name属性,name值为username

hget user name:获得用户对象中name的值

hmset:设置对象中的多个键值对

-> hset user age 18 phone 139123123

hmsetnx:设置对象中的多个键值对,存在则不添加

-> hset user age 18 phone 139123123

hmsetnx:设置对象中的多个键值对,存在则不添加

hmget:获得对象中的多个属性

-> hmget user age phone

hgetall user:获得整个对象的内容

hincrby user age 2:累加属性

hincrbyfloat user age 2.2:累加属性

hlen user:有多少个属性

hexists user age:判断属性是否存在

hkeys user:获得所有属性

hvals user:获得所有值

hdel user:删除对象

用途

很适合存储序列化后得到的json数据

3.List

列表,按照String元素插入顺序排序,类似与栈,先进后出

lpush mylist aaa

lpush mylist bbb

lpush mylist ccc

lrange mylist 0 3(返回“ccc” “bbb” “aaa”)

使用

lpush userList 1 2 3 4 5:构建一个list,从左边开始存入数据

``rpush userList 1 2 3 4 5:构建一个list,从右边开始存入数据`

lrange list start end:获得数据

lpop:从左侧开始拿出一个数据

rpop:从右侧开始拿出一个数据

pig cow sheep chicken duck

llen list:list长度

lindex list index:获取list下标的值

lset list index value:把某个下标的值替换

linsert list before/after value:插入一个新的值

lrem list num value:删除几个相同数据

ltrim list start end:截取值,替换原来的list

用途

可以用于实现最新消息排行等功能

4.Set

无序集合,通过哈希表实现,不允许重复、

使用

sadd myset aaa(插入成功,返回1)

sadd myset bbb

sadd myset aaa(插入失败,返回0)

用途

可以很轻易的达到去重的效果,比如可以将所有关注某个博主的粉丝存在同一个集合中,同时redis提供了方便的求交集、并集、差集等操作,所以可以非常方便的实现共同关注、共同喜好的功能。

5.Sorted Set(ZSet)

排序的set,可以去重且可以排序

zadd myzset 3 aaa

zadd myzset 2 bbb

zadd myzset 1 ccc

zrangebyscore myzset 0 10(返回结果为:“ccc” “bbb” “aaa”)

使用

zadd zset 10 value1 20 value2 30 value3:设置member和对应的分数

zrange zset 0 -1:查看所有zset中的内容

zrange zset 0 -1 withscores:带有分数

zrank zset value:获得对应的下标

zscore zset value:获得对应的分数

zcard zset:统计个数

zcount zset 分数1 分数2:统计个数

zrangebyscore zset 分数1 分数2:查询分数之间的member(包含分数1 分数2)

zrangebyscore zset (分数1 (分数2:查询分数之间的member(不包含分数1 和 分数2)

zrangebyscore zset 分数1 分数2 limit start end:查询分数之间的member(包含分数1 分数2),获得的结果集再次根据下标区间做查询

zrem zset value:删除member

用途

可以实现分数排名、用户积分排名等,还可以用作带权重的消息任务,重要任务的权重为1,优先执行,次要的为2

6.其他

用于计数的HyperLogLog,用于支持存储地理位置信息的Geo等

用于布隆过滤器,用于判断数据是否存在(解决缓存穿透的方案之一)


发布(pub)订阅模式(sub)模式

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第1张图片

订阅up主,更新后接到通知,和MQ机制类似

  1. 发布:PUBLISH [channal名称] [Massage消息]
  2. 订阅:SUBSCRIBE [channal名称]
  3. 批量订阅:PSUBSCRIBE [channal前缀*] (使用前缀加通配符的方式)

作用

只能起到基本的发布订阅作用,不能保证消息的容错性和可靠性,如果项目中发布的消息比较重要,不建议使用redis做消息处理,推荐使用MQ。

Redis的持久化机制 - RDB

  • RDB(默认): Redis DataBase
  • AOF(配置): Append Only File

1. 什么是RDB

RDB(全量备份):每隔一段时间,把内存中的数据写入磁盘的临时文件,作为快照,恢复的时候把快照文件读进内存。如果宕机重启,那么内存里的数据肯定会没有的,那么再次启动redis后,则会恢复。

2. 备份与恢复

内存备份 --> 磁盘临时文件
临时文件 --> 恢复到内存

3. RDB优劣势

  • 优势
    1. 每隔一段时间备份,全量备份
    2. 灾备简单,可以远程传输
    3. 子进程备份的时候,主进程不会有任何io操作(不会有写入修改或删除),保证备份数据的的完整性
    4. 相对AOF来说,当有更大文件的时候可以快速重启恢复
  • 劣势
    1. 发生故障时,有可能会丢失最后一次的备份数据
    2. 子进程所占用的内存比会和父进程一模一样,如果数据量比较大,会造成CPU负担
    3. 由于定时全量备份是重量级操作,所以对于实时备份,就无法处理了

4. RDB的配置

  1. 保存位置,可以在redis.conf自定义:
    /user/local/redis/working/dump.rdb

  2. 配置文件:

    Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第2张图片

  3. 保存机制:

save 900 1
save 300 10
save 60 10000
save 10 3
* 如果1个缓存更新,则15分钟后备份
* 如果10个缓存更新,则5分钟后备份
* 如果10000个缓存更新,则1分钟后备份
* 演示:更新3个缓存,10秒后备份
* 演示:备份dump.rdb,删除重启
  1. stop-writes-on-bgsave-error
    • yes:如果save过程出错,则停止写操作
    • no:可能造成数据不一致
  2. rdbcompression
    • yes:开启rdb压缩模式
    • no:关闭,会节约cpu损耗,但是文件会大,道理同nginx
  3. rdbchecksum
    • yes:使用CRC64算法校验对rdb进行数据校验,有10%性能损耗
    • no:不校验

5. RDB总结

RDB适合大量数据的恢复,但是数据的完整性和一致性可能会不足。

Redis的持久化机制 - AOF

1. 引子

RDB可能会丢失最后一次备份的rdb文件,但是其实也无所谓,通常情况下也可以忽略不计,毕竟是缓存,丢了就丢了,但是如果追求数据的完整性,那就得考虑使用AOF了。

2. AOF特点

  1. 以日志的形式来记录用户请求的写操作。读操作不会记录,因为写操作才会存储。
  2. 文件以追加的形式而不是修改的形式。
  3. redis的aof恢复其实就是把追加的文件从开始到结尾读取执行写操作。

3. AOF优势与劣势

  • 优势
    1. AOF更加耐用,可以以秒级别为单位备份,如果发生问题,也只会丢失最后一秒的数据,大大增加了可靠性和数据完整性。所以AOF可以每秒备份一次,使用fsync操作。
    2. 以log日志形式追加,如果磁盘满了,会执行 redis-check-aof 工具
    3. 当数据太大的时候,redis可以在后台自动重写aof。当redis继续把日志追加到老的文件中去时,重写也是非常安全的,不会影响客户端的读写操作。
    4. AOF 日志包含的所有写操作,会更加便于redis的解析恢复。
  • 劣势
    1. 相同的数据,同一份数据,AOF比RDB大
    2. 针对不同的同步机制,AOF会比RDB慢,因为AOF每秒都会备份做写操作,这样相对与RDB来说就略低。 每秒备份fsync没毛病,但是如果客户端的每次写入就做一次备份fsync的话,那么redis的性能就会下降。
    3. AOF发生过bug,就是数据恢复的时候数据不完整,这样显得AOF会比较脆弱,容易出现bug,因为AOF没有RDB那么简单,但是呢为了防止bug的产生,AOF就不会根据旧的指令去重构,而是根据当时缓存中存在的数据指令去做重构,这样就更加健壮和可靠了。

4. AOF的配置

# AOF 默认关闭,yes可以开启
appendonly no

# AOF 的文件名
appendfilename "appendonly.aof"

# no:不同步
# everysec:每秒备份,推荐使用
# always:每次操作都会备份,安全并且数据完整,但是慢性能差
appendfsync everysec

# 重写的时候是否要同步,no可以保证数据安全
no-appendfsync-on-rewrite no

# 重写机制:避免文件越来越大,自动优化压缩指令,会fork一个新的进程去完成重写动作,新进程里的内存数据会被重写,此时旧的aof文件不会被读取使用,类似rdb
# 当前AOF文件的大小是上次AOF大小的100%(可设置) 并且文件体积达到64m(根据运维监控修改),满足两者则触发重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

5. 思考:到底采用RDB还是AOF呢?

  • 如果你能接受一段时间的缓存丢失,那么可以使用RDB
  • 如果你对实时性的数据比较care,那么就用AOF
  • 使用RDB和AOF结合一起做持久化,RDB做冷备,可以在不同时期对不同版本做恢复,AOF做热备,保证数据仅仅只有1秒的损失。当AOF破损不可用了,那么再用RDB恢复,这样就做到了两者的相互结合,也就是说Redis恢复会先加载AOF,如果AOF有问题会再加载RDB,这样就达到冷热备份的目的了。

redis的主从(读写分离)架构

1. 概念图

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第3张图片

2. 主从原理

  • 磁盘化复制:
    Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第4张图片
  1. 当第一次开启主从时,Slave会主动的ping—>Master节点,Master接收到Slave的数据包后会同步它的数据给从节点;

  2. Master节点将自己的RDB文件复制一份到磁盘中;

  3. 通过内网传输RDB文件;

  4. Slave端接收到RDB文件会先将其持久化到磁盘中,再将数据读取到内存中。

    注意:

    • 第一次同步时是全量更新,后续的数据同步是增量的
    • 主节点一定要开启持久化,否则会导致从节点的数据丢失情况
    • 一定要使用内网传输RDB文件,防止数据泄露
  • 无磁盘化复制

    • 官方解释,目前处于试用阶段,暂时不建议使用
    • 使用socket的方式实现

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第5张图片

  1. master创建一个新的进程将RDB文件写入到socket中
  2. 然后通过网络传输,直接将文件传入到slave节点的内存中,在此过程中没有经过磁盘
    # 默认关闭无磁盘化复制方式
   	 repl-diskless-sync no

使用场景:

  • 适用于磁盘的速度比较慢(机械硬盘/低速率的SSD),网络速度比较快(带宽大)的服务器

3. 相关配置解析

# 配置主节点的ip和端口号
# slaveof  
slaveof 192.168.0.131 6379
# 主节点master的密码
# masterauth 
masterauth 123456
# 所有的从节点默认yes只有读的权限,没有写的权限
slave-read-only yes在配置主从模式时,通常都是一主二从的模式。

4. 总结

  • 一个主节点,对应着多个从节点,主节点负责写操作,从节点负责读操作
  • 主节点挂了,从节点默认不会推举出一个新的主节点,而是保持slave状态,等待master重新连接
  • 主从无法保证缓存的高可用性
  • 通常使用一主二从的机制,如果有资源配置更多节点,不建议使用主从模式

Redis缓存过期机制

1. 定期删除(主动)

redis会定期随机检测过期的key,如果检测到的key过期了,就主动的将key删除

  • 配置文件

  • redis默认每秒10次的随机检测过期的key,根据自己系统的情况,可用设置1-500次,但是设置的越高,占用的cpu资源越高,通常不建议超过100次

  •  # By default "hz" is set to 10. Raising the value will use more CPU when
     # Redis is idle, but at the same time will make Redis more responsive when
     # there are many keys expiring at the same time, and timeouts may be
     # handled with more precision.
     #
     # The range is between 1 and 500, however a value over 100 is usually not
     # a good idea. Most users should use the default of 10 and raise this up to
     # 100 only in environments where very low latency is required.
     hz 10 ```
    
    

2. 惰性删除 (被动)

redis不会定期检测过期的key,只有当有请求访问这个key时,才会检测该key是否过期,如果过期则删除这个key

3. 内存淘汰管理机制

Redis检查内存使用情况,如果实际使用内存已经超出maxmemory,Redis就会根据用户配置的淘汰策略选出无用的key

通常的,我们会为redis设置一个内存管理的大小maxmemory,当redis的内存快要达到这个阀值时,redis会主动清理访问率较低且永久存在的key(没有过期时间)

# maxmemory 

6种内存淘汰策略

  1. volatile-lru:从设置过期时间的数据集(server.db[i].expires)中挑选出最近最少使用的数据淘汰。没有设置过期时间的key不会被淘汰,这样就可以在增加内存空间的同时保证需要持久化的数据不会丢失。

  2. volatile-ttl:除了淘汰机制采用LRU,策略基本上与volatile-lru相似,从设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰,ttl值越大越优先被淘汰。

  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。当内存达到限制无法写入非过期时间的数据集时,可以通过该淘汰策略在主键空间中随机移除某个key。

  4. allkeys-lru(常用):从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰,该策略要淘汰的key面向的是全体key集合,而非过期的key集合。

  5. allkeys-random:从数据集(server.db[i].dict)中选择任意数据淘汰。

  6. noenviction:禁止驱逐数据,也就是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失,这也是系统默认的一种淘汰策略。

淘汰策略的选择

  1. 在Redis中,数据有一部分访问频率较高,其余部分访问频率较低,或者无法预测数据的使用频率时,设置allkeys-lru是比较合适的。

  2. 如果所有数据访问概率大致相等时,可以选择allkeys-random。

  3. 如果研发者需要通过设置不同的ttl来判断数据过期的先后顺序,此时可以选择volatile-ttl策略。

  4. 如果希望一些数据能长期被保存,而一些数据可以被淘汰掉时,选择volatile-lru或volatile-random都是比较不错的。

  5. 由于设置expire会消耗额外的内存,如果计划避免Redis内存在此项上的浪费,可以选用allkeys-lru 策略,这样就可以不再设置过期时间,高效利用内存了。


Redis 的哨兵模式

前面说到了redis的主从模式,但是如果Master挂了,slave节点就只能进行读请求,那如何保证可用性,继续实现读写操作呢?

1. 什么是哨兵

Sentinel(哨兵)是用于监控Redis集群中Master状态的工具,是 Redis 高可用解决方案之一,哨兵可以监视一个或者多个redis master服务,以及这些master服务的所有从服务;当某个master服务宕机后,会把这个master下的某个从服务升级为master来替代已宕机的master继续工作。

2. 示例图

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第6张图片

3. 配置哨兵监控master

创建并且配置sentinel.conf:

  • 普通配置

    # 端口号
    port 26379
    # 进程的pid,和redis主进程不是同一个pid
    pidfile "/usr/local/redis/sentinel/redis-sentinel.pid"
    # 工作空间
    dir "/usr/local/redis/sentinel"
    # 配置后台运行哨兵
    daemonize yes
    protected-mode no
    # 日志文件的位置
    logfile "/usr/local/redis/sentinel/redis-sentinel.log"
    
  • 核心配置

    # 配置哨兵的名称 和master的内网ip和端口号 2代表多少个哨兵在同一时间发现master连接不上就进行故障转移机制
    # 假设5个哨兵节点,有2个在同一发现master连接不上,2个节点的其中一个节点就可以开启故障转移了
    sentinel monitor mymaster 127.0.0.1 6379 2
    # 密码
    sentinel auth-pass <master-name> <password>
    # master被sentinel认定为失效的间隔时间,默认为30S
    sentinel down-after-milliseconds mymaster 30000
    # 剩余的slaves重新和新的master做同步的并行个数
    sentinel parallel-syncs mymaster 1
    # 主备切换的超时时间,哨兵要去做故障转移,这个时候哨兵也是一个进程,如果他没有去执行,超过这个时间后,会由其他的哨兵来处理
    sentinel failover-timeout mymaster 180000
    

4. 启动哨兵 x 3

redis-sentinel sentinel.conf

5. Master数据同步问题

一般master数据无法同步给slave的方案检查为如下:

  1. 网络通信问题,要保证互相ping通,内网互通。
  2. 关闭防火墙,对应的端口开放(虚拟机中建议永久关闭防火墙,云服务器的话需要保证内网互通)。
  3. 统一所有的密码,通过逐台检查机器以防某个节点被遗漏。

6. 结论

master挂了以后,由于哨兵监控,剩余slave会进行选举,选举后其中一个成为master,当原来的master恢复后,他会成为slave而不会重新变成master。


redis 集群

1. 概述

主从及主从+哨兵模式都会产生这样的问题:

当msater因突发状况宕机时,master节点来不及将数据同步到从节点上,会导致这部分的缓存数据丢失,且哨兵选举和故障转移也要耗费零点几秒甚至几秒的时间,在这段时间内,redis是无法提供写操作的,故而无法保证redis的高可用性,为保证缓存的高可用,我们可用采用redis的集群模式(通常是三主三从,也就是6个节点)。

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第7张图片

2. 配置文件

# 配置是否使用集群模式
cluster-enabled yes
# 配置节点的超时时间(超过多久没连接被认为挂掉了)
cluster-node-timeout 15000
# 开启aof
appendonly yes
appendfilename "appendonly.aof"

redis的 一致性哈希算法

1.前景

在Redis 集群模式Cluster中,Redis采用的是分片Sharding的方式,也就是将数据采用一定的分区策略分发到相应的集群节点中

但是我们使用上述HASH算法进行缓存时,会出现一些缺陷,主要体现在服务器数量变动(根据业务情况动态扩容,或节点宕机等场景)的时候,所有缓存的位置都要发生改变。

具体来讲就是说:当缓存服务器数量发生变化时,会引起缓存的雪崩,可能会引起整体系统压力过大而崩溃(大量缓存同一时间失效)。

2.基本概念

一致性Hash算法使用取模的方法,一致性Hash算法是对2^32取模,什么意思呢?

简单来说:一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2^32 - 1(即哈希值是一个32位无符号整形),整个哈希环如下:

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第8张图片

将服务器的IP或主机名使用hash算法进行计算,确定其在hash环上的位置,

举例:

假设我们有4台缓存服务器,服务器A、服务器B、服务器C,服务器D,那么,在生产环境中,这4台服务器肯定有自己的IP地址或主机名,我们使用它们各自的IP地址或主机名作为关键字进行哈希计算,使用哈希后的结果对2^32取模,可以使用如下公式示意:

 hash(服务器A的IP地址) %  2^32 

上述公式算出的结果一定是一个0到2^32-1之间的一个整数,上图中的hash环上必定有一个点与这个整数对应,而我们刚才已经说明,使用这个整数代表服务器A,那么,服务器A就可以映射到这个环上。

如下图,节点ABCD通过一致性的hash算法映射到hash的对应位置:

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第9张图片

同样的,将数据的key通过同样的hash运算定位到其在hash环上的位置,然后从此位置沿环顺时针“行走”,遇到的第一台服务器就是其应该定位到的服务器:

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第10张图片

3.容错性和可扩展性

  • 节点宕机

    假设Node C不幸宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响,如下所示:

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第11张图片

  • 节点增加

    如果在系统中增加一台服务器Node X,如下图所示:此时对象Object A、B、D不受影响只有C需要重定位到新的Node X

    一般的,在一致性Hash算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它数据不会受到影响

    Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第12张图片

总结:一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

4. 数据倾斜问题

概述:

一致性Hash算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中服务器和key分布不均时,如下图所示:

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第13张图片

此时必然造成大量数据集中到服务器1上,而只有极少量会定位到其他服务器上,从而出现hash环偏斜的情况,当hash环偏斜以后,缓存往往会极度不均衡的分布在各服务器上。

解决方案:

如果想要均衡的将缓存分布到服务器上,最好能让这些服务器尽量多的均匀的分布在hash环上,但是,真实的服务器资源只有4台,我们怎样让它们多起来呢?既然没有多余的物理服务器节点,我们就只能将现有的物理节点通过虚拟的方法复制出来。

这些由实际节点虚拟复制而来的节点被称为**”虚拟节点“,即对每一个服务节点计算多个哈希**,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器IP或主机名的后面增加编号来实现

例如上面的情况,假设有只有AB两台服务器,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第14张图片

总结:对每一个服务节点计算多个哈希,这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布


缓存击穿

是指一个热点key,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

比如在电商项目中,这个商品就称为“爆款”。

解决方案

  1. 设置热点key永不过期
  2. 接口做限流、降级、熔断
  3. 加互斥锁(如分布式锁,同时只有一个线程能够访问这个接口,并且从数据库查询后立马缓存到redis中,其他线程放行后可从redis中查询这个key)

redis的缓存穿透

1.概念

用户请求一个数据,发现redis中没有,也就是缓存没有命中,于是请求数据库查询,发现也没有。

当用户很多的时候,缓存都没有命中,于是请求都打在了数据库上,这会给持久层数据库造成很大的压力,这就是所谓的缓存穿透。

如图所示:

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第15张图片

2.危害

  1. 频繁的缓存穿透,会给数据库造成很大的压力,甚至导致数据库宕机
  2. 容易被黑客攻击
  3. 造成数据库服务的资源浪费

3.解决方案

1.缓存空对象

当数据库不命中后,即使返回的空对象也将其缓存起来,同时会设置过期时间,之后再访问这个数据将会从缓存中获取。

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第16张图片

问题:

  1. 缓存需要更多的存储空间,用来存放更多的key
  2. 即使对空值设置了过期时间,可能会存在缓存层和存储层数据,会有一段时间的数据不一致,这对于需要保持一致性的业务会有影响

2.布隆过滤器

布隆过滤器是一种数据结构 ,底层维护了一个只包含0,1数据的数组

数据经过hash运算(一种随机映射函数),映射到数组的多个下标位置,如果所有对应的下标位置值都为1,则该数据很可能有;如果对应的下标数字有一个不为1,则一定没有该数据。

**注:**布隆过滤器能确定一定没有该数据,但不能判断一定有该数据(可以设置布隆过滤器的参数,维护的数组越大,判断越精确,但消耗的空间也越大)

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第17张图片

过程:

  1. 将数据库中的数据hash后离线存储到布隆过滤器中
  2. 用户请求时直接从布隆过滤器中判断数据是否存在
  3. 如果布隆过滤器中没有该数据,则直接返回
  4. 如果布隆过滤器中有该数据,则从缓存或数据库中查询

Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第18张图片

问题:

  1. 布隆过滤器无法做到,当数据从数据库和redis删除时,布隆过滤器无法做到同步删除该数据(也就是说下次请求该数据时,布隆过滤器仍会判读有该数据存在,进而请求redis或数据库)
  2. 会有一定误判概率,不能判断一定有某个数据(可以设置布隆过滤器的参数,维护的数组越大,判断越精确,但消耗的空间也越大)
  3. 一定程度上增加代码的复杂度和系统的复杂度

示例代码:

  • 引入guava依赖

    		<dependency>
                <groupId>com.google.guavagroupId>
                <artifactId>guavaartifactId>
                <version>28.1-jreversion>
            dependency>
    
  • 代码:创建布隆过滤器,向过滤器中放入10000个元素,然后统计错误个数和错误率

    public class BloomFilterTest extends SpringBootApplicationTest {
        @Test
        public void myTest(){
            /**
             * BloomFilter一共四个create方法,不过最终都是走向第四个。看一下每个参数的含义:
             *    funnel:数据类型(一般是调用Funnels工具类中的)
             *    expectedInsertions:期望插入的值的个数
             *    fpp 错误率(默认值为0.03)
             *    strategy 哈希算法(指定某个算法)
             */
            BloomFilter<CharSequence> bloomFilter = BloomFilter.create(
                    Funnels.stringFunnel(Charset.forName("utf-8")), 10000
            );
    
            for (int i = 0; i < 10000; i++) {
                //向布隆过滤器中放入数据
                bloomFilter.put(String.valueOf(i));
            }
            
            //初始化误判数
            int missCount = 0;
            for (int i = 0; i < 10000; i++) {
                //判断这个数据在布隆过滤器中是否存在
                boolean exist = bloomFilter.mightContain("误判" + i);
                //如果误判了
                if (exist){
                    missCount ++;
                }
            }
    
            System.out.println("误判个数为:" + missCount );
            System.out.println("误判率为:" + BigDecimal.valueOf(missCount).divide(new BigDecimal(10000),2,ROUND_HALF_UP));
        }
    }
    
    
    输出:
    误判个数为:318
    误判率为:0.03
    

PS:

缓存穿透和缓存击穿的区别:缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。


redis的缓存雪崩

1.概念

缓存雪崩是指,缓存层出现了错误(如:同一时间大量的key失效,节点突然宕机等),不能正常工作了。所有的流量都打到了存储层,导致存储层压力暴增,甚至宕机。

2.解决方案

说明:缓存雪崩无法完全的解决,只能说是尽可能的预防

  • 永不过期

    一些请求量大的key我们可以设置成永不过期,这样一定程度上可以避免一些热点key突然失效的情况

  • 错开过期时间

    如:某些key的过期时间为1个小时,我们可以在1小时后面加上一个随机时间(如随机3-15分钟)

  • 多级缓存

    采用多级缓存的方式:

    如redis的key失效时间为1h,memcache的key失效时间为1.5h;

    甚至当redis或者memcache中有一个宕机时,仍能保证缓存的高可用性

    Redis基本数据类型、持久化机制、集群模式、淘汰策略、缓存穿透、击穿、雪崩、常见面试题大集合!_第19张图片


redis的面试题

1.什么是 Redis?

2.Redis 的数据类型

3.使用 Redis 有哪些好处

4.Redis 相比 Memcached 有哪些优势?

5.Memcached 与 Redis 的区别都有哪些?

6.Redis 是单进程单线程的吗?为何它那么快那么高效?

7.一个字符串类型的值能存储最大容量是多少?

8.Redis 的持久化机制是什么?各自的优缺点?

9.Redis 常见性能问题和解决方案有哪些?

10.Redis 过期键的删除策略

11.Redis 的回收策略(淘汰策略)?

12.为什么Redis 需要把所有数据放到内存中?

13.Redis 的同步机制了解么?

14.Pipeline 有什么好处,为什么要用 Pipeline?

15.是否使用过 Redis 集群,集群的原理是什么?

16.Redis 集群方案什么情况下会导致整个集群不可用

17.Redis 支持的 Java 客户端都有哪些?官方推荐用哪个?

18.Jedis 与 Redisson 对比有什么优缺点?

19.Redis 如何设置密码及验证密码?

20.说说 Redis 哈希槽的概念?

21.Redis 集群的主从复制模型是怎样的?

22.Redis 集群会有写操作丢失吗?为什么?

23.Redis 集群之间是如何复制的?

24.Redis 集群最大节点个数是多少?

25.Redis 集群如何选择数据库?

26.怎么测试 Redis 的连通性

27.怎么理解 Redis 事务

28.Redis 事务相关的命令有哪几个?

29.Redis key 的过期时间和永久有效分别怎么设置?

30.Redis 如何做内存优化

31.Redis 回收进程如何工作的?

32.都有哪些办法可以降低 Redis 的内存使用情况呢?

33.Redis 的内存用完了会发生什么?

34.一个 Redis 实例最多能存放多少的 keys?List、Set、Sorted Set他们最多能存放多少元素?

35.MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证Redis 中的数据都是热点数据

36.Redis 最适合的场景是什么?

37.假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?

38.如果有大量的 key 需要设置同一时间过期,一般需要注意什么?

39.使用过 Redis 做异步队列么,你是怎么用的?

40.使用过 Redis 分布式锁么,它是什么回事?

41.如何预防缓存穿透与雪崩

声明:部分图片来源于网上,非本人原创,谢谢大家支持!
另,创作不易,转载请声明出处,谢谢!

持续更新中,持之以恒ing……

你可能感兴趣的:(Redis,redis,缓存,数据库)