黑马-Redis

1、Redis基础

2、Redis高级

2.1持久化

1、RDB(关注点:数据)

概述

当前数据进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据

开启方式一

命令:

127.0.0.1:6379>save

作用:手动执行一次保存操作,默会在Redis的安装路径的data目录下生成一个dump.rdb二进制文件

save指令相关配置
  • dbfilename dump.rdb
    • 说明:设置温蒂数据库文件名,默认值为dump.rdb
    • 经验:通常设置为dump-端口号.rdb
  • dir
    • 说明:设置存储.rdb文件的路径
    • 经验:通常设置成存储空间较大的目录中,目录名称data
  • rdbcompression yes
    • 说明:设置存储至本地数据库时是否压缩数据就,默认为yes,采用LZF压缩
    • 经验:通常默认为开启状态,如果设置为no,可以节省CPU运行时间,但会使存储文件变得巨大
  • rdbchecksum yes
    • 说明:设置是否进行rdb文件格式校验,该校验过程在写文件和读文件过程均进行
    • 经验:通常为开启状态,如果设置为no,可以解决读写过程约10%时间消耗,但是存在一定的数据损坏的风险
数据恢复过程

在Redis服务启动的时候,就会自动加载rdb文件,将数据恢复

save指令工作原理

黑马-Redis_第1张图片
当有四个客户端依次执行以上命令时,由于Redis单线程的特点,四条指令会被塞进队列中依次执行,但是save指令的执行会阻塞当前Redis服务器,直到当前rdb过程完成为止,因此有可能会造成长时间阻塞,线上环境不建议使用

开启方式二

命令

127.0.0.1:6379>bgsave
Background saving started

工作原理:
黑马-Redis_第2张图片

作用:手动启动后台保存操作(会调用glibc的函数fork产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求,子进程刚刚产生时,他和父进程共享内存里面的代码段和数据段),但不是立即执行

开启方式三

命令:
在配置文件中添加如下配置:

save second changes

作用:满足限定时间范围内(second)key的变化数量达到指定数量(changes)即进行持久化
范例:在10秒内300个key发生变化就会进行持久化

save 10 300
三种开启方式的对比

黑马-Redis_第3张图片

2、AOF(关注点:数据的操作过程)

概述

将数据的操作过程进行保存,日志形式,存储的是操作过程,存储格式复杂,关注点在数据的操作过程

AOF写数据过程

黑马-Redis_第4张图片
当Redis服务端接收到set指令时,并不会直接写.aof文件,而是先存储在缓存区,等达到一定条件之后再写.aof文件

AOF从缓存往.aof文件写数据的三种策略(appendfsync)
  • always(每次):每次写入操作均同步到AOF文件中,数据零误差,性能较低,不建议使用
  • everysec(每秒):每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,性能较高,建议使用,也是默认配置,但是在系统突然宕机的情况下会丢失1秒内的数据
  • no(系统控制):由操作系统控制每次同步到AOF文件的周期,整体过程不可控
开启方式

配置:
在配置文件中添加如下配置:

appendonly yes|no

作用:
是否开启AOF持久化功能,默认为不开启状态

配置:
在配置文件中添加如下配置:

appendfsync always|everysec|no

作用:
AOF写数据策略

其他配置:

appendfilename filename

修改AOF持久化文件名,建议配置为APPendonly-端口号.aof

dir

AOF持久化文件保存路径,与RDB持久化文件保持一致即可

AOF重写

随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。AOF文件重写是将Redis进程内的数据转化为写命令同步到新AOF文件的过程。简单说就是对同一个数据的弱干条命令执行结果转化为最终结果数据对应的指令进行记录。

如下图中左边的指令,重写后会生成左边两条指令,但最终结果都是一样的
黑马-Redis_第5张图片

AOF重写规则
  • 进程内已超时的数据不再写入文件
  • 忽略无效指令,重写时使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令
  • 对同一条数据的多条写命令合并为一条命令
    如:lpush a、lpush b、lpush c 可转化为:lpush a b c,为防止数据量过大造成客户端缓冲区溢出,对list、hash、set、zset等类型,每条指令最多写入64个元素
AOF重写方式

手动重写:

127.0.0.1:6379>bgrewriteaof
Background append only file rewriting started

自动重写:
在配置文件中添加如下配置:

auto-aof-rewrite-min-size(自动重写最小尺寸) size
auto-aof-rewrite-percentage(自动重写百分比) percentage
AOF重写原理

点击查看

3、RDB和AOF的区别

黑马-Redis_第6张图片

4、如何选择持久化方式

  • 对数据非常敏感,建议使用默认的AOF持久化方案
  • 数据呈现阶段有效性,建议使用RDB持久化方案
  • 综合对比
    • 如果不能承受数分钟以内的数据丢失,对业务数据非常敏感,选用AOF
    • 如果能承受数分钟以内的数据丢失,且追求大数据集的恢复速度,选用RDB
    • 灾难恢复选用RDB
    • 双保险策略,同时开启RDB和AOF,重启后,Redis优先选用AOF来恢复数据,降低丢失数据的量

5、持久化的应用场景

黑马-Redis_第7张图片

2.2Redis事务

1、事务简介

Redis事务就是一个命令执行的队列,将一系列预定义命令包装成一个整体(一个队列),当执行时,一次性按照添加顺序依次执行,中间不会被打断或者干扰

2、事务的基本操作

开启事务:

127.0.0.1:6379>multi

作用:设定事务的开启位置,此指令执行后,后续的所有指令均加入到事务队列中

执行事务:

127.0.0.1:6379>exec

作用:设定事务的结束位置,同时执行事务。与multi成对出现,成对使用

注意:加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令才开始执行

取消事务:

127.0.0.1:6379>discard

作用:终止当前事务的定义,发生在multi之后,exec之前

3、事务的工作流程

4、事物的注意事项

事务中的命令输入错误

描述:比如一个不存在的命令test key
事务处理:如果事务队列的命令中存在语法错误,那么所有的指令都不执行

事物的执行出现错误

描述:比如对string类型的key执行incr操作
事务处理:能够正确执行的命令会执行,运行错误的命令不会被执行

注意:已经执行完毕的命令对应的数据不会自动回滚,需要程序员自己在代码中实现回滚

5、锁

业务场景

天猫双11热卖过程中,对已经售罄的商品追加不过4个业务员都有权限进行补货。补货的操作可能是一些列的操作,牵扯到多个连续操作,如何保证不会重复操作?

业务分析
  • 多个客户端有可能同时操作同一组数据,并且该数据一旦被修改后将不适用于继续操作
  • 在操作之前锁定要操作的数据,一旦发生变化,终止当前操作
解决方案

对key添加监视锁在执行exec前如果key发生了变化,将终止事务执行

127.0.0.1:6379>watch key1 key2

取消对所有key的监视

127.0.0.1:6379>unwatch

黑马-Redis_第8张图片
注意:watch命令必须在multi命令之前执行

6、分布式锁

业务场景

天猫双11热卖过程中,对已经售罄的货物追加补货,且补货完成,客户购买热情高涨,3秒内将所有商品购买完毕,本次补货已经将库存清空,那么如何避免最后一件商品不被多人购买?【超卖问题】

解决方案

使用setnx设置一个公共锁

127.0.0.1:6379>setnx lock-key value

作用:利用setnx命令的返回值特性,有值则返回设置失败,无值则返回设置成功。对于返回设置成功的,拥有控制权,进行下一步的具体业务操作,对于返回设置失败的,不具有控制权,排队或等待。

注意:操作完毕通过del lock-key操作释放锁

黑马-Redis_第9张图片
但是有个问题:如果逻辑执行到中间出现异常了,可能会导致del指令没有被调用,这样就会陷入死锁,锁永远等不到释放。于是我们在拿到锁之后,再给锁加上一个过期时间,比如5s,这样即使中间出现异常可以保证锁会在5s之后得到释放。

> setnx lock-num true
OK
> expire lock-num 5
do something...
>del lock-num

但是这样还有一个问题,如果在setnx和expire之间服务器进程突然挂掉了,就会导致expire得不到执行,也会造成死锁。这种问题的根源就在于setnx和expire是两条指令而不是原子指令。

为了解决这个问题,在Redis2.8版本中,作者加入了set指令的扩展参数,是的setnx和expire指令可以一起执行,彻底解决了分布式锁。

> set lock-num true ex 5 nx
OK
do something...
> del lock-num

上面这个指令就是setnx和expire组合在一起的原子指令,他就是分布式锁的奥义所在

Redis的分布式锁不能解决超时问题,如果在加锁和解锁之间的逻辑执行的时间太长,以至于超出了了锁的超时限制,就会出现问题,这时因为第一个线程持有的锁过期了,临界区的逻辑还没执行完,而同时第二个线程就提前获取了这把锁,导致临界区的代码不能得到严格的串行执行。

为了避免这个问题,Redis分布式锁不要用于较长时间的任务

有一个稍微安全一点的方案就是将set指令的value参数设置为一个随机数,释放锁时先匹配随机数是否一致,然后再删除key,这是为了确保当前线程持有的锁不会被其他线程释放,除非这个锁时因为过期了而被服务器自动释放的。

但是匹配value和删除key也不是一个原子操作,这就需要Lua脚本来处理了。

2.3删除策略

过期删除策略
内存淘汰LRU算法

2.4redis.conf

2.5高级数据类型

1、Bitmaps(Redis高级数据结构之一)

在我们平时的开发过程中,会有一些bool型数据需要存取,比如用户一年的签到记录,签了是1,没签是0,要记录365天。如果使用普通的key/value,每个用户要记录365个,当用户上亿的时候,需要的存储空间是惊人的。

为了解决这个问题,Redis提供了位图数据结构,这样每天的签到记录值占据一个位,365天就是365个位,46个字节就可以完全放下。这就大大节约了存储空间。位图的最小单位是比特(bit),每个bit的取值只能是0或1。

获取指定key对应偏移量上的bit值

>getbit key offset

设置指定key对应偏移量上的bit值,value只能是1或0

>setbit key offset value

位图不是特殊的数据结构,他的内容其实就是个普通的字符串,也就是byte数组。

2、HyperLogLog(Redis高级数据结构之一)

如果要统计网站上的PV,那非常好办,给每个网页分配一个独立的Redis计数器就可以了,把这个计数器的key后缀加上当天的日期,这样来一个请求,执行incrby指令一次,最终就可以统计出所有的PV数据。

但是UV不一样,他要去重,同一个用户一天之内的多访问请求只能计数一次。这就要求每个网页的请求都需要带上用户的ID,无论是登录用户还是非登录用户都需要一个唯一ID来标识。

你也许已经想到了一个简单的方案:那就是为每一个网页设置一个独立的set集合来存储所有当天访问过此页面的用户ID,当一个请求过来时,我们是用sadd将用户ID塞进去就可以了,通过scard可以取出这个集合的大小,这个数字就是这个网页的UV。但是访问量很大时,那就需要一个很大的set集合来统计,这就非常浪费空间。

HyperLogLog就可以解决这个问题,HyperLogLog提供不精确的去重计数方案,虽然不精确,但也不是非常离谱,标准误差是0.81%。

使用方法:

HyperLogLog提供了两个指令pfadd和pfcount,一个是增加计数,一个是获取计数。

> pfadd codehole user1
(integer) 1
> pfadd codehole user2
(integer) 1
> pfcount codehole
(integer) 2

HyperLogLog出了上面两个命令之外还提供了第三个命令:pfmerge,用于将多个pf计数值加在一起形成新的值。

HyperLogLog数据结构需要占据12KB的存储空间,所以不适合统计单个用户的相关逻辑,如果用户有上亿个,可以知道,所使用的的存储空间相比于set就是九牛一毛。

3、GeoHash(Redis高级数据结构之一)

用于地理位置距离排序算法,如附近的人。

添加两个坐标点a和b并计算其距离

>geoadd geos 1 1 a
(integer) 1
>geoadd geos 2 2 b
(integer) 1
>geodist geos a b
"157270.0561"

2.6Redis集群

1、主从复制

主从复制简介

互联网“三高”架构:

  • 高并发
  • 高性能
  • 高可用(服务器年宕机时间低于315秒,约5.25分钟)

单机Redis的风险与问题:

  • 机器故障
  • 容量瓶颈

解决办法:为了避免单机Redis服务器故障,准备多台服务,互相连通,将数据复制多个副本保存在不同的服务器上,连接在一起,并保证数据是同步的。即使有一台服务器宕机,其他服务器依然可以继续提供服务,实现Redis的高可用,同时实现数据冗余备份

黑马-Redis_第10张图片
主从复制的核心工作:master的数据复制到slave中

主从复制的作用:

  • 读写分离:master写,slave读,提高服务器的读写负载能力
  • 负载均衡:基于主从架构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数据,通过多个从节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量。
  • 故障恢复:当master出现问题时,由slave提供服务,实现快速故障恢复
  • 数据冗余:实现数据热备份
  • 高可用基石:基于主从复制架构,构建哨兵模式与集群,实现Redis的高可用方案
主从复制工作流程

主从复制大致可以分为3个阶段(下面有详细描述):

  • 建立连接(①建立网络连接)
  • 数据同步(②数据同步)
  • 命令传播(③反复同步数据)
    黑马-Redis_第11张图片
第一个阶段:建立连接

步骤1:设置master的地址和端口,保存master信息
步骤2:建立socket连接
步骤3:发送ping命令
步骤4:身份验证
步骤5:发送slave端口信息
至此,主从连接成!
黑马-Redis_第12张图片
搭建步骤:

在slave的配置文件中添加如下配置

slaveof 127.0.0.1 6379

再次启动slave即可连接完成

状态:
slave:info中保存了master的ip地址与端口
黑马-Redis_第13张图片
master:info中保存slave的ip地址与端口
黑马-Redis_第14张图片
总体:两者之间建立了socket连接

第二个阶段:数据同步

步骤1:slave请求同步数据
步骤2:master创建RDB同步数据
步骤3:slave恢复RDB同步数据
步骤4:slave请求部分同步数据(全量同步产生的数据,同步的是指令而不是数据)
步骤5:slave恢复部分同步数据(slave执行同步过来的增量数据的指令,恢复数据)
至此,数据同步工作完成

黑马-Redis_第15张图片
状态:
slave:具有master端全部数据
master:保存slave当前数据同步的位子
总体:master和slave在当前节点数据一致

数据同步阶段master说明:

1、如果master数据量巨大,数据同步阶段应该避免流量高峰,避免造成master阻塞,影响业务运行
2、复制缓冲区大小设置不合理,会导致数据溢出。如进行全量复制周期太长,进行部分复制时发现缓冲区数据已经存在丢失情况,必须进行第二次全量复制,致使slave陷入死循环状态。
3、master单机内存占用主机内存的比例不应过大,建议使用50%-70%,留下30%-50%的内存用于执行bgsave命令和创建复制缓冲区

数据同步阶段slave说明:

1、为避免slave进行全量复制、部分复制时服务器响应阻塞或数据不同步,建议关闭此期间的对外服务
2、数据同步阶段,master发送给slave信息可以理解为master是slave的一个客户端,主动向slave发送命令
3、多个slave同时对master请求数据同步,master发送的RDB文件增多,会对带宽造成巨大冲击,所以要适量错峰
4、slave过多时,建议调整拓扑结构,由一主多从变为树状结构,中间节点既是master,也是slave。

第三个阶段:命令传播

命令传播阶段出现了断网现象:

  • 网络闪断闪连:忽略
  • 短时间网络中断:部分复制
  • 长时间网络中断:全量复制

部分复制的三个核心要素:

  • 服务器的运行id(run id)【识别身份】

    • 概念:服务器运行id是每一台服务器每次运行的身份识别码,一台服务器多次运行可以生成多个运行id
    • 组成:运行id由40位字符组成,是一个随机的十六进制字符
    • 作用:运行id被用于在服务器间进行传输,识别身份如果两次操作均对同一台服务器进行,必须每次操作携带对应的运行id,用于对方识别
    • 实现方式:运行id在每台服务器启动时自动生成master在首次连接slave时,会将自己的运行id发送给slave,slave保存此id,通过info service命令,可以查看节点的runid
  • 主服务器的复制积压缓冲区

    • 概念:是一个先进先出的队列,用于存储服务器执行过的命令,每次传播命令,master都会将传播的命令记下来,并存储在复制缓冲区
    • 工作原理
      黑马-Redis_第16张图片
  • 主从服务器的复制偏移量

    • 概念:一个数字,描述复制缓冲区中的指令字节位置
    • 分类:master复制偏移量(会有多个)和slave复制偏移量(只有一个)。
    • 作用:同步信息,比对master和slave的差异,当slave断线后,恢复数据使用

全流程总结
黑马-Redis_第17张图片

心跳检测

  • 进入命令传播阶段,master和slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线
  • master心跳
    • 指令:ping
    • 周期:默认10秒
    • 作用:判断slave是否在线
  • slave心跳
    • 指令:REPLCONF ACK {offset}
    • 周期:1秒
    • 作用:汇报salve自己的复制偏移量,获取最新的数据变更指令、判断master是否在线

注意:当slave多数掉线或延迟过高,master为保障数据稳定性,将拒绝所有信息同步操作

主从复制常见问题

1、伴随着系统的运行,master的数据量越来越大,一旦master重启,runid将会发生变化,会导致全部slave的全量复制操作。解决办法:内部调整

2、频繁的全量复制。解决办法:修改缓冲区的大小

3、多个slave之间数据不同步。解决办法:优化主从网络环境,通常部署在同一个机房、监控主从节点延迟,如果slave延迟过大,暂时屏蔽程序对该slave的数据访问。

2.7哨兵模式

1、哨兵简介

哨兵(sentinel)是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master
黑马-Redis_第18张图片
哨兵的作用

  • 监控:不断地检查master和slave是否正常运行
  • 通知(提醒):当被监控的服务器出现问题时,向其他(哨兵、客户端)发送通知
  • 自动故障转移:断开master与slave的连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址

注意:哨兵也是一台Redis服务器,只是不提供数据服务,通常哨兵配置数量为单数

2、启用哨兵模式

配置哨兵:

步骤1:配置一拖二的主从结构
步骤2:配置三个哨兵(配置相同,端口不同),参看sentinel.conf
步骤3:启动哨兵

redis-sentinel sentinel-端口号.conf

哨兵配置文件详解
黑马-Redis_第19张图片

启动顺序:先启动主机,再启动从机,最后启动哨兵

启动完哨兵配置文件发生的变化
黑马-Redis_第20张图片

3、哨兵工作原理

哨兵在进行主从切换的过程中经历三个阶段

监控

黑马-Redis_第21张图片
总结:sentinel会向master要状态,向slave要状态,向其他sentinel要状态,最后sentinel之间相互同步信息

通知

黑马-Redis_第22张图片

故障转移
第一个阶段:发现故障

黑马-Redis_第23张图片过程描述:某个sentinel向master发送hello指令,如果master没有回应,sentinel就会频繁发送hello指令,发到一定阶段以后,就会把这个master标记为SRI_S_DOWN(主观下线:只有一个sentinel认为master挂了)状态,同时把信息在sentinel之间同步,然后其他sentinel也会频繁向master发送hello指令,结果还是没回应,其他的sentinel同样会把信息在sentinel之间同步,同步完之后就会认为master真的挂了(其实是过半sentinel认为他挂了就是真的挂了,上面配置有写),同时把master的状态标记为SRI_O_DOWN(客观下线:超过半数以上的sentinel认为master挂了)。

第二个阶段:sentinel选举

黑马-Redis_第24张图片
sentinel之间投票选出一个leader:每一个sentinel会向其他sentinel发送消息,其他sentinel会根据接收到消息的顺序决定将票投给哪个sentinel,如果一轮没有投票产生,那就再来一轮投票,每增加一轮,竞选次数就+1,最终,拥有多数投票的sentinel处理下一步工作

第三个阶段:新master选择

黑马-Redis_第25张图片
sentinel处置阶段:被选举出来的sentinel会根据四大原则挑选备选的master,如下:

  • slave在线的
  • 与sentinel通信响应快的
  • 与原master断开时间较短的
  • 优先原则:
    • 优先级
    • offset较大的
    • runid比较小的
第四个阶段:后续处理

sentinel发送指令:
向新的master发送slaveof no one
向其他salve发送slaveof 新master的ip port,和新的master建立主从关系
向其他sentinel通过发布订阅机制同步新的master信息

2.8企业级解决方案

缓存预热

缓存预热就是系统启动前,提前将相关的缓存数据加载到缓存系统(Redis),避免在用户请求的时候,先查询数据库(MySQL),然后再将数据缓存的问题!用户直接查询视线被预热的缓存数据,可以优先降低数据库(MySQL的压力)。

缓存雪崩

现象:数据库服务器崩溃

1、系统平稳运行过程中,忽然数据库连接量激增
2、应用服务器无法及时处理请求
3、数据库崩溃
4、应用服务器崩溃
5、重启应用服务器无效
6、Redis服务器崩溃
7、重启数据库后再次被瞬间流量放倒

问题排查

1、在一个较短的时间内,缓存中较多的key集中过期
2、此周期内请求访问过期的数据,Redis未命中,Redis向数据库获取数据
3、数据库同时接收到大量的请求无法及时处理
4、Redis大量请求被积压,开始出现超时现象
5、数据库流量激增,数据库崩溃
6、重启后仍然面对缓存中无可用数据的问题
7、Redis服务器被严重占用,Redis服务器崩溃
8、应用服务器无法及时响应数据请求,来自客户端的请求越来越多,应用服务器崩溃
9、应用服务器、Redis、数据库全部重启,效果还是不理想

解决方案(道)

1、更多的页面静态化处理
2、构建多级缓存架构:Nginx缓存+Redis缓存+Cache缓存
3、检测MySQL严重耗时的业务进行SQL优化
4、灾难预警机制
5、限流、降级

解决方案(术)

1、LRU( 最近最少使用)与LFU(最不经常使用)切换
2、数据有效期策略调整:过期时间使用固定时间+随机值的形式,避免大量key同时过期
3、超热数据使用永久key
4、定期维护:对即将过期数据做访问量分析,配合访问量做热点数据的延迟过期

总结

缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如果能够有效避免过期时间集中,可以优先解决雪崩现象的出现,配合其他策略一起使用,并监控服务器的运行情况,根据运行记录做快速调整。

缓存击穿

现象:

1、系统平稳运行过程中
2、数据库连接量瞬间激增
3、Redis服务器无大量key过期
4、Redis内存和CPU平稳,无波动
5、数据库崩溃

问题排查

1、Redis中某个可以过期,该key访问量巨大
2、多个数据请求从服务器直接压到Redis,均为命中
3、Redis在短时间内发起了大量对数据库中同一数据的访问

解决方案

缓存击穿可以看做时缓存雪崩的一种特殊形式,所以解决方案可以参考缓存雪崩

总结

缓存击穿就是单个高热数据过期的瞬间,数据量访问较大,未命中Redis后,发起了大量对同一数据的数据库访问,导致对数据库服务器造成巨大压力。

缓存穿透

现象

1、应用系统运行平稳
2、应用服务器流量随时间增量较大
3、Redis服务器命中率随时间逐步降低
4、Redis内存平稳,CPU占用激增
5、数据库压力激增直至数据库崩溃

问题排查

1、Redis中大面积出现未命中key
2、出现非正常访问的URL访问

解决方案

1、将key与空结果缓存到Redis中,并设置一个60s有效期。那么在这一分钟里,所有同样的访问,都将直接从缓存中获得NULL
2、采用布隆过滤器,将数据库有的数据放一份副本到足够大的布隆过滤器中,只有用户访问,先把条件从布隆过滤器中判断一下,如果存储则放行,如果没有则直接拦截返回,从而避免了对底层数据库的访问
3、热点数据不过期 ,将用户频繁访问的热点数据设置为永不过期,这就可以保证访问不会落到数据库

性能监控指标

你可能感兴趣的:(面试题集锦,redis,数据库,缓存)