redis学习笔记(详细)——高级篇

redis配置文件介绍

linux环境下配置大于编程

redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf。一般情况下,会单独拷贝出来一份进行操作。来保证初始文件的安全

config get * # 获取全部的配置

include部分

组合多个配置。和Spring配置文件类似,可以通过includes包含,redis.conf 作为总文件,可以包含其他配置文件!

network网络部分

网络相关配置

bind 127.0.0.1 # 绑定的ip
protected-mode yes # 保护模式
port 6379 # 默认端口

General部分

序号 配置项 说明
1 daemonize no Redis 默认不是以守护进程的方式运行,可以通过该配置项修改,
使用 yes 启用守护进程(Windows 不支持守护线程的配置为 no )
2 tcp-backlog 设置tcp的backlog, backlog其实是一一个连接队列,
backlog队列总和 = 未完成三次握手队列+已经完成三次握手队列。
注意Linux内核会将这个值减小到/ proc/sys/ net/ core/somaxconn的值,
所以需要确认增大somaxconn和tcp_ max_ syn backlog两个值
5 timeout 300 当客户端闲置多少秒后关闭连接,如果指定为 0 ,表示关闭该功能
6 Tcp-keepalive 0 检测连接是否中断,设置为0时表示禁用该服务
7 loglevel notice 日志级别,Redis 总共支持四个级别:debug、verbose、notice、warning,
级别逐渐增高,打印的信息随着级别的变高而减少,默认为 notice
8 syslog-enabled no 是否打印日志到syslog中,默认为no
9 syslog-ident redis 指定syslog中的日志标志
10 databases 16 设置数据库的数量,默认数据库为0,可以使用SELECT 命令连接指定数据库id
11 logfile "" 日志文件的位置,当指定为空字符串时,为标准输出

SNAPSHOTTING部分

快照,持久化规则

AOF

# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
save 900 1
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
save 300 10
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 60 10000

RGB

stop-writes-on-bgsave-error yes # 持久化出现错误后,是否依然进行继续进行工作

rdbcompression yes # 使用压缩rdb文件 yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间

rdbchecksum yes # 是否校验rdb文件,更有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗

dbfilename dump.rdb # dbfilenamerdb文件名称

dir ./ # dir 数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录

REPLICATION主从复制

后续主从复制部分详细说明

SECURITY部分

# 启动redis
# 连接客户端

# 获得和设置密码
config get requirepass
config set requirepass "123456"
#密码置空:
config set requirepass  ''

#测试ping,发现需要验证
127.0.0.1:6379> ping
NOAUTH Authentication required.
# 验证: auth 密码
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> ping
PONG

客户端连接相关

maxclients 10000  最大客户端数量
maxmemory <bytes> 最大内存限制
maxmemory-policy noeviction # 内存达到限制值的处理策略

maxmemory-policy 六种方式

  • volatile-lru:利用LRU算法移除设置过过期时间的key。

  • allkeys-lru :用lru算法删除lkey

  • volatile-random:随机删除即将过期key

  • allkeys-random:随机删除

  • volatile-ttl :删除即将过期的

  • noeviction :不移除任何key,只是返回一个写错误。

redis 中的默认的过期策略是 volatile-lru

设置方式

config set maxmemory-policy volatile-lru 

append only mode 部分

AOF相关部分

appendonly no #默认是不开启AOF模式的,而是使用RGB方式持久化,大多数情况下RGB完全够用
appendfilename "appendonly.aof"	#持久化文件名
appendfsync everysec # appendfsync aof持久化策略的配置
        # no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
        # always表示每次写入都执行fsync,以保证数据同步到磁盘。
        # everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。

LIMITS部分

  • maxclients :设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息
  • maxmemory-policy 数据清除策
    • volatile-lru: 设置了过期时间的数据采取LRU(近期最少使用)算法.如果对key使用"expire"指令指定了过期时间,那么此key将会被添加到"过期集合"中。将已经过期/LRU的数据优先移除.如果"过期集合"中全部移除仍不能满足内存需求,将OOM。
    • allkeys-lru:对所有的数据,采用LRU算法
    • volatile-random:对设置了过期时间的数据采取"随即选取"算法,并移除选中的K-V,直到"内存足够"为止。如果"过期集合"中全部移除全部移除仍不能满足,将OOM
    • allkeys-random:对所有的数据,采取"随机选取"算法,并移除选中的K-V,直到"内存足够"为止
    • volatile-ttl:对设置了过期时间的数据采取TTL算法(最小存活时间),移除即将过期的数据。
    • noeviction:不做任何干扰操作,直接返回OOM异常,也是默认选项,实际开发不要用该选项
  • # maxmemory-samples 3:上面LRU和最小TTL策略并非严谨的策略,而是大约估算的方式,因此可以选择取样值以便检查,默认值3

redis持久化

传送门:redis——持久化

redis订阅发布

简介

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 客户端可以订阅任意数量的频道。

订阅/发布消息图:

redis学习笔记(详细)——高级篇

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

redis学习笔记(详细)——高级篇

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

redis学习笔记(详细)——高级篇

命令

下表列出了 redis 发布订阅常用命令:

序号 命令及描述
1 PSUBSCRIBE pattern [pattern ...]
订阅一个或多个符合给定模式的频道。
2 PUBSUB subcommand [argument [argument ...]]
查看订阅与发布系统状态。
3 PUBLISH channel message
将信息发送到指定的频道。
4 PUNSUBSCRIBE [pattern [pattern ...]]
退订所有给定模式的频道。
5 SUBSCRIBE channel [channel ...]]
订阅给定的一个或多个频道的信息。
6 UNSUBSCRIBE [channel [channel ...]]
指退订给定的频道。

测试

我们先打开两个 redis-cli 客户端

在第一个 redis-cli 客户端作为订阅客户端,创建订阅频道名为 redisChat,输入SUBSCRIBE redisChat

redis 127.0.0.1:6379> SUBSCRIBE redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1 

打开第二个客户端作为发送端,在指定频道上发布两次消息,订阅者就能接收 到消息。

redis 127.0.0.1:6379> PUBLISH redisChat "Hello,Redis"
(integer) 1
redis 127.0.0.1:6379> PUBLISH redisChat "Hello,java"
(integer) 1

订阅者的客户端会显示如下消息

1. "message"
2. "redisChat"
3. "Hello,Redis"

1. "message"
2. "redisChat"
3. "Hello,java"

总结

Redis是使用C实现的,通过分析 Redis 源码里的 pubsub.c 文件,了解发布和订阅机制的底层实现,来加深对 Redis 的理解。

Redis 通过 PUBLISH 、SUBSCRIBE 和 PSUBSCRIBE 等命令实现发布和订阅功能。

通过 SUBSCRIBE 命令订阅某频道后,redis-server 里维护了一个字典,字典的键就是一个个 channel ,而字典的值则是一个链表,链表中保存了所有订阅这个 channel 的客户端。SUBSCRIBE 命令的关键,就是将客户端添加到给定 channel 的订阅链表中。

通过 PUBLISH 命令向订阅者发送消息,redis-server 会使用给定的频道作为键,在它所维护的 channel 字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。

Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个 key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。

使用场景:Redis的Pub/Sub系统可以构建实时的消息系统,比如很多用Pub/Sub构建的实时聊天系统的例子

集群环境搭建

Redis集群详解

Redis支持三种集群方案

  • 主从复制模式
  • Sentinel(哨兵)模式
  • Cluster模式

主从复制

传送门:redis集群之主从复制 - 至安 - 博客园 (cnblogs.com)

哨兵模式

解决什么问题

哨兵模式之前主从切换的方法是当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵) 架构来解决这个问题。

谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独 立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

redis学习笔记(详细)——高级篇

这里的哨兵有两个作用

  • 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。

然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。 各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

redis学习笔记(详细)——高级篇

假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认 为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一 定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。 切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为 客观下线

相关配置

模式配置文件 sentinel.conf

# Example sentinel.conf
 
# 哨兵sentinel实例运行的端口 默认26379
port 26379
 
# 哨兵sentinel的工作目录
dir /tmp
 
# 哨兵sentinel监控的redis主节点的 ip port 
# master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1
 
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
 
 
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
 
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
 
 
 
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面: 
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。  
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
 
# SCRIPTS EXECUTION
 
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
 
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
#这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
#一个是事件的类型,
#一个是事件的描述。
#如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
#通知脚本
# sentinel notification-script <master-name> <script-path>
  sentinel notification-script mymaster /var/redis/notify.sh
 
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。 
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

实战测试

1 - 调整结构,6379带着80、81

2 - 在redis.conf同级目录下新建 sentinel.conf 文件,文件名固定

3 - 配置哨兵,填写内容

#sentinel monitor 被监控主机名字 127.0.0.1 6379 n
例如:sentinel monitor mymaster 127.0.0.1 6379 1,

上面最后一个数字n,表示得票数,主机挂掉后从机投票接替成为主机,得票数到n的从机接替成为主机

4 - 启动哨兵

Redis-sentinel myconfig/sentinel.conf

上述目录依照各自的实际情况配置,可能目录不同

成功启动哨兵模式

redis学习笔记(详细)——高级篇

此时哨兵监视着我们的主机6379,当我们断开主机后:

redis学习笔记(详细)——高级篇

哨兵模式的优缺点

优点

  1. 哨兵集群,基于主从复制模式,所有主从复制的优点,它都有
  2. 主从可以切换,故障可以转移,系统的可用性更好
  3. 哨兵模式是主从模式的升级,手动到自动,更加健壮

缺点:

  1. Redis不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦
  2. 实现哨兵模式的配置其实是很麻烦的,里面有很多配置项

cluster模式

Redis Cluster日常操作命令梳理 - 散尽浮华 - 博客园 (cnblogs.com)

缓存穿透和雪崩

缓存穿透(查不到)

在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据(例如秒杀场景)在缓存中都没有命中的话,就会全部转移到数据库上,造成数据库极大的压力,可能导致数据库崩溃。网络安全中也有人恶意使用这种手段进行攻击被称为洪水攻击。

解决方案

布隆过滤器

对所有可能查询的参数以Hash的形式存储,以便快速确定是否存在这个值,在控制层先进行拦截校验,校验不通过直接打回,减轻了存储系统的压力。

redis学习笔记(详细)——高级篇

缓存空对象

一次请求若在缓存和数据库中都没找到,就在缓存中方一个空对象用于处理后续这个请求。

redis学习笔记(详细)——高级篇

这样做有一个缺陷:存储空对象也需要空间,大量的空对象会耗费一定的空间,存储效率并不高。解决这个缺陷的方式就是设置较短过期时间。即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响

缓存击穿(量太大,缓存过期)

相较于缓存穿透,缓存击穿的目的性更强,缓存中原本存在的key在过期的一刻,同时有大量的请求,这些请求都会击穿到数据库,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。

比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。

解决方案

  1. 设置热点数据永不过期

    这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间。

  2. 加互斥锁(分布式锁)

    在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问。这样对锁的要求就十分高。

缓存雪崩

大量的key设置了相同的过期时间,导致缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。

比如马上就要到双十一零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

redis学习笔记(详细)——高级篇

其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然 形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就 是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知 的,很有可能瞬间就把数据库压垮。

解决方案

  • redis高可用

    这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群

  • 限流降级

    这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

  • 数据预热

    数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

上一篇:Redis之哨兵模式


下一篇:Sentinel-Go 源码系列(二)|初始化流程和责任链设计模式