全网最详细Redis高阶从入门到精通

全网最详细Redis高阶从入门到入坑

1.Redis 基础数据结构

Redis 有 5 种基础数据结构,分别为:
 ● string (字符串)
 ● list (列表)
 ● set (集合)
 ● hash (哈希)
 ● zset (有序集合)。
全网最详细Redis高阶从入门到精通_第1张图片
string (字符串)
 ● 字符串 string 是 Redis 最简单的数据结构。
 ● Redis 所有的数据结构都是以唯一的 key 字符串作为名称,然后通过这个唯一 key 值来获取相应的 value 数据。
 ● 不同类型的数据结构的差异就在于 value 的结构不一样
 ● 字符串结构使用非常广泛,一个常见的用途就是缓存用户信息。我们将用户信息结构体使用 JSON 序列化成字符串,然后将序列化后的字符串塞进 Redis 来缓存。同样,获得取用户信息会经过一次反序列化的过程。
键值对(get、set底层使用HashMap实现,生成对应的HashCode)
全网最详细Redis高阶从入门到精通_第2张图片
批量键值对:可以批量对多个字符串进行读写,节省网络耗时开销
全网最详细Redis高阶从入门到精通_第3张图片
过期和 set 命令扩展:可以对 key 设置过期时间,到点自动删除,这个功能常用来控制缓存的失效时间。
全网最详细Redis高阶从入门到精通_第4张图片
原子计数:如果 value 值是一个整数,还可以对它进行自增操作。自增是有范围的,它的范围是 signed long 的最大最小值,超过了这个值,Redis 会报错。
全网最详细Redis高阶从入门到精通_第5张图片
list (列表)
Redis 的列表相当于 Java 语言里面的 LinkedList,注意它是链表而不是数组。这意味着 list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为 O(n),这点让人非常意外。 当列表弹出了最后一个元素之后,该数据结构自动被删除,内存被回收。
Redis 的列表结构常用来做异步队列使用。将需要延后处理的任务结构体序列化成字符串塞进 Redis 的列表,另一个线程从这个列表中轮询数据进行处理。

右边进右边出:栈
全网最详细Redis高阶从入门到精通_第6张图片
右边进左边出:队列
全网最详细Redis高阶从入门到精通_第7张图片
hash (字典)

 ● Redis 的字典相当于 Java 语言里面的 HashMap,它是无序字典。内部实现结构上同 Java 的 HashMap 也是一致的,同样的数组 + 链表二维结构。第一维 hash 的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。

 ● hash 结构也可以用来存储用户信息,不同于字符串一次性需要全部序列化整个对象,hash 可以对用户结构中的每个字段单独存储。这样当我们需要获取用户信息时可以进行部分获取。而以整个字符串的形式去保存用户信息的话就只能一次性全部读取,这样就会比较浪费网络流量
 ● hash 也有缺点,hash 结构的存储消耗要高于单个字符串,到底该使用 hash 还是字符串,需要根据实际情况再三权衡。

全网最详细Redis高阶从入门到精通_第8张图片
全网最详细Redis高阶从入门到精通_第9张图片

set (集合)

Redis 的集合相当于 Java 语言里面的 HashSet,它内部的键值对是无序的唯一的,可以用来存放某个用户的兴趣爱好。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值NULL。 当集合中最后一个元素移除之后,数据结构自动删除,内存被回收。

全网最详细Redis高阶从入门到精通_第10张图片
全网最详细Redis高阶从入门到精通_第11张图片
zset (有序集合)

 ● zset 似于 Java 的 SortedSet 和 HashMap 的结合体,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权重
 ● zset 可以用来存粉丝列表,value 值是粉丝的用户 ID,score 是关注时间。我们可以对粉丝列表按关注时间进行排序。
 ● zset 还可以用来存储学生的成绩,value 值是学生的 ID,score 是他的考试成绩。我们可以对成绩按分数进行排序就可以得到他的名次。
全网最详细Redis高阶从入门到精通_第12张图片
全网最详细Redis高阶从入门到精通_第13张图片
ZSet应用之排行榜系统
全网最详细Redis高阶从入门到精通_第14张图片
全网最详细Redis高阶从入门到精通_第15张图片
redis文章投票项目地址

ZSet、List、Set 的比较
全网最详细Redis高阶从入门到精通_第16张图片

其他高级命令
scan:渐进式遍历键,scan 参数提供了三个参数,第一个是 cursor 整数值,第二个是 key 的正则模式,第三个是遍历的 limit hint。第一次遍历时,cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor,一直遍历到返回的 cursor 值为 0 时结束。
全网最详细Redis高阶从入门到精通_第17张图片

基本操作

 1.开启redis服务并开启redis客户端: src/redis-server redis.conf、src/redis-cli
 2.重启redis服务:ps -fe | grep redis找出redis的pid、kill 该pid、src/redis-server redis.conf

核心原理

Redis的单线程和高性能

Redis 单线程为什么还能这么快?
 ●因为它所有的数据都在内存中,所有的运算都是内存级别的运算
 ●而且单线程避免了多线程的切换性能损耗问题。正因为 Redis 是单线程,所以要小心使用 Redis 指令,对于那些耗时的指令(比如keys),一定要谨慎使用,一不小心就可能会导致 Redis 卡顿。

Redis 单线程如何处理那么多的并发客户端连接?
Redis的IO多路复用:redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。
Nginx也是采用IO多路复用原理解决C10K问题

问题如果某个命令执行慢,会造成其它命令的阻塞,比如 set name kun等指令,redis会将set、get这些指令放到一个地方,然后执行执行并返回结果。
全网最详细Redis高阶从入门到精通_第18张图片
全网最详细Redis高阶从入门到精通_第19张图片
全网最详细Redis高阶从入门到精通_第20张图片

2.持久化

2.1、RDB快照(snapshot)

在默认情况下, Redis 将内存数据库快照保存在名字为 dump.rdb 的二进制文件中。
你可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集。
比如说, 以下设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次数据集:#save 60 1000

2.1.1、RDB快照工作流程

 1. redis根据配置自己尝试生成rdb快照文件
 2. fork一个子进程出来
 3. 子进程尝试将数据写到临时的rdb快照文件中
 4. 完成rdb快照文件的生成之后,就替换之前旧的快照文件,并删除旧的 RDB 文件。
 5. AOF持久化会记录服务器执行的所有写操作命令,并在redis重启的时候,通过回访AOF文件来还原数据集,文件使用append-only的默认,所有操作都会追加到文件的末尾。当AOF文件体积超出保存数据集状态的情况下,会进行一个重写(rewrite)操作,确保文件不会太大。通常情况下,AOF文件保存的数据及会比RDB文件保存的数据集更完整
默认情况下AOF持久化是关闭的,通过以下配置开启AOF持久化:appendonly yes

RDB 的优点:
RDB 是一个非常紧凑(compact)的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份: 比如说,你可以在最近的 24 小时内,每小时备份一次 RDB 文件,并且在每个月的每一天,也备份一个 RDB 文件。 这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。RDB 非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的数据中心,或者亚马逊 S3 中。RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

RDB 的缺点:

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

2.2、AOF(append-only file)

 ● 快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化,将修改的每一条指令记录进文件。
 ● 你可以通过修改配置文件来打开 AOF 功能:#appendonly yes
 ● 从现在开始, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾
这样的话, 当 Redis 重新启时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。
 ● 你可以配置 Redis 多久才将数据 fsync 到磁盘一次。#appendfsync everysec

AOF 的优点:

使用 AOF 持久化会让 Redis 变得非常耐久(much more durable):你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)。AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek , 即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

AOF 的缺点:

对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。AOF 在过去曾经发生过这样的 bug : 因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。 (举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。) 测试套件里为这种情况添加了测试: 它们会自动生成随机的、复杂的数据集, 并通过重新载入这些数据来确保一切正常。 虽然这种 bug 在 AOF 文件中并不常见, 但是对比来说, RDB 几乎是不可能出现这种 bug 的。
AOF的fsync策略

可以通过配置以下内容来配置AOF的fsync策略:

appendfsync always
appendfsync everysec
appendfsync no

always每写入一条数据,立即将这个数据对应给的写日志fsync到磁盘上去,性能会变得很差,但是可以确保说每一条数据都不会丢失

everysec每秒将os cache中的数据fsync到磁盘,这个最常用,生产环境下一般都使用这种策略,性能很高

no仅仅负责将数据写到os cache就可以了,不进行fsync等待操作系统对这些数据写入磁盘,这种策略,会让我们的数据不可控,因为无法预知操作系统什么时候会把数据刷到磁盘。

AOF的rewrite

redis中的数据是有限的,很多数据可能会自动过期,也有可能会被用户删除,但是这些操作都会被AOF文件记录下来,所有有可能存在AOF文件越来越大的情况,意思就是说,重建数据集根本不需要执行所有AOF记录的命令,为了处理这种情况,会有一种rewrite策略,最终合并为对该key操作的一条指令,在redis 2.4之前是需要手动执行BGREWRITEAOF命令来进行重写AOF文件,redis 2.4版本之后提供了自动rewrite的操作。我们可以配置rewrite的策略。
 ● auto-aof-rewrite-percentage 100

 ● auto-aof-rewrite-min-size 64m
最少AOF文件需要达到64mb就会进行重写,重写了之后,当检测到当前的AOP文件增长幅度大于100%,也就是64mb,即当前的AOF文件大小为128mb的时候,就会自动触发对AOF进行重写操作。

rewrite的过程如下:

 1 redis fork一个子进程;

 2 子进程基于当前内存中的数据构建日志,开启往一个新的临时AOF文件中写入日志

 3 redis主进程,接收到client的写操作在内存中写入日志,同时新的日志也继续写入旧的AOF文件;

 4 子进程完成新的日志文件之后,redis主进程将内存中的新日志再次追加到新的AOF文件中;

 5 用新的日志文件替换旧的日志文件。

2.3. Redis 4.0 混合持久化

重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。 Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。AOF在重写(aof文件里可能有太多没用指令,所以aof会定期根据内存的最新数据生成aof文件)时将重写这一刻之前的内存rdb快照文件的内容和增量的 AOF修改内存数据的命令日志文件存在一起,都写入新的aof文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,原子的覆盖原有的AOF文件,完成新旧两个AOF文件的替换
AOF根据配置规则在后台自动重写,也可以人为执行命令bgrewriteaof重写AOF。 于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

开启混合持久化:
#aof-use-rdb-preamble yes
混合持久化aof文件结构
全网最详细Redis高阶从入门到精通_第21张图片

3. Redis集群方案比较

哨兵模式
全网最详细Redis高阶从入门到精通_第22张图片
  在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态,如果master节点异常,则会做主从切换,将某一台slave作为master,哨兵的配置略微复杂,并且性能和高可用性等各方面表现一般,特别是在主从切换的瞬间存在访问瞬断的情况,而且哨兵模式只有一个主节点对外提供服务,没法支持很高的并发,且单个主节点内存也不宜设置得过大,否则会导致持久化文件过大,影响数据恢复或主从同步的效率。

高可用集群模式
全网最详细Redis高阶从入门到精通_第23张图片
  redis集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。

4. Redis高可用集群搭建

1、redis安装
安装步骤:
 ● 安装gcc(由于redis底层用C语言写的,因此要安装C的编译环境)
    yum install gcc
 ● 把下载好的redis-5.0.2.tar.gz放在/usr/local文件夹下,并解压
    wget http://download.redis.io/releases/redis-5.0.2.tar.gz
    tar xzf redis-5.0.2.tar.gz
 ● 进入到解压好的redis-5.0.2目录下,进行编译与安装
    cd redis-5.0.2
    make & make install
 ● 启动并指定配置文件
    src/redis-server redis.conf(注意要使用后台启动,所以修改redis.conf里的daemonize改为yes)
 ● 验证启动是否成功
    ps -ef | grep redis
 ● 常用命令
  #进入redis客户端
   /usr/local/redis/bin/redis-cli
  #退出客户端
   quit
  #退出redis服务:
  (1)pkill redis-server
  (2)kill 进程号
  (3)src/redis-cli shutdown

2.redis集群搭建
  redis集群需要至少要三个master节点,我们这里搭建三个master节点,并且给每个master再搭建一个slave节点,总共6个redis节点,这里用三台机器部署6个redis实例,每台机器一主一从,搭建集群的步骤如下:
  第一步:在第一台机器的/usr/local下创建文件夹redis-cluster,然后在其下面分别创建2个文件夾如下
    (1)mkdir -p /usr/local/redis-cluster
    (2)mkdir 8001、 mkdir 8004

  第二步:把之前的redis.conf配置文件copy到8001下,修改如下内容:
    (1)daemonize yes
    (2)port 8001(分别对每个机器的端口号进行设置)
    (3)dir /usr/local/redis-cluster/8001/(指定数据文件存放位置,必须要指定不同的目录位置,不然会丢失数据)
    (4)cluster-enabled yes(启动集群模式)
    (5)cluster-config-file nodes-8001.conf(集群节点信息文件,这里800x最好和port对应上)
    (6)cluster-node-timeout 5000
    (7) # bind 127.0.0.1(去掉bind绑定访问ip信息)
    (8) protected-mode no (关闭保护模式)
    (9)appendonly yes
    如果要设置密码需要增加如下配置:
    (10)requirepass kun(设置redis访问密码)
    (11)masterauth kun (设置集群节点间访问密码,跟上面一致)

  第三步:把修改后的配置文件,copy到8002,修改第2、3、5项里的端口号,可以用批量替换:
:%s/源字符串/目的字符串/g

  第四步:另外两台机器也需要做上面几步操作,第二台机器用8002和8005,第三台机器用8003和8006

  第五步:分别启动6个redis实例,然后检查是否启动成功
    (1)/usr/local/redis-5.0.2/src/redis-server /usr/local/rediscluster/8001/redis.conf
    (2)ps -ef | grep redis 查看是否启动成功

  第六步:用redis-cli创建整个redis集群(redis5以前的版本集群是依靠ruby脚本redis-trib.rb实现)
    (1)/usr/local/redis-5.0.2/src/redis-cli -a kun --cluster create --cluster-replicas 1 192.168.61.xx1:8001 192.168.61.xx0:8002 192.168.61.xx1:8003 192.168.61.xx2:8004 192.168.61.xx0:8005 192.168.61.xx1:8006 ( --cluster-replicas 1 代表为每个创建的主服务器节点创建一个从服务器节点)
  第七步:验证集群
    (1)连接任意一个客户端即可:./redis-cli -c -h -p (-a访问服务端密码,-c表示集群模式,指定ip地址和端口号)如:/usr/local/redis-5.0.2/src/redis-cli -a kun -c -h 192.168.61.xx0 -p 8002
    (2)进行验证: cluster info(查看集群信息)、cluster nodes(查看节点列表)
    (3)进行数据操作验证
    (4)关闭集群则需要逐个进行关闭,使用命令:
      /usr/local/redis/bin/redis-cli -a zhuge -c -h 192.168.0.60 -p 800* shutdown

5. Java操作Redis集群

5.1 引入Jedis的Maven依赖
全网最详细Redis高阶从入门到精通_第24张图片
5.2 Java编写访问redis集群的代码非常简单,如下所示:
全网最详细Redis高阶从入门到精通_第25张图片

6. Redis集群原理分析

  Redis Cluster 将所有数据划分为 16384 的 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。
  当 Redis Cluster 的客户端来连接集群时,它也会得到一份集群的槽位配置信息并将其缓存在客户端本地。这样当客户端要查找某个 key 时,可以直接定位到目标节点。同时因为槽位的信息可能会存在客户端与服务器不一致的情况,还需要纠正机制来实现槽位信息的校验调整。

1. 槽位定位算法
  Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位:
  公式:HASH_SLOT = CRC16(key) mod 16384

2. 跳转重定位
  当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射表。
在这里插入图片描述
3. 网络抖动
  真实世界的机房网络往往并不是风平浪静的,它们经常会发生各种各样的小问题。比如网络抖动就是非常常见的一种现象,突然之间部分连接变得不可访问,然后很快又恢复正常。
  为解决这种问题,Redis Cluster 提供了一种选项cluster-node-timeout,表示当某个节点持续 timeout 的时间失联时,才可以认定该节点出现故障,需要进行主从切换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。

7. Redis水平扩展

Redis3.0以后的版本虽然有了集群功能,提供了比之前版本的哨兵模式更高的性能与可用性,但是集群的水平扩展却比较麻烦,今天就来带大家看看redis高可用集群如何做水平扩展,原始集群(见下图)由6个节点组成,6个节点分布在三台机器上,采用三主三从的模式
全网最详细Redis高阶从入门到精通_第26张图片
1、启动集群

#启动整个集群
/usr/local/redis-5.0.2/src/redis-server /usr/local/redis-cluster/8001/redis.conf
/usr/local/redis-5.0.2/src/redis-server /usr/local/redis-cluster/8002/redis.conf
/usr/local/redis-5.0.2/src/redis-server /usr/local/redis-cluster/8003/redis.conf
/usr/local/redis-5.0.2/src/redis-server /usr/local/redis-cluster/8004/redis.conf
/usr/local/redis-5.0.2/src/redis-server /usr/local/redis-cluster/8005/redis.conf
/usr/local/redis-5.0.2/src/redis-server /usr/local/redis-cluster/8006/redis.conf
#客户端连接8001端口的redis实例
/usr/local/redis-5.0.2/src/redis-cli -a kun-c -h 192.168.61.xx0 -p 8001
查看集群状态
192.168.0.61:8001> cluster nodes
全网最详细Redis高阶从入门到精通_第27张图片
从上图可以看出,整个集群运行正常,三个master节点和三个slave节点,8001端口的实例节点存储0-5460这些hash槽,8002端口的实例节点存储5461-10922这些hash槽,8003端口的实例节点存储10923-16383这些hash槽,这三个master节点存储的所有hash槽组成redis集群的存储槽位,slave点是每个主节点的备份从节点,不显示存储槽位 。

2、集群操作
我们在原始集群基础上再增加一主(8007)一从(8008),增加节点后的集群参见下图,新增节点用虚线框表示

全网最详细Redis高阶从入门到精通_第28张图片
增加redis实例

#在/usr/local/redis-cluster下创建8007和8008文件夹,并拷贝8001文件夹下的redis.conf文件到8007和8008这两个文件夹下
mkdir 8007
mkdir 8008
cd 8001
cp redis.conf /usr/local/redis-cluster/8007/
cp redis.conf /usr/local/redis-cluster/8008/
#修改8007文件夹下的redis.conf配置文件
vim /usr/local/redis-cluster/8007/redis.conf

#修改如下内容:
port:8007
dir /usr/local/redis-cluster/8007/
cluster-config-file nodes-8007.conf
#修改8008文件夹下的redis.conf配置文件
vim /usr/local/redis-cluster/8008/redis.conf

修改内容如下:
port:8008
dir /usr/local/redis-cluster/8008/
cluster-config-file nodes8008.conf

#启动8007和8008俩个服务并查看服务状态
/usr/local/redis-5.0.2/src/redis-server /usr/local/redis-cluster/8007/redis.conf
/usr/local/redis-5.0.2/src/redis-server /usr/local/redis-cluster/8008/redis.conf
ps -el | grep redis

查看redis集群的命令帮助
cd /usr/local/redis-5.0.2
src/redis-cli --cluster help
全网最详细Redis高阶从入门到精通_第29张图片全网最详细Redis高阶从入门到精通_第30张图片
配置8007为集群主节点

#使用add-node命令新增一个主节点8007(master)

前一个为新增节点,后一个为已知存在节点,看到日志最后有"[OK] New node added correctly"提示代表新节点加入成功
/usr/local/redis-5.0.2/src/redis-cli --cluster add-node 192.168.0.64:8007 192.168.0.61:8001

#查看集群状态
全网最详细Redis高阶从入门到精通_第31张图片
注意:当添加节点成功以后,新增的节点不会有任何数据,因为它还没有分配任何的slot(hash槽),我们需要为新节点手工分配hash槽
全网最详细Redis高阶从入门到精通_第32张图片
#查看下最新的集群状态
全网最详细Redis高阶从入门到精通_第33张图片
在这里插入图片描述
全网最详细Redis高阶从入门到精通_第34张图片
全网最详细Redis高阶从入门到精通_第35张图片
全网最详细Redis高阶从入门到精通_第36张图片
全网最详细Redis高阶从入门到精通_第37张图片
全网最详细Redis高阶从入门到精通_第38张图片

8、项目中使用Redis缓存

不是所有的数据都适合放到缓存中,项目中一般把经常需要查询不需要经常修改的且不大重要的数据放到缓存中。一般一个网站的首页访问量最大,应该对其内容进行缓存。
(1)引入Redis相关依赖:

    <!-- redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>

在application.yml中配置redis:

  redis:
    host: 192.168.159.131
    port: 6379
    database: 0
    timeout: 80000ms
    lettuce:
      pool:
        max-active: 20
        max-wait: -1ms
        max-idle: 5
        min-idle: 0

在项目基础框架中写个RedisConfig配置类:
全网最详细Redis高阶从入门到精通_第39张图片

@Configuration
@EnableCaching  //开启缓存
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

项目中使用@Cacheable注解将数据存入缓存:
一般有

@Service
public class CrmBannerServiceImpl extends ServiceImpl<CrmBannerMapper, CrmBanner> implements CrmBannerService {

    @Override
    @Cacheable(value = "banner", key = "'indexBanner'")
    public List<CrmBanner> getAllBanner() {
        /*前台首页面轮播图排序*/
        QueryWrapper<CrmBanner> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("id");
        wrapper.last("limit 2");

        List<CrmBanner> crmBannerList = this.baseMapper.selectList(new LambdaQueryWrapper<CrmBanner>().orderByDesc(CrmBanner::getId).last("limit 2"));
        return crmBannerList;
    }
}

存入缓存后可以发现,key的名称为"value::key"的格式,其中value为缓存器的名称,用来指定哪一个缓存器,key一般使用方法的参数,这样value::key的组合就可以用来唯一标识存在redis中数据的key。
全网最详细Redis高阶从入门到精通_第40张图片
全网最详细Redis高阶从入门到精通_第41张图片
全网最详细Redis高阶从入门到精通_第42张图片

有关Spring Boot缓存注解@Cacheable、@CacheEvict、@CachePut使用,详见:https://blog.csdn.net/dreamhai/article/details/80642010


欢迎关注公众号Java技术大联盟,会不定期分享BAT面试资料等福利。

在这里插入图片描述


你可能感兴趣的:(Redis,redis,分布式,数据库)