一篇学会Redis(下)

本系列内容根据自己的学习和理解的基础上,将介绍Redis相关的知识和一些基础操作。如果有写的不对的地方,请各位多多提点。



Redis

本篇为内容的下篇,上篇内容:一篇学会Redis(上).

Redis.conf

Redis.conf是redis的配置文件,与redis相关的参数设置都在其中,了解这些参数是对Redis进行调优的关键。

下面讲解一些常用的配置参数:

  • (1)单位
############## 第一部分:单位 ##############
# 当你需要为某个配置项指定内存大小的时候,必须要带上单位,
# 通常的格式就是 1k 5gb 4m 等:
#
# 1k  => 1000 bytes
# 1kb => 1024 bytes
# 1m  => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g  => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# 单位是不区分大小写的,你写 1K 5GB 4M 也行
  • (2)引入
############## 第二部分:INCLUDES  ##############
# include /path/dev.conf
# include /path/test.conf
# 将多个配置环境引入,
# 由于 redis 总是以最后的加工线作为一个配置指令值,所以最好把 include 放在配置文件的最前面
  • (3)网络
############## 第三部分:网络  ##############
bind 127.0.0.1  # 绑定ip
port 6379  # 端口号
protected-mode yes #保护模式,需配置bind ip或者设置访问密码
  • (4)通用
############## 第四部分:通用  ##############
# 以守护线程运行(即关闭后在后台运行),默认为no
daemonize no  

# 当redis作为守护进程运行的时候,它会把 pid 默认写到 
# /var/run/redis.pid 文件里面,可自行指定文件目录
pidfile /var/run/redis.pid  

# 日志级别(debug/verbose/notice/warning)
loglevel notice
# 指定日志文件的位置
logfile path

# 数据库个数,默认16个
database 16

#是否显示logo,默认yes
always-show-logo yes
  • (5)快照
############## 第五部分:快照  ##############
# 即在规定时间段内保存到磁盘上,若不配置快照则断电即失

#   下面的例子的意思是:
#   900 秒内如果至少有 1 个 key 的值变化,则保存
#   300 秒内如果至少有 10 个 key 的值变化,则保存
#   60 秒内如果至少有 10000 个 key 的值变化,则保存
#  
#   注意:你可以注释掉所有的 save 行来停用保存功能,或者svae "" 空串。
save 900 1
save 300 10
save 60 10000

# 持久化出错是否继续工作
stop-writes-on-bgsave-error yes

# 是否压缩rdb,会消耗cpu资源,否则快照会比较大,默认yes
rdbcompression yes

# 是否校验rdb文件
rdbchecksum yes

# dbfilename设置快照文件名称, dir设置路径
dbfilename dump.rdb
dir ./
  • (6)主从复制

一般主从复制相关参数不在配置文件中配置,否则每次重启就会按照配置文件参数设置,大多情况下用命令去动态配置从机。或者直接使用哨兵模式,则主要配置哨兵配置文件。

############## 第六部分:主从复制  ##############
# 主从复制。使用 slaveof 来让一个 redis 实例成为另一个reids 实例的副本。
# 注意这个只需要在 slave 上配置。
#
# slaveof <masterip> <masterport>

#当 master 不能正常工作的时候,Redis Sentinel 会从 slaves 中选出一个新的 master,
#优先级越高越容易选中,若为0则永远不可能选上,默认值100
slave-priority 100
  • (7)安全
############## 第七部分:安全  ##############
# 设置认证密码,Redis默认未设置密码,设置后需密码才能访问
# requirepass foobared

# 一些Redis安全相关的操作指令
 - config get requirepass //用指令获取配置的密码
 - config set requirepass psw//用指令设置密码为psw
 - auth psw //用指令登录redis,设置密码之后需认证通过才能操作
  • (8)限制
############## 第八部分:限制  ##############
# 客户端的最大连接数,一旦达到最大限制,redis 将关闭所有的新连接
# 并发送一个‘max number of clients reached’的错误。
maxclients 10000

# 最大使用内存
# maxmemory <bytes>

#达到内存上限后的处理策略,即过期淘汰策略,共有6种
# volatile-lru 只对设置了过期时间的key进行LRU(最近少使用)算法,默认此策略
# allkeys-lru 对所有的key进行LRU算法
# volatile-random 对即将过期的key随机删除
# allkeys-random 对所有key随机删除
# volatile-ttl 删除即将过期的
# noeviction 永不过期,抛出错误
maxmemory-policy noeviction
  • (9)AOF设置
######### 第九部分:APPEND ONLY MODE ########
# 默认不开启aof快照模式,使用rdb模式
appendonly no

# 持久化文件名,默认叫appendonly.aof
appendfilename "appendonly.aof"

# 持久化规则,有三种
# always 每次修改都会同步,消耗性能,速度最慢
# no 不执行同步,完全依赖操作系统,没有持久化,速度最快
# everysec 每秒执行一次,可能会丢失一秒的数据,默认方式
appendfsync everysec


Redis的持久化

Redis是基于内存的非关系型数据库,断电数据就丢失了,所以持久化非常重要。

Redis有两种持久化方式:RDB快照、AOF文件记录追加。

RDB持久化

RDB(Redis DataBase),是在指定的时间间隔内将内存中的数据集写入磁盘,也就是快照(sanpshot)。当需要恢复数据时将快照文件直接读入内存中即可。

Redis默认的持久化方式是RDB方式
RDB
RDB过程:Redis会单独创建(fork)一个子线程来进行持久化,将数据写入一个临时快照文件中,待持久化结束了,再用这个临时文件替换上次的同名快照文件。每次快照持久化都是将内存数据完整写入到磁盘一次

client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求,所以不推荐使用。

  • 优点
  1. 方便备份,归档和保留备份rdb文件否很容易。整个Redis数据库将只包含一个rdb文件,可每1天归档一些数据。将一个一个RDB文件移动到其他的存储介质上同样很方便。
  2. RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
  3. RDB 可以最大化 Redis 的性能。父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。

  • 缺点
  1. RDB会丢失最后一次的数据。 虽然 Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失最后一次里好几分钟的数据。
  2. 数据集过大时持久化可能导致停止。每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。


  • 触发条件
  1. save、bgsave操作。
  2. 执行flushall操作。
  3. 退出redis,也会产生rdb文件。

RDB方式适合需要大规模恢复数据,对数据恢复的完整性不敏感的情况。

AOF持久化

AOF(Append Only File)方式,以日志的形式来记录每个写操作,将每个写操作追加到文件中,只允许追加不允许修改,可以说是写操作记录日志。

恢复的方式是在Redis启动之初,会到设置的目录下找到aof文件,读取该文件重新构建数据。换言之,Redis重启的话就根据日志文件将写指令按顺序重新执行一遍以完成数据恢复工作。Redis可以对aof文件进行重写(rewrite),当aof文件超过指定大小重新创建一个aof文件写入。

AOF方式默认是关闭的,使用需手动开启。

  • 优点
  1. 每一次修改都会追加记录,完整性更好。
  2. 可每秒同步一次,可能会丢失一秒的数据;也可不同步日志,效率更高。

  • 缺点
  1. 相较于rdb文件来说,aof文件大小要大得多,且恢复数据的耗时也更长。
  2. aof 操作的效率比 rdb 效率低,默认使用的是rdb方式。

AOF适合对数据完整性要求高的情况。若想获取更高的数据安全性,可以两种方式一起使用。

持久化总结

方式说明优点缺点
RDB在指定的时间间隔内将内存中的数据集写入磁盘形成快照文件方便备份和移动备份快照;恢复数据的速度更快;性能最好,主线程没有I/O调度。可能丢失最后一次快照数据;子线程备份占用空间,且数据过大时会因为备份资源占用导致主线程停止。
AOF以日志的形式来追加记录每个写操作,只准追加不准修改数据完整性更高日志文件占用空间,执行效率和恢复数据效率慢。
RDB+AOF同时开启两种模式,重启后会优先使用aof文件恢复数据数据完整性最高文件占用空间,恢复数据效率慢。RDB文件不实时。
  1. RDB方式能够在指定的时间间隔内将数据生成快照文件。
  2. AOF方式记录每次对数据的写操作,以Redis命令追加到文件末尾。恢复数据时是将这些操作按顺序重新执行。Redis可以对aof文件进行重写(rewrite,当aof文件超过指定大小重新创建一个aof文件写入),使得aof文件不至于过大。
  3. 如果只用作缓存,数据只在服务器运行时使用,可以不开启持久化方式。
  4. 若同时开启两种方式(RDB+AOF):
  • 在这种方式下,当redis重启时会优先载入aof文件来恢复数据,因为通常情况下aof文件中恢复的数据要更完整,那么刚启动时恢复数据将更耗时。
  • RDB文件数据不实时。因为重启时也只会找aof文件进行恢复,那么rdb文件可能总是因为丢失最后一次备份的数据而导致不实时。
  • 备份数据相较于aof文件更推荐拷贝走rdb文件,因为rdb更适合用于数据备份(aof不断变化不好拷贝),恢复数据重启更快,而且不会存在aof可能因为指令错误导致的bug,作为一个万一的手段
  1. 性能建议。
  • 因为rdb文件一般只用作备份,建议只在slave上持久化rdb文件,而且频率不需要太过频繁,只保留默认配置文件中save 900 1 这一条备份设置15分钟一次就足够了。
  • 如果开启aof,好处是在最恶劣的情况下也只会丢失不超过2s的数据,重启时只加载slave中自己的aof文件即可。代价的话,一方面,带来了持续的I/O操作,增加消耗;另一方面,aof r重写的最后将重写过程中产生的新数据写到新文件造成的阻塞现象几乎是不可避免的。只要硬盘大小允许,应该增加重写的文件大小设置,降低重写的频率,可以考虑设置到5G以上,默认是超过100%进行重写也可以适当调整下比例。
  • 如果不开启aof,仅靠主从复制(master-slave Replication)实现高可用也可以,能省掉一大笔I/O,也减少了重写时带来的系统波动。代价是如果maste和slave同时挂掉,会丢失十几分钟的数据。重启后脚本会比较其中rdb文件,载入最新的那份(微博就是这种架构)。


发布订阅

Redis中发布订阅,可以用命令构建实时通讯应用,比如网络聊天室和实时广播、实时提醒等。通过PUBLISHSUBSCRIBEPSUBSCRIBE等命令来实现发布订阅。

Redis底层是使用c语言实现的,可通过分析Redis源码中的publish.c文件,了解发布和订阅的底层实现。

当通过SUBSCRIBE命令订阅某频道后,redis-server实际上是维护了一个字典,用于存储一个个频道。这个字典的值是个链表,链表用来存储订阅了此频道的用户客户端。SUBSCRIBE命令的关键,就是将客户端添加到指定channel的订阅链表中。

通过PUBLISH命令向订阅者发送消息,Redis会根据给定的编导作为key去字典中遍历出客服端信息,向他们发送信息。

操作说明
PSUBSCRIBE [pattern …]订阅一个或多个给定模式的频道
SUBSCRIBE [channel …]订阅一个或多个频道
PUBLISH channel message将信息发送到指定的频道
PUBSUB subcommand [argumeng …]查看发布与订阅系统的状态
UNSUBSCRIBE [channel …]退订一个或多个频道
PUNSUBSCRIBE [pattern …]退订一个或多个给定模式的频道


Redis主从复制

主从复制,是指将一台Redis服务器中的数据,复制到其他Redis服务器中,前者称为主节点(master/leader),后者称为从节点(slave/follower)。数据的复制只能从主节点复制到从节点,单向复制。master以写为主,slave以读为主。

默认情况下,每个Redis服务器都可以是主节点,而从节点需要进行配置。每个主节点可以有多个从节点,每个从节点只能有一个主节点。

  • 主要作用
  1. 数据冗余。主从复制实现了数据的热备份,是持久化之外的另一种冗余方式。
  2. 故障恢复。当主节点出现故障时,可以由从节点提供服务,实现快速的故障恢复。实际上是服务的一种冗余。
  3. 负载均衡。在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载。尤其是写少读多的场景下,由从节点分担负载,可以大大提高Redis服务器的并发量。
  4. 高可用(集群)基石。主从复制是哨兵模式和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

  • 运用场景分析

一般来说,要将Redis运用在工程项目中,只有一台Redis服务是万万不能的。

  1. 从结构上来说,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有请求,负载压力过大。
  2. 从容量上来说,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256G,也不能将所有内存作为Redis存储内存。一般来说,单个Redis服务器最大内存不应该超过20G。


主从复制配置

//查看当前Redis库信息,可以看到有没有从机等信息
- info replication

//单机多集群需要配置:端口  pid名字  log文件名字   dump.rdb文件路径及名称
//主从复制一般只配置从机即可,有从机连上主机
//真实环境中一般配置在配置文件中,以下命令仅做演示参考
- SLAVEOF ip port  //设置为从机,指定主机的ip和端口

# 配置文件中设置从机
replicaof <masterip> <masterport> 


  • 特殊情况
  1. 主机断开时,从机依然作为从机连接着。当主机重启后,仍然可以取到主机数据。
  2. 使用命令设置的从机,重启后就会失效。


复制原理

slave启动成功,连接到master后会发送一个sync命令,将主机的数据同步过来。

master接到命令时,启动后台的存储进程,同时收集所有接到的用于手改数据集的命令,在后台进程执行完毕后,master将数据传递整个文件给slave,并完成一次同步。

  • 全量复制

slave服务器在接收到数据库文件之后,将其存盘并加载到内存中。

  • 增量复制
    master将新的所有收集到的修改命令依次传给slave,完成同步。在原有数据基础上执行修改命令等于增量增加新数据。

但是只要slave重新连接master,就会自动执行一次全量复制(同步)。


哨兵模式

如果主机宕机了,等到主机恢复之后需要手动连接回去,或者立即使用slaveof no one,自动选举出一个主机。为了解决这种情况可以使用哨兵模式。

哨兵模式是一种特殊的模式,原理是通过发送命令,等待Redis服务器响应,从而监控多个Redis实例,当某个主机出现故障时,根据投票数自动在从机中选举出新主机。

哨兵是一个独立的进程,它会独立运行。Redis提供了哨兵的命令配置。

只有一个哨兵进程很容易出现问题,当哨兵也出现问题时就无法进行监控了。一般使用多个哨兵配合监控,各个哨兵之间也互相监控,形成多哨兵模式。

哨兵模式执行过程

  • 当发现问题时,哨兵会将投票结果通过发布订阅模式通知从节点们,并修改配置形成新的主从复制结构。
  • 当一个哨兵发现故障时,并不会立即进行failover(故障转移)操作。仅一个哨兵主管认为服务器不可用的情况称为主管下线现象。
  • 当某个哨兵都相继发现故障后,由一个哨兵发起投票,进行failover操作,然后通过发布订阅模式,通知各个哨兵把自己监控的从机切换主机,这个过程叫做客观下线。
  • 之前旧的主机重启后,哨兵会将其设为从机连上选举后的新主机。

  • 哨兵模式配置

配置哨兵配置文件 sentinel.conf

sentinel monitor name ip port type
#设置哨兵,type为1即开启选举模式,这是哨兵中最核心的配置

启动哨兵

- redis-sentinel path //用命令操作,以配置文件的方式启动哨兵


哨兵模式优缺点

  • 优点
  1. 基于主从模式,拥有主从复制模式的优点。
  2. 主从可以选举自动切换,故障可以快速转移,系统可用性更好。
  3. 哨兵模式就是主从模式的升级,从手动到自动选举,更加健壮。
  • 缺点
  1. Redis在线扩容不方便,集群容量一旦到达上限,在线扩容十分麻烦。
  2. 哨兵模式的配置有很多种方式,配置麻烦,灵活运用不是一件简单的事。


缓存穿透、击穿、雪崩


  • 缓存穿透

缓存穿透指的是缓存和数据库中都没有数据,而用户不断发起请求,当发起该请求的用户过多时,导致数据库的很大压力甚至宕机,出现的这种状况就是缓存穿透。

  • 缓存击穿

缓存击穿指的是,缓存中没有数据,但是数据库中有(大多数情况下是缓存到期了),这时由于读取该数据的用户过多,引起数据库的巨大压力甚至宕机。

  • 缓存雪崩

缓存雪崩是指,缓存中不同的数据大批量过期或者缓存服务器宕机,而查询数据巨大,引起的数据库压力过大甚至宕机。

现象名称情况特点解决方式
缓存穿透巨大并发访问某一个缓存和数据库中都没有的数据,导致数据库服务器压力超负荷或宕机空数据点穿透(1)布隆过滤器;(2)缓存空数据
缓存击穿巨大并发访问某一个缓存刚到期的数据,导致数据库服务器压力超负荷或宕机缓存到期点穿透(1)设置热点数据永不过期;(2)加互斥锁
缓存雪崩巨大并发访问多个缓存刚到期的数据,导致数据库服务器压力超负荷或宕机缓存到期面穿透(1)缓存高可用,增设几台备用集群;(2)限流降级,使用队列或者加锁控制数量;(3)数据预热


缓存穿透解决方案

  1. 布隆过滤器

布隆过滤器是一种数据结构,对所有可能查询的参数以hash的形式存储,在控制层先校验,不符合则丢弃,从而避免了对底层存储系统的压力。

  1. 缓存空对象

当存储层未命中后,即使返回空对象也将其缓存起来,同时会设置一个过期时间,之后再次未命中则可以取缓存,保护了后端存储系统。

该方式存在两个问题:

  • 如果缓存空值,意味着需要更多的空间存储键,浪费空间。
  • 即使对空值设置了过期时间,还是会存在一段时间内,缓存层和存储层数据不一致的窗口期。如果系统中有需要保持数据一致性的业务会产生影响。

缓存击穿解决方案

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

  2. 加互斥锁

使用分布式锁,让线程互斥的去查询,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式的权限,就只能等待了。这种方式将高并发的压力转移到了分布式锁上,因此对锁的考研很大。

缓存雪崩解决方案

  1. 缓存高可用

这个思想是指,多增设几台备用服务器,即搭建集群。

  1. 限流降级

这个思想是指,通过加锁或者队列的方式来控制同时访问的线程数量。

  1. 数据预热

数据预热就是指,在正式部署之前,把可能用到的数据都先访问一遍,先加载到缓存中,在即将发生大并发访问前手动设置好不同的过期时间,让失效时间尽量均匀分布。

Jedis

Jedis是官方推荐的Java链接Redis的工具,是Java操作Redis的中间件。

SpringBoot中整合Jedis的具体步骤:

  1. 导入Jedis的依赖,fastjson的依赖。
  2. 新建Jedis对象,查看源码,根据源码进行操作。其中有多个构建函数,常用带host、port的。
Jedis jedis = new Jedis("127.0.0.1","6379");
  1. 相关操作命令即Redis的操作指令,例如:
jedis.ping(); //等同于Redis 的 ping 命令操作,查看Redis服务是否已启动


SpringBoot整合Redis

SpringBoot 中操作数据的相关中间件都集成在 Spring-Data里,例如Jpa、Redis等的操作。Spring-Data 是与 SpringBoot 齐名的项目。


(1)引入依赖

  1. 引入Redis的启动器:spring-boot-start-data-redis
  2. 在SpringBoot 2.x之后,启动器中的Jedis被更换为了Lettuce。
  3. 更换的原因:
    Jedis,采用的直连,多线程下不是线程安全的,要避免此情况需要使用Jedis pool。类似于BIO。
    Lettuce,底层采用的netty,实例可以在多个线程中共享,不存在线程不安全的情况。类似于NIO。

(2)开启配置

  1. @RedisAutoConfiguration,Springboot中多有的配置类,都有一个自动配置类,需要在主入口方法上开启注解。
  2. 自动配置类都会关联绑定一个properties配置文件,即RedisProperties,在其中配置相应参数。
  3. RedisTemple模板工具类,用于操作Redis,在自动配置类中有条件判定注解,若没有自定义的模板文件就会使用默认的RedisTemple。
  4. 默认的RedisTemple模板工具类比较简单,没有序列化,且泛型中两个都设置的Object,为了避免中文没有序列化乱码的情况,通常都要自定义一个工具类。
    5.由于Sting是Redis中最常用的类型,默认已经创建了一个String类型的Temple模板工具类。

(3)配置参数

  1. 配置Redis、端口、host‘lettuce连接池等。

  2. 配置系统分布式参数、Redis集群等。

(4)自定义Temple模板工具类

默认的RedisTemple模板工具类没有序列化(序列化传递null),则底层使用了JDK的序列化,传递中文或者对象时会出现乱码。需要自定义一个工具类,并进行序列化转换为二进制避免乱码。

//配置类中自定义了Temple模板工具类
@Configuration
public class RedisConfig{
	@Bean
	//为了开发方便使用String,Object泛型
	Public RedisTemple<String,Object> redisTemple(RedisConnectionFactory factory){
		RedisTemple<String,Object> temple = new RedisTemple<String,Object>();
		temple.setConnectionFactory(factory);
		//使用Json方式传递对象,新建的对象可用new ObjectMapper().WriteValueAsString(Obejct);反序列化
		//创建的pojo类也需进行相应的序列化,implements Serializer就行
		Jackson2JsonRedisSerializer jlier = new Jackson2JsonRedisSerializer(Object.Class);
		ObjectMapper om = new ObjectMapper;
		om.setVisibility(PropertyAccessor.All,JsonAutoDetct.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jlier.setObjectMapper(OM);
		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
		//key、hash key采用String方式序列化
		temple.setKeySerializer(stringRedisSerializer);
		temple.setKeyHashSerializer(stringRedisSerializer);
		//value、hash value采用Json方式序列化
		temple.setValueSerializer(jlier);
		temple.setHashValueSerializer(jlier);
		temple.afterPropertiesSet();
		return temple.;
	}
}



(5)操作

  1. 创建RedisUtils工具类,使用try/catch块封装常用的操作系列指令在工具类中,之后可以直接使用工具类即可。
  2. 自动注入RedisTemple。使用工具类操作Redis。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值