Redis高级

持久化机制

什么是持久化

利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化。

redis为了内存数据的安全考虑,会把内存中的数据以文件形式保存到硬盘中一份,在服务器重启之后会自动把硬盘的数据恢复到内存(redis)的里边。

redis支持两种持久化方式:

snapshotting(快照)    默认方式

append-only file(aof)    

snapshotting 快照

该持久化默认开启,一次性把redis中全部的数据保存一份存储在硬盘中(备份文件名字默认是dump.rdb),

如果数据非常多(10-20G)就不适合频繁进行该持久化操作。

  • 可以设置保存位置,和备份的文件名。

    dbfilename dump.rdb 快照文件的名称,默认是dump.rdb

    dir ./ 快照文件的存储位置

其他配置项

//导出的rdb文件是否压缩
rdbcompression yes 

//导入rdb恢复数据时,要不要检验rdb的完整性
Rdbchecksum yes

//后台备份进程出错时,主进程停不停止写入
stop-writes-on-bgsave-error    yes    
  • 手动发起快照

    方式一:登录状态

    直接执行bgsave即可
    

    方式二:没有登录状态

    ./redis-cli -a 密码 bgsave    
    
    
    
  • 自动执行(配置文件)

    每隔N分钟或N次写操作后,从内存dump数据形成rdb文件,压缩放在备份的目录中。

  • 如何开启 (默认开启,有自己的触发条件)

    save 900 1 900秒内如果超过1个key被修改,则发起快照保存

    save 300 10 300秒超过10个key被修改,发起快照

    save 60 10000 60秒超过10000个key被修改,发起快照

    注意:

    屏蔽该触发条件,即可关闭快照方式。
    
    
    
  • RDB优点
RDB是一个紧凑压缩的二进制文件,存储效率较高

RDB内部存储的是redis在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景

RDB恢复数据的速度要比AOF快很多

应用:服务器中没X小时执行bgsave备份,并将RDB文件拷贝到远程机器中,用于灾难恢复

  • RDB缺点
RDB方式无论是执行指令还是利用配置,由于快照方式是在一定间隔做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。

bgsave指令每次运行要执行fork操作创建子进程,要牺牲掉一些性能

Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式无法兼容形象

append-only-file AOF

RDB存储的弊端

  • 存储数据量较大,效率较低

    基于快照思想,每次读写都是全部数据,当数据量巨大时,效率非常低

  • 大数据量下的IO性能较低
  • 基于fork创建子进程,内存产生额外消耗
  • 宕机带来的数据丢失风险

本质:以独立日志的方式记录每次 “写”指令 (添加、修改、删除),重启时再再重新执行AOF文件中命令达到恢复数据的目的。 与RDB相比可以简单描述为 改记录数据为记录数据产生的过程

  • AOF写数据三种策略

    appendfsync always //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用

    appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐。

    appendfsync no //完全依赖 os ,性能最好,持久化没保证

  • 如何开启
//启用 aof 持久化方式,默认不开启
appendonly  yes

//策略只开启一个即可
appendfsync     everysec

//保存命令的文件(可以指定路径)
appendfilename   appendonly.aof
  • aof文件的重写

问题:每个命令重写一次aof,如果某个key操作100次,产生100行记录,aof文件会很大,怎么解决?

比如,当执行多次 incr number 操作,aof文件中会保存多次 incr number的命令。

这样会增大aof文件容量,我们可以对aof文件重写,把里面重复的命令压缩成一条命令。

就比如执行10次incr number 压缩成 set number 11

随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。

aof文件重写是将Redis进程内的数据转化为写命令同步到新AOF文件的过程。

简单说就是将对同一个数据的若干条命令执行结果转化成最终结果数据对应的指令进行记录。

作用:

降低磁盘占用量,提高磁盘利用率

提高持久化效率,降低持久化写时间,提高IO性能

降低数据恢复时间,提高数据恢复效率

  • 手动执行重写命令:

    登录状态:

        直接输入bgrewriteaof
    

    未登录状态:

        ./bin/redis-cli -a 密码 bgrewriteaof
    
  • 执行自动重写条件:

    //文件大小比起上次重写时的大小,增长率100%时,重写
    auto-aof-rewrite-percentage 100

    //aof文件,至少超过64M时,重写
    auto-aof-rewrite-min-size 64mb

    //正在导出rdb快照的过程中,要不要停止同步aof
    no-appendfsync-on-rewrite yes

其他问题

  • 在dump rdb过程中,aof如果停止同步,会不会丢失?
不会,所有的操作缓存在内存的队列里,dump完成后,统一操作
  • aof重写是指什么
aof重写是指把内存中的数据,逆化成命令,写入到.aof日志里,以解决aof日志过大的问题。
  • 如果rdb文件,和aof文件都存在,优先用谁来恢复数据
aof
  • 两种是否可以同时使用
可以,而且推荐这么做
  • 恢复时,rdb和aof哪个恢复的快
rdb快,因为其是数据的内存映射,直接载入到内存,而aof是命令,需要逐条执行。

注意点:

如果两种持久化方式都开启,则以aof为准,
虽然快照方式恢复速度快,但是最终被aof给覆盖,所以两种方式都开启时,以aof为准。


redis事务

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

Redis 与 mysql 事务的对比

MySQL Redis
开启 start transaction multi
语句 普通sql 普通命令
失败 rollback回滚 discard取消
成功 commit exec

## 基本操作

set zhao 1000

set wang 2000

multi   //开启事务

decrby zhao 100  //加入事务队列

incrby wang 100  //加入事务队列

exec   // 提交事务,真正执行

mget zhao wang
//900
//2100
mget zhao wang
//900
//2100

multi            //开启事务

decyby zhao 200  //加入事务队列

incrby wang 200  //加入事务队列

discard          //取消事务,释放事务队列            

mget zhao wang
//900
//2100

语句出错可能有2种情况

1、语法有问题

这种情况,exec时,报错,所有语句得不到执行

mget zhao wang 
//900
//2100

multi

decrby zhao 200

aghdsajd    // 语法错误

exec        // 提交事务,就被取消了,因为有语法错误

mget zhao wang
//900
//2100

2、语法本身没错,但适用对象有问题,

比如zadd操作list对象,exec之后,会执行正确的语句,并跳过有不适当的语句

mget zhao wang 
//900
//2100

multi

decrby zhao 200

sadd wang 200  该语句语法没有错误,但是会执行失败,wang是字符串类型,操作的是集合的语法。

exec   //提交时,会成功一半,就有点违反了事务原子性了。

mget zhao wang
//700
//2100


watch锁

watch命令可以监控一个或多个key,一旦其中有一个key被修改(或删除),之后的事务就不会执行,

监控一直持续到exec命令(事务中的命令是在exec之后才执行的,exec命令执行完之后被监控的key会自动被unwatch)

场景:我正在买票

ticket -1 , money -100

而票只有一张,如果在multi之后,和exec之前,票被别人买了------即ticket变成0了。

该如何观察这种情况,并不再提交。

悲观的想法

    世界充满危险,肯定有人和我抢,给ticket上锁,只有我能操作   ------悲观锁


乐观的想法

    没有人和我抢,我只需要注意,有没有人更改ticket的值就可以   ------乐观锁

Redis的事务中,启用的是乐观锁,只负责监测key有没有被改动

set ticket 1

set money 100

watch ticket   //开启监测ticket值的变化,一旦有变化,当前的事务就取消
// ok 

multi

decr ticket

decrby money 100

exec 
//nil   返回nil,说明监视的ticket已经改变了,事务就取消了

分布式锁

业务场景:如何避免最后一件商品不被多人同时购买?【超卖问题】

解决方案:

使用setnx设置一个公共锁,利用setnx命令的返回值特征(有值则返回设置失败,无值则返回设置成功)

对于返回设置成功的,拥有控制权,进行下一步的具体业务操作

对于返回设置失败的,不具有控制权,排队或等待

操作完毕通过del操作释放锁

$redis = new Redis();
$redis->connect('127.0.0.1');
$redis->auth('321612');

 //加锁
 $lock = $redis -> setnx('lock-num',1);
 
 if($lock){
     $num = $redis->get('num');
     if($num>0){
        $redis->decr('num');
     }
     $redis->del('lock-num');
 }

死锁

依赖分布式锁的机制,某个用户操作时对应客户端宕机,且此时已经获取到锁。如何解决?

分析:

由于锁操作由用户控制加锁解锁,必定会存在加锁后未解锁的风险

需要解锁操作不能仅依赖用户控制,系统级别要给出对应的保底处理方案。

解决方案:

使用expire为锁key添加时间限定,到时不释放,放弃锁

expire key second

pexpire key milliseconds

删除策略

过期数据

Redis是一种内存级数据库,所有数据均存放在内存中,内存种的数据可以通过TTL指令获取其状态

XX:具有时效性的数据

-1:永久有效的数据

-2:已经过期的数据 或 被删除的数据 或 未定义的数据

数据删除策略

数据删除策略的目标:

在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕机或内存泄漏
  • 定时删除
创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作

优点:节约内存,到时就删除,快速释放掉不必要的内存占用

缺点:CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响Redis服务器响应时间和指令吞吐量

总结:用处理器性能换取存储空间(拿时间换空间)

  • 惰性删除

数据到达过期时间,不做处理。等下次访问该数据时

如果未过期,返回数据

发现已过期,删除,返回不存在

优点:节约CPU性能,发现必须删除的时候才删除

缺点:内存压力很大,出现长期占用内存的数据

总结:用存储空间换取处理器的性能(拿空间换时间)

  • 定期删除
周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度

特点1:CPU性能占用设置有峰值,检测频度可自定义设置

特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理

总结:周期性抽查存储空间(随机抽查,重点抽查)

原理:

Redis启动服务器初始化时,读取配置server.hz的值,默认为10

每秒钟执行server.hz次serverCron()

​ --》databasesCron()

​ --》activeExpireCycle()

activeExpireCycle()对每个expires[*]逐一进行检测,每次执行250ms/server.hz

对某个expires[*]检测时,随机挑选W个key检测

如果key超时,删除key

如果一轮中删除的key的数量>W*25%,循环该过程

如果一轮中删除的key的数量<=W*25%,检查下一个expires[*],0-15循环

W取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值

参数current_db用于记录activeExpireCycle()进入哪个expires[*]执行

如果activeExpireCycle()执行时间到期,下次从current_db继续向下执行

  • 删除策略对比
定时删除 惰性删除 定期删除
内存 节约内存,无占用 内存占用严重 内存定期随机清理
CPU 不分时段占用CPU资源,频度高 延时执行,CPU利用率高 每秒花费固定的CPU资源维护内存
总结 拿时间换空间 拿空间换时间 随机抽查,重点抽查

redis内部使用惰性删除、定期删除

逐出算法

当新数据进入redis时,如果内存不足怎么办?

redis使用内存存储数据,在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足。

如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。

清理数据的策略称为逐出算法。

注意:

逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。

当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息。

影响数据逐出的相关配置

//最大可使用内存。
maxmemory    //占用物理内存的比例,默认值为0,表示不限制。生产环境中根据需求设定,通常设置在50%以上

//每次选取待删除数据的个数
maxmemory-samples    //选取数据时并不会全库扫描,导致严重的性能消耗,降低读写性能。因此采用随机获取数据的方式作为待检测删除数据

//逐出策略
maxmemory-policy    //达到最大内存后的,对被挑选出来的数据进行删除的策略

逐出策略有三类

  • 检测易失数据(可能会过期的数据集server.db[i].expires)
volatile-lru:挑选最近最少使用的数据淘汰

volatile-lfu:挑选最近使用次数最少的数据淘汰

volatile-ttl:挑选将要过期的数据淘汰

volatile-random:任意选择数据淘汰

  • 检测全库数据(所有数据集server.db[i].dict)
allkeys-lru:挑选最近最少使用的数据淘汰

allkeys-lfu:挑选最近使用次数最少的数据淘汰

allkeys-random:任意选择数据淘汰

  • 放弃数据驱逐
no-enviction:禁止驱逐数据(redis4.0中默认策略),会引发错误OOM(Out Of Memory)

例:具体配置

maxmemory-policy volatile-lru

数据逐出策略配置依据

使用INFO命令输出监控信息,查询缓存hit和miss的次数,根据业务需求调优Redis配置

keyspace_hits

keyspace_misses

服务器配置

服务端设定

//设置服务器以守护进程的方式运行
daemonize yes|no

//绑定主机地址
bind 127.0.0.1

//设置服务器端口号
port 6379

//设置数据库数量
databases 16

日志配置

//设置服务器以指定日志记录级别
loglevel debug|verbose|notice|warning

//日志记录文件名
logfile 端口号.log

客户端配置

//设置同一时间最大客户端连接数,默认无限制。当客户端连接到达上限,Redis会关闭新的连接
maxclients 0

//客户端闲置等待最大时长,达到最大值后关闭连接。如需关闭该功能,设置为0
timeout 300

多服务器快捷配置

导入并加载指定配置文件信息,用于快速创建redis公共配置较多的redis实例配置文件,便于维护
include /path/server-端口号.conf

高级数据类型 bitmaps

作用:应用于信息状态统计

基本操作

  • setbit key offset value 设置指定key对应偏移量上的bit值,value只能是1或0
setbit bits 0 1
  • getbit key offset 获取指定key对应偏移量上的bit值
getbit bits 0
//1

getbit bits 10
//0

扩展操作

电影网站

统计每天某一步电影是否被点播

统计每天有多少部电影被点播

统计每周/月/年有多少部电影被点播

统计年度哪部电影没有被点播

  • bitcount key [start end] 统计指定key中1的数量
setbit 20200808 11 1
setbit 20200808 333 1
setbit 20200808 1024 1

setbit 20200809 44 1
setbit 20200809 55 1
setbit 20200809 1024 1

bitcount 20200808 
//3
bitcount 20200809
//3

setbit 20200808 6 1
bitcount 20200808 
//4
  • bitop op destKey key1 [key2...] 对指定key按位进行交、并、非、异或操作,并将结果保存到destKey中
and:交

or:并

not:非

xor:异或

bitop or 08-09 20200808 20200809

高级数据类型 HyperLogLog

作用:应用于基数统计

基数:基数是数据集去重后元素个数

注意:

用于进行基数统计,不是集合,不保存数据,只记录数据而不是具体数据

核心是基数估算算法,最终数值存在一定误差

误差范围:基数估计的结果是一个带有0.81%标准错误的近似值

耗空间极少,每个hyperloglog key占用12k的内存用于标记基数

pfmerge命令合并后占用的存储空间为12k,无论合并之前数据量多少

基本操作

  • pfadd key element [element...] 添加数据
pfadd hll 1

pfadd hll 1

pfadd hll 1

pfadd hll 1

pfadd hll 2
pfadd hll 2
pfadd hll 3
  • pfcount key [key...] 统计数据
pfcount hll
//3
  • pfmerge destkey sourcekey [sourcekey...] 合并数据

高级数据类型 GEO

作用:应用于地理位置信息计算

基本操作

  • 添加地理位置的坐标

    geoadd key longitude latitude member [longitude latitude member ...]

geoadd geos 1 1 a

geoadd geos 2 2 b
  • 获取地理位置的坐标

    geopos key member [member...]

geopos geos a

geopos geos b
  • 计算两个位置之间的距离

    geodist key member1 member2 [unit]

geodist geos a b m

geodist geos a b km
  • 根据用户给定的经纬度坐标来获取指定范围内的地理位置集合

    GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

    geoadd geos 1 1 1,1
    geoadd geos 1 2 1,2
    geoadd geos 1 3 1,3
    geoadd geos 2 1 2,1
    geoadd geos 2 2 2,2
    geoadd geos 2 3 2,3
    geoadd geos 3 1 3,1
    geoadd geos 3 2 3,2
    geoadd geos 3 3 3,3
    geoadd geos 5 5 5,5
    
    georadius geos 1.5 1.5 90 km
    //1,2
    //2,2
    //1,1
    //2,1
  • 根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合

    GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

    georadiusbymember geos 2,2 180 km
    //1,1
    //2,1
    //1,2
    //2,2
    //3,1
    //3,2
    //1,3
    //2,3
    //3,3
  • 返回一个或多个位置对象的 geohash 值

    GEOHASH key member [member ...]

    geohash geos 2,2
    //s037ms06g70

主从复制

为了降低每个redis服务器的负载,可以多设置几个,并做主从模式

一个服务器负载“写”(添加、修改、删除)数据,其他服务器负载“读”数据,主服务器数据会“自动”同步给从服务器。

image-20200819215609769

Redis支持简单易用的主从复制(master-slave replication)功能,该功能可以让从服务器(slave server)成为主服务器(master server)的精确复制品。

主从复制的作用:

读写分离:master写,slave读,提高服务器的读写负载能力

负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数量,通过多个从节点分担数据读取负载,大大提高redis服务器并发量与数据吞吐量

故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复

数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式

高可用基石:基于主从复制,构建哨兵模式与集群,实现redis的高可用方案

注意点:

Redis使用异步复制,复制功能不能阻塞主、从服务器

一个主服务器可以有多个从服务器,不仅主服务器可以有从服务器,从服务器也可以有自己的从服务器

主从复制三个阶段:

建立连接阶段

数据同步阶段

命令传播阶段

主从通信过程

image-20200819220821039

配置步骤

准备两台虚拟机:

192.168.1.69 主
192.168.1.70 从

主服务器配置

bind 127.0.0.1 改为 #bind 127.0.0.1

protected-mode yes 改为 protected-mode no

从服务器配置

  • 通过slaveof指定自己的角色,主服务器的地址和ip

        //slaveof 主服务器ip 端口号
    slaveof 192.168.1.69 6379
    
  • 从服务器只读

    //从redis2.6开始,从服务器支持只读模式,通过slave-read-only配置项配置,该模式为从服务器的默认模式。
    slave-read-only yes
  • 指定从服务器连接主服务器的密码

    如果主服务器通过requirepass选项设置了密码,为了让从服务器同步操作顺利进行,通过masterauth配置连接主服务器密码。

    masterauth 321612(主服务器密码)

**验证**:


    在从服务器执行info replication 命令查看是否配置正确
    
    master_link_status:up
    如果是up表示配置成功,如果是down表示不能同步,配置失败

**撤销**:

> 只需在从服务器里面屏蔽上面的配置即可。



## 数据同步阶段注意事项

master:

* 如果master数据量巨大,数据同步阶段应避开流量高峰期,避免造成master阻塞,影响业务正常执行
* 复制缓冲区大小设定不合理,会导致数据溢出。如进行全量复制周期太长,进行部分复制时发现数据已经存在丢失的情况,必须进行第二次全量复制,致使slave陷入死循环状态。

* master单机内存占用主机内存的比例不应过大,建议使用50%-70%的内存,留下30%-50%的内存用于执行bgsave命令和创建复制缓冲区

//默认1mb
repl-backlog-size 1mb


slave:

* 为避免slave进行全量复制、部分复制时服务器响应阻塞或数据不同步,建议关闭此期间的对外服务

slave-serve-stale-data yes|no


* 数据同步阶段,master发送给slave信息(ping)可以理解master是slave的一个客户端,主动向slave发送命令

* 多个slave同时对master请求数据同步,master发送的RDB文件增多,会给带宽造成巨大冲击,如果master带宽不足,因此数据同步需要根据业务需求,适量错峰

  

## 命令传播阶段

* 当master数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的状态,同步的动作称为命令传播

* master将接收到的数据变更命令发送给slave,slave接收命令后执行命令



命令传播阶段的部分复制:

* 命令传播阶段出现了断网现象
  * 网络闪断闪连    忽略
  * 短时间网络中断   部分复制
  * 长时间网络中断   全量复制

* 部分复制的三个核心要素
  * 服务器的运行id(run id)
  * 主服务器的复制积压缓冲区
  * 主从服务器的复制偏移量



## 主从复制常见问题

**频繁的全量复制**

**频繁的网络中断**

**数据不一致**



每次slave服务器断开后,无论是主动断开,还是网络故障,再连接master,从服务器都要从master服务器全部dump出来rdb,再aof;

即同步的过程都要重新执行一遍,

所以要记住如果是多台从服务器时,不要一下子都启动起来。



# 哨兵

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

哨兵的作用:

* 监控
  * 不断的检查master和slave是否正常运行
  * master存活检测、master与slave运行情况检测

* 通知(提醒)
* 自动故障转移
  * 断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址

注意:

​        哨兵也是一台redis服务器,只是不提供数据服务

​        通常哨兵配置数量为单数



## 启用哨兵模式

* 配置一拖二的主从结构
* 配置三个哨兵sentinel.conf(配置相同,端口不同)

//端口
port 26379

//信息存储目录
dir /tmp

//mymaster:自定义的名 2:多少个哨兵认为主挂掉,通常设定(哨兵数量/2)+1
sentinel monitor mymaster 127.0.0.1 6379 2

//监控的主多长时间没响应
sentinel down-after-milliseconds mymaster 30000

//有几条线开始数据同步
sentinel parallel-syncs mymaster 1

//同步超时
sentinel failover-timeout mymaster 180000


* 启动哨兵

  * redis-sentinel sentinel-端口号.conf

  

## 哨兵工作原理

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



* 监控    用于同步各个节点的状态信息

  * 获取各个sentinel的状态(是否在线)

  * 获取master的状态

    * master属性
      * runid
      * role:master
    * 各个slave的详细信息

  * 获取所有slave的状态(根据master中的slave信息)

    * slave属性

      * runid
      * role:slave
      * master_host、master_port
      * offset
      * ……

      

* 通知

  

* 故障转移

  * 投票选择哪个sentinel,来做处理者
  * 服务器列表中挑选备选master
    * 在线的
    * 响应慢的
    * 与原master断开时间久的
    * 优先原则
      * 优先级
      * offset
      * runid
  * 发送指令(sentinel)
    * 向新的master发送slaveof no one
    * 向其他slave发送slaveof新masterIP端口

集群

集群就是使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果

集群作用:

分散单台服务器的访问压力,实现负载均衡

分散单台服务器的存储压力,实现可扩展性

降低单台服务器宕机带来的业务灾难

Redis集群结构设计

数据存储设计

  • 通过算法设计,计算出key应该保存的位置
  • 将所有的存储空间计划切割成16384份,每台主机保存一部分

    • 每份代表的是一个存储空间,不是一个key的保存空间
  • 将key按照计算出的结果放到对应的存储空间
  • 增强可扩展性

集群内部通讯设计

  • 各个数据库相互通信,保存各个库中槽的编号数据
  • 一次命中,直接返回
  • 一次未命中,告知具体位置

cluster集群结构搭建

三组主从(一对一)架构

配置redis.conf

//设置加入cluster,成为其中的节点
cluster-enabled    yes

//cluster配置文件名,该文件属于自动生成,仅用于快速查找文件并查询文件内容
cluster-config-file    nodes-6379.conf

//节点服务响应超时时间,用于判定该节点是否下线或切换为从节点
cluster-node-timeout 10000

//master连接的slave最小数量
cluster-migration-barrier 

启动cluster

//1:一个master连1个slave    主在前,从在后   三组,前三个为主,后三个为从
./redis-trib.rb create --replicas 2 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384

操作

redis-cli -c 

//设置数据
set name xiaoming

//获取数据
get name 

企业级解决方案

缓存预热

问题:服务器启动后迅速宕机

排查:

1、请求数量较高

2、主从之间数据吞吐量较大,数据同步操作频度较高

解决方案:

  • 前置准备工作:

    1、日常例行统计数据访问记录,统计访问频度较高的热点数据

    2、利用LRU数据删除策略,构建数据留存队列

  • 准备工作:

    将统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据

    利用分布式多服务器同时进行数据读取,提速数据加载过程

  • 实施:

    使用脚本程序固定触发数据预热过程

    如果条件允许,使用了CDN(内容分发网络),效果会更好

总结:

缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据。

缓存雪崩

数据库服务器崩溃:

  1. 系统平稳运行过程中,忽然数据库连接量激增
  2. 应用服务器无法及时处理请求
  3. 大量408,500错误页面出现
  4. 客户反复刷新页面获取数据
  5. 数据库崩溃
  6. 应用服务器崩溃
  7. 重启应用服务器无效
  8. redis服务器崩溃
  9. redis集群崩溃
  10. 重启数据库后再次被瞬间流量放倒

问题排查

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

问题分析

  • 短时间范围内
  • 大量key集中过期

解决方案(道)

  1. 更多的页面静态化处理
  2. 构建多级缓存架构

    Nginx缓存+redis缓存+ehcache缓存

  3. 检测Mysql严重耗时业务进行优化

    对数据库的瓶颈排查:例如超时查询、耗时较高事务等

  4. 灾难预警机制

    监控redis服务器性能指标

    • CPU占用、CPU使用率
    • 内存容量
    • 查询平均响应时间
    • 线程数
  5. 限流、降级

    短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问

解决方案(术)

  1. LRU与LFU切换
  2. 数据有效期策略调整

    • 根据业务数据有效期进行分类错峰,A类90分钟,B类80分钟,C类70分钟
    • 过期时间使用固定时间+随机值的形式,稀释集中到期的key的数量
  3. 超热数据使用永久key
  4. 定期维护(自动+人工)

    对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时

  5. 加锁

    慎用!

总结

缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。

如能够有效避免过期时间集中,可以有效解决雪崩现象的出现(约40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。

缓存击穿

数据库服务器崩溃

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

问题排查

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

问题分析

  • 单个key高热数据
  • key过期

解决方案(术)

  1. 预先设定

    以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时长

    注意:购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低的趋势

  2. 现场调整

    监控访问量,对自然流量激增的数据延长过期时间或设置为永久性key

  3. 后台刷新数据

    启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失

  4. 二级缓存

    设置不同的失效时间,保障不会被同时淘汰就行

  5. 加锁

    分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!

总结

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

应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过期监控难度较高,配合雪崩处理策略即可。

缓存穿透

数据库服务器崩溃

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

问题排查

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

问题分析

  • 获取的数据在数据库中也不存在,数据库查询未得到对应数据
  • Redis获取到null数据未进行持久化,直接返回
  • 下次此类数据到达,重复上述过程
  • 出现黑客攻击服务器

解决方案(术)

  1. 缓存null

    对查询结果为null的数据进行缓存(长期使用,定期清理),设定短时限,例如30-60秒,最高5分钟

  2. 白名单策略
  • 提前预热各种分类数据id对应的bitmaps,id作为bitmaps的offset,相当于设置了数据白名单。当加载正常数据时,放行,加载异常数据时直接拦截(效率偏低)
  • 使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)
  1. 实施监控

    实时监控redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比

  • 非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象
  • 活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象

    根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控(运营)

  1. key加密

    问题出现后,临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校验

    例如每天随机分配60个加密串,挑选2到3个,混淆到页面数据id中,发现访问key不满足规则,驳回数据访问

总结

缓存穿透:访问了不存在的数据,跳过了合法数据的redis数据缓存阶段,每次访问数据库,导致对数据库服务器造成压力。通常此类数据的出现量是一个较低的值,当出现此类情况以毒攻毒,并及时报警。

应对策略应该在临时预案防范方面多做文章。

无论是黑名单还是白名单,都是对整体系统的压力,警报解除后尽快移除。

性能指标监控

  • 性能指标:Performance
Name Description
latency Redis响应一个请求的时间
instantaneous_ops_per_sec 平均每秒处理请求总数
hit rate(calculated) 缓存命中率(计算出来的)
  • 内存指标:Memory
Name Description
used_memory 已使用内存
mem_fragmentation_ratio 内存碎片率
evicted_keys 由于最大内存限制被移除的key的数量
blocked_clients 由于BLPOOP,BRPOP,or BRPOPLPUSH而被阻塞的客户端
  • 基本活动指标:Basic activity
Name Description
connected_clients 客户端连接数
connected_slaves Slave数量
master_last_io_seconds_ago 最近一次主从交互之后的秒数
keyspace 数据库中的key值总数
  • 持久性指标:Persistence
Name Description
rdb_last_save_time 最后一次持久化保存到磁盘的时间戳
rdb_changes_since_last_save 自最后一次持久化以来数据库的更改数
  • 错误指标:Error
Name Description
rejected_connections 由于达到maxclient限制而被拒绝的连接数
keyspace_misses key值查找失败(没有命中)次数
master_link_down_since_seconds 主从断开的持续时间(以秒为单位)

性能指标监控命令

  • benchmark

命令

redis-benchmark [-h ] [-p ] [-c ] [-n  [-k ]
//默认:50个连接,10000次请求对应的性能
redis-benchmark

//100个连接,5000次请求对应的性能
redis-benchmark -c 100 -n 5000
  • monitor 打印服务器调试信息

monitor






* slowlog  [operator]
 * get :获取慢查询日志

 * len :获取慢查询日志条目数

 * reset :重置慢查询日志

slowlog get


 相关配置

slowlog-log-slower-than 1000 #设置慢查询的时间下线,单位:微妙

slowlog-max-len 100 #设置慢查询命令对应的日志显示长度,单位:命令数

你可能感兴趣的:(redis)