Linux环境安装Redis高可用及配置主从复制、哨兵模式、分布式集群模式

导读:本篇文章讲解 Linux环境安装Redis高可用及配置主从复制、哨兵模式、分布式集群模式,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

认识

在过去的几年中,NoSQL数据库一度成为高并发、海量数据存储解决方案的代名词,与之相应的产品也呈现出雨后春笋般的生机。然而在众多产品中能够脱颖而出的却屈指可数,如Redis、MongoDB、Memcached和CouchDB等等。下面我们主要讲解Redis。
Redis是一个开源免费的、使用C语言编写、支持网络交互的、可持久化存储、适应高并发、Key-Value分布式内存数据库,并提供多种语言的API,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器。

优势:

  • 性能极高: Redis读的速度是 110000 次/s,,写的速度是 81000 次/s。
  • 丰富的数据类型:Redis支持二进制案例的String,、Hash、List、 Set、ZSet数据类型操作。
  • 原子性:Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行,单个操作是原子性的。多个操作也支持事务,即原子性,通过 MULTI 和 EXEC 指令包起来。
  • 其他特性:Redis还支持发布/订阅通知、Key过期、事务、持久化等特性。

为什么说Redis中的String是二进制安全的?因为Redis是C语言编写,通过二进制传输,中间不涉及编解码。
二进制安全的特点:

  • 编码/解码发生在客户端,执行效率高
  • 不需要频繁的编码解码,不会出现乱码

一、安装

(1)先安装编译Redis需要的依赖,Redis是由C语言编写的,它的运行需要C环境,因此我们需要先安装gcc,命令如下:
yum install gcc-c++

(2)上传官网下载好的Redis压缩包到服务器上,进行解压,解压后进入Redis根目录,如下图:
Redis官网地址:https://redis.io/download
cd /usr/local/
tar -zxvf redis-6.2.3.tar.gz
cd redis-6.2.3

在这里插入图片描述

(3)进入解压好的Redis根目录后,执行make编译及安装并指定安装后的bin目录为Redis根目录命令,安装好后目录结构如下图:
make & make install PREFIX=/usr/local/redis-6.2.3

在这里插入图片描述

根据上面安装步骤,分别上传到相应的服务器进行安装,安装好后,按照如下配置各自所需要的模式即可

二、主从复制

主从复制是指将一台 Redis 服务器的数据(主节点Master),复制到其它的 Redis 服务器(从节点Slave),数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台 Redis 服务器都是主节点,且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
缺点:
1.不具备自动容错与恢复功能,master或slave的宕机都可能导致客户端请求失败,需要等待机器重启或手动切换客户端IP才能恢复。
2.master宕机,如果宕机前数据没有同步完,则切换IP后会存在数据不一致的问题。
3.难以支持在线扩容,Redis的容量受限于单机配置。
4.每台redis服务器都存储相同的数据,很浪费内存。

作用:

  • 数据冗余: 主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  • 故障恢复: 当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余。
  • 负载均衡: 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点)
    ,分担服务器负载。尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 高可用基石: 除了上述作用以外,主从复制还是哨兵模式和集群模式能够实施的基础。

我这里使用VMware虚拟机搭建Centos7进行配置,分别在2台服务器上传Redis压缩包后解压,配置步骤以及项目中如何连接,如下几点,我这里配置一主一从,可以配置一主多从

1.192.168.80.130:7001 配置主机 master

(1)进入解压后的Redis根目录,复制redis.conf并改名,改名后编辑文件redis_7001.conf,配置参数如下:
cd /usr/local/redis-6.2.3
cp redis.conf redis_7001.conf
vim redis_7001.conf

# 1.默认127.0.0.1,主机需要监听多个ip,改成bind 0.0.0.0,或者改为多个ip使用空格分割如:bind 192.168.80.131 192.168.80.132
# 这里改为bind 0.0.0.0,使得Redis服务器可以跨网络访问
bind 0.0.0.0

# 2.指定Redis监听端口,默认端口为6379
port 7001
# 3.客户端空闲N秒后关闭连接,默认是0,0表示禁用
timeout 0
# 4.当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,我们可以通过pidfile指定文件位置或文件名
pidfile /usr/local/redis-6.2.3/data/redis_7001.pid
# 5.设置日志文件输出路径,默认输出到/dev/null/redis.log
logfile "/usr/local/redis-6.2.3/data/redis_7001.log"
# 6.指定本地数据库文件名,默认值为dump.rdb,Redis 支持两种形式的持久化,一种是RDB快照(snapshotting)(默认开启RDB),另外一种是AOF(append-only-file)
dbfilename dump_7001.rdb
# 7.指定本地数据库存放目录,默认是./,表示Redis根目录,没有data目录需要自己创建data目录
dir /usr/local/redis-6.2.3/data/
# 8.设置Redis连接密码,默认无密码。
# 8-1.建议密码设置复杂且长度长一些,因为redis的查询速度是非常快的,外部用户一秒内可以尝试多达150K个密码,所以密码要尽量长。
requirepass www.baidu.com@123

(2)先启动主机服务,进入Redis根目录的bin目录,启动的时候需要指定启动的配置文件,如果不指定,默认使用根目录的redis.conf文件启动,启动命令如下:
cd /usr/local/redis-6.2.3/bin
./redis-server ../redis_7001.conf
(3)查看服务是否启动,使用命令如下表示启动成功,效果如下图:
ps -ef|grep redis

在这里插入图片描述

2.192.168.80.131:7002 配置从机 slave

(1)从机的配置与主机一致,端口不要有冲突,不同点在如下代码的红色字体部分,从机的Redis更改为如下代码红色字体部分即可:
#1.更改redis.conf配置文件为redis_7002.conf
cd /usr/local/redis-6.2.3
#2.编辑redis_7002.conf文件
port 7002
pidfile /usr/local/redis-6.2.3/data/redis_7002.pid
logfile "/usr/local/redis-6.2.3/data/redis_7002.log"
dbfilename dump_7002.rdb

#3.从机需要在redis_7002.conf最后增加如下配置
#从机设置只读模式,默认是只读
slave-read-only yes
#指定主机master节点的ip和端口,这是最重要的一步,增加这一步才能主从同步
slaveof 192.168.80.130 7001
#如果主机master配置了requiress登录密码,需要配置masterauth认证,就是配置连接主机的密码
masterauth www.baidu.com@123

# 1.默认127.0.0.1,修改成本机的IP地址或者0.0.0.0,要不然,客户端无法进行访问
# 这里改为bind 0.0.0.0,使得Redis服务器可以跨网络访问
bind 0.0.0.0

# 2.指定Redis监听端口,默认端口为6379
port 7002
# 3.设置deamon守护进程是否开启,就是后台以进程方式启动,默认是no
daemonize yes
# 4.当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,我们可以通过pidfile指定文件位置或文件名
pidfile /usr/local/redis-6.2.3/data/redis_7002.pid
# 5.设置日志文件输出路径,默认输出到/dev/null/redis.log
logfile "/usr/local/redis-6.2.3/data/redis_7002.log"
# 6.指定本地数据库文件名,默认值为dump.rdb,Redis 支持两种形式的持久化,一种是RDB快照(snapshotting)(默认开启RDB),另外一种是AOF(append-only-file)
dbfilename dump_7002.rdb
# 7.指定本地数据库存放目录,默认是./,表示Redis根目录,没有data需要自己创建
dir /usr/local/redis-6.2.3/data/
# 8.设置Redis连接密码,默认无密码。
# 8-1.建议密码设置复杂且长度长一些,因为redis的查询速度是非常快的,外部用户一秒内可以尝试多达150K个密码,所以密码要尽量长。
requirepass www.baidu.com@123

#从机设置只读模式,默认是只读
slave-read-only yes
#指定主机master节点的ip和端口,这是最重要得一步,增加这一步才能主从同步
slaveof 192.168.80.130 7001
#如果主机master配置了requiress登录密码,需要配置masterauth认证,就是配置连接主机的密码
masterauth www.baidu.com@123

(2)启动服务,启动流程与主机一致:
cd /usr/local/redis-6.2.3/bin
./redis-server ../redis_7002.conf
ps -ef|grep redis

3.验证及宕机切换主机

(1)主机Master,进入redis的bin目录,使用如下命令连接redis,并往Redis中set值,如下图:
#在主机服务器上连接主机,-h连接地址,-p端口,-a密码
./redis-cli -h 127.0.0.1 -p 7001 -a www.baidu.com@123

在这里插入图片描述

(2)从机Slave,只需要查看从机中Redis有没有在主机中set的值,有的话,表示主从复制配置已经成功,如下图:

#在从机服务器上连接从机,-h连接地址,-p端口,-a密码
./redis-cli -h 127.0.0.1 -p 7002 -a www.baidu.com@123

在这里插入图片描述

(3)主机故障宕机处理
  当主节点出现宕机时,这时候最简单的方式可以使用主从手动切换的方式,手动的将一台从节点切换成主节点,所以我们需要人工干预手动设置,最关键在手动切换的过程中会造成Redis服务不可用。所以说主从手动切换的方案不是一个合适的主从切换方案。
#当主节点出现宕机,这时候我们需要手动将从节点临时设置成主节点。代码如下,选择一个方式即可:
#linux上直接执行命令1
redis-cli -h 192.168.80.131 -p 7002 -a www.baidu.com@123 slaveof no one
#命令2,登录redis客户端,进入redis,后执行命令即可
./redis-cli -h 127.0.0.1 -p 7002 -a www.baidu.com@123
slaveof no one

当主节点启动redis后,从节点也重启下redis,主节点会自动还原,但是会丢失临时担任主机这段时间的数据。
毕竟主从手动切换方案是存在问题的不是很适用,所以一般主从切换会采用哨兵模式

三、哨兵模式

Sentinel(哨兵)是用于监控redis集群中Master状态的工具,是Redis 的高可用解决方案,sentinel哨兵模式已经被集成在redis2.4之后的版本中。
sentinel可以监视一个或者多个redis的master服务,以及这些master服务的所有从服务;当某个master服务下线时,自动将该master下的某个从服务升级为master服务替代已下线的master服务继续处理请求。

哨兵工作原理:

  • 哨兵(sentinel)是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel)进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。
  • 每个哨兵会向其它哨兵、master、slave定时发送消息,以确认对方是否“活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的“主观认为宕机”)。
  • 若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master”彻底死亡”(即:客观上的真正down机),通过一定的vote算法,从剩下的slave节点中选一台提升为master,然后自动修改相关配置。
  • 虽然哨兵(sentinel)为一个单独的可执行文件redis-sentinel,但实际上它只是一个运行在特殊模式下的Redis服务器,你可以在启动一个普通Redis服务器时通过给定 –sentinel 选项来启动哨兵(sentinel)。

sentinel可以让redis实现主从复制,当一个集群中的master失效之后,sentinel可以选举出一个新的master用于自动接替master的工作,集群中的其他redis服务器自动指向新的master同步数据。一般建议sentinel采取奇数台,防止某一台sentinel无法连接到master导致误切换
sentinel(哨兵)作用:

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

这里配置3个哨兵1主2从的Redis服务器来演示这个过程。机器的分配,如下表格所示,相应配置如下1、2、3点:

服务类型 是否主服务器 IP地址 端口
Redis 192.168.80.132 7003
Redis 192.168.80.133 7004
Redis 192.168.80.134 7005
Sentinel 192.168.80.132 27003
Sentinel 192.168.80.133 27004
Sentinel 192.168.80.134 27005

1.192.168.80.132:7003/27003 配置主机

这里把配置文件及服务单独复制到新建的文件夹进行修改,方便查日志及管理,把相应的文件复制到新文件夹,如下图:

在这里插入图片描述

(1)编辑redis_7003.conf文件,和上面的主从模式配置差不多,端口和文件名称不要一样,如下代码:

# 1.使得Redis服务器可以跨网络访问
bind 0.0.0.0

# 3.指定Redis监听端口,默认端口为6379
port 7003
# 4.设置deamon守护进程是否开启,就是后台以进程方式启动,默认是no
daemonize yes
# 5.当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,我们可以通过pidfile指定文件位置或文件名
pidfile /usr/local/redis/sentinel/redis_7003.pid
# 6.设置日志文件输出路径,默认输出到/dev/null/redis.log
logfile "/usr/local/redis/sentinel/data/redis_7003.log"
# 7.指定本地数据库文件名,默认值为dump.rdb,Redis 支持两种形式的持久化,一种是RDB快照(snapshotting)(默认开启RDB),另外一种是AOF(append-only-file)
dbfilename dump_7003.rdb
# 8.指定本地数据库存放目录,默认是./,表示Redis根目录
dir /usr/local/redis/sentinel/
# 9.设置Redis连接密码,默认无密码。
# 9-1.建议密码设置复杂且长度长一些,因为redis的查询速度是非常快的,外部用户一秒内可以尝试多达150K个密码,所以密码要尽量长。
requirepass "admin@123"

(2)启动主机Redis服务,启动后该目录会自动生成日志文件,启动命令及验证如下图:

在这里插入图片描述

2.192.168.80.133:7004/27004 配置从机

(1)这里把配置文件及服务单独复制到新建的文件夹进行修改,方便查日志,把相应的文件复制到新文件夹,如下图:

在这里插入图片描述

编辑redis_7004.conf,配置与主机一样,端口和文件名称不要一样,区别在于如下代码:
#从机设置只读模式,默认是只读
slave-read-only yes
##指定主机master节点的ip和端口,这是最重要得一步
slaveof 192.168.217.132 7003
#主机服务器密码,注:如果主机有配置密码,这里添加上主机的密码
masterauth admin@123

# 1.使得Redis服务器可以跨网络访问
bind 0.0.0.0

# 3.指定Redis监听端口,默认端口为6379
port 7004
# 4.设置deamon守护进程是否开启,就是后台以进程方式启动,默认是no
daemonize yes
# 5.当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,我们可以通过pidfile指定文件位置或文件名
pidfile /usr/local/redis/sentinel/redis_7004.pid
# 6.设置日志文件输出路径,默认输出到/dev/null/redis.log
logfile "/usr/local/redis/sentinel/data/redis_7004.log"
# 7.指定本地数据库文件名,默认值为dump.rdb,Redis 支持两种形式的持久化,一种是RDB快照(snapshotting)(默认开启RDB),另外一种是AOF(append-only-file)
dbfilename dump_7004.rdb
# 8.指定本地数据库存放目录,默认是./,表示Redis根目录
dir /usr/local/redis/sentinel/
# 9.设置Redis连接密码,默认无密码。
# 9-1.建议密码设置复杂且长度长一些,因为redis的查询速度是非常快的,外部用户一秒内可以尝试多达150K个密码,所以密码要尽量长。
requirepass "admin@123"

#从机设置只读模式,默认是只读
slave-read-only yes
##指定主机master节点的ip和端口,这是最重要得一步
slaveof 192.168.217.132 7003
#主机服务器密码,注:如果主机有配置密码,这里添加上主机的密码
masterauth "admin@123"

(2)启动从机Redis服务,启动后该目录会自动生成日志文件,启动命令及验证如下图:

在这里插入图片描述

3.192.168.80.134:7005/27005 配置从机

这里把配置文件及服务单独复制到新建的文件夹进行修改,方便查日志,把相应的文件复制到新文件夹,如下图:

在这里插入图片描述

编辑redis_7005.conf,配置与主机一样,端口和文件名称不要一样,记得更改,区别在于如下代码:
#从机设置只读模式,默认是只读
slave-read-only yes
##指定主机master节点的ip和端口,这是最重要得一步
slaveof 192.168.217.132 7003
#主机服务器密码,注:如果主机有配置密码,这里添加上主机的密码
masterauth admin@123

# 1.使得Redis服务器可以跨网络访问
bind 0.0.0.0

# 3.指定Redis监听端口,默认端口为6379
port 7005
# 4.设置deamon守护进程是否开启,就是后台以进程方式启动,默认是no
daemonize yes
# 5.当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,我们可以通过pidfile指定文件位置或文件名
pidfile /usr/local/redis/sentinel/redis_7005.pid
# 6.设置日志文件输出路径,默认输出到/dev/null/redis.log
logfile "/usr/local/redis/sentinel/data/redis_7005.log"
# 7.指定本地数据库文件名,默认值为dump.rdb,Redis 支持两种形式的持久化,一种是RDB快照(snapshotting)(默认开启EDB),另外一种是AOF(append-only-file)
dbfilename dump_7005.rdb
# 8.指定本地数据库存放目录,默认是./,表示Redis根目录
dir /usr/local/redis/sentinel/
# 9.设置Redis连接密码,默认无密码。
# 9-1.建议密码设置复杂且长度长一些,因为redis的查询速度是非常快的,外部用户一秒内可以尝试多达150K个密码,所以密码要尽量长。
requirepass admin@123

#从机设置只读模式,默认是只读
slave-read-only yes
##指定主机master节点的ip和端口,这是最重要的一步
slaveof 192.168.217.132 7003
#主机服务器密码,注:如果主机有配置密码,这里添加上主机的密码
masterauth "admin@123"

(2)启动从机Redis服务,启动后该目录会自动生成日志文件,启动命令及验证如下图:

在这里插入图片描述

4.哨兵配置

(1)哨兵配置都是一样的,都需要指向Redis主机(master)服务,只要配置好一个哨兵,把哨兵配置文件复制到其他服务器即可,前提是服务器之间网络要互通:
在192.168.217.132上编辑sentinel_27003.conf文件,该文件为哨兵模式文件,如下代码:

# 1.哨兵监控端口配置,默认26379
port 27003
# 2.保护模式,默认是yes,表示不允许通过外界网络连接使用redis,如果需要通过网络访问redis,将该项改为no
protected-mode no
# 3.设置deamon守护进程是否开启,就是后台以进程方式启动,默认是no
daemonize yes
# 4.默认会把pid写入/var/run/redis-sentinel.pid,我们可以通过pidfile指定文件位置或文件名,便于管理
pidfile /usr/local/redis/sentinel/redis-sentinel_27003.pid
# 5.设置日志文件输出路径,默认输出到/dev/null/redis.log
logfile "/usr/local/redis/sentinel/redis-sentinel_27003.log"
# 6.默认本都数据文件存放路径是./tmp,我们这里指定本地数据库存放目录,便于管理
dir /usr/local/redis/sentinel/

# 7.配置监听的主服务器地址,
# sentinel monitor代表监控
# mymaster代表服务器名称,可以自定义
# 192.168.217.132 代表监控的主服务器,7003:主服务器的redis端口
# 2代表只有两个或者两个以上的烧饼认为主服务器不可用的时候,才会做故障切换操作
sentinel monitor mymaster  192.168.217.132 7003 2
# 8.配置哨兵服务链接主机的密码,sentinel auth-pass 定义服务的密码
# mymaster服务名称
# admin@123 服务密码
sentinel auth-pass mymaster admin@123

# 以下信息为默认即可
# 哨兵与主机(master)的心跳时间(毫秒),默认30秒。
sentinel down-after-milliseconds mymaster 30000
# #故障转移时,最多可以有多少个slave同时对新的master进行数据同步,该值越小,完成故障转移的时间越长,但可用slave数量越多,该值越大,越多slave因为replication而不可用。建议设置为1。
sentinel parallel-syncs mymaster 1
# 故障转移超时时间(毫秒),默认180秒。
sentinel failover-timeout mymaster 180000

把上面的哨兵配置sentinel_27003.conf文件复制到服务器,更改相应端口,确保端口不冲突即可。
1.复制到192.168.217.133后改名为sentinel_27004.conf,并把文件内所有27003端口改为27004
2.复制到192.168.217.134后改名为sentinel_27005.conf,并把文件内所有27003端口改为27005

(2)启动哨兵,注:要确保redis服务主机从机都启动完成后,最后启动哨兵,分别进入相应服务器启动哨兵,如下命令启动哨兵:
#进入哨兵配置文件所在目录
cd /usr/local/redis/sentinel/
#启动哨兵
./redis-sentinel sentinel_27003.conf

在这里插入图片描述

5.启动验证及宕机自动切换Redis主机

(1)根据上面1、2、3、4点都已经配置好并且启动完毕后,先验证redis主从,随便进入一台机器验证,我这里进入主机服务:192.168.217.132,如下图命令:
cd /usr/local/redis/sentinel
#命令行链接redis,-h连接地址,-p端口,-a密码
./redis-cli -h 192.168.217.132 -p 7003 -a admin@123
#输入命令,回车出现如下图所示
info

在这里插入图片描述

(2)验证哨兵模式,随便进入一台服务器验证,我这里进入主机:192.168.217.132,如下图查看哨兵链接信息命令:
#进入哨兵模式端口
./redis-cli -h 192.168.217.132 -p 27003

在这里插入图片描述

(3)查看哨兵模式的监控信息,随便进入一台从机,这里进入192.168.217.133,如下图查看:
#进入Redis从机服务
./redis-cli -h 192.168.217.133 -p 7004 -a admin@123
#输入如下命令后,会出现如下图所示表示哨兵正在监控主机
monitor

在这里插入图片描述

(4)查看主机宕机哨兵是否自动切换,这里测试把主机Redis服务192.168.217.132:7003杀掉(kill),等待设置的故障转移时间180秒,然后再次重启原来的主机,再次按照 5(1)步骤进入如下命令查看,发现主机由原来的192.168.217.132:7003,变为192.168.217.133:7004,表示哨兵模式选举自动切换主机成功:

在这里插入图片描述
在这里插入图片描述

四、集群模式

Redis 的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的内容。

为什么需要集群?
  1.并发量
  通常来说,单台Redis能够执行11万/秒的命令,这个并发基本上能够满足我们所有需求了,但有时候比如做离线计算,为了更快的得出结果,有时候我们希望超过这个并发,那这个时候单机就不满足我们需求了,就需要集群了。
  2.数据量
  单台服务器的内存大概在16G-256G之间,前面我们说Redis数据量都是存在内存中的,那如果实际业务要保存在Redis的数据量超过了单台机器的内存,这个时候最简单的方法是增加服务器内存,但是单台服务器内存不可能无限制的增加,纵向扩展不了了,便想到如何进行横向扩展,这时候我们就会想将这些业务数据分散存储在多台Redis服务器中,但是要保证多台Redis服务器能够无障碍的进行内存数据沟通,这也就是Redis集群。

(1)简介

  • 在集群模式推出之前,主从模式的可用性要靠 Sentinel 保证,集群模式引入了新的故障检测机制,而在故障转移这块复用了 Sentinel 的代码逻辑,不需要单独启动一个 Sentinel 集群,redis Cluster本身就能自动进行 master 选举和 failover
  • redis集群采用p2p模式,也就是集群没有中心服务器,每个节点都是同样的权限,通过访问一个节点,就能获取其他节点的信。

(2)投票容错机制

  • 为了实现集群的高可用,即判断节点能否正常使用,redis-cluster有这么一个投票容错机制:如果集群中超过半数的节点投票认为某个节点挂了,那么这个节点就挂了(fail),这是判断节点是否挂了的方法。如果集群中任意一个节点挂了,而且该节点没有从节点(备份节点),那么这个集群就挂了。这是判断集群是否挂了的方法。

(3)特点

  • 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  • 节点的fail(宕机)是通过集群中超过半数的节点检测失效时才生效。
  • 客户端与redis节点直连,不需要中间proxy层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  • redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护node<->slot<->value。
  • Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key)%16384 的值,决定将一个key放到哪个桶中。

(4)集群分片策略

  • Redis-cluster分片策略,是用来解决key存储位置的。
    集群将整个数据库分为16384个槽位slot,所有key-value数据都存储在这些
  • slot中的某一个上。一个slot槽位可以存放多个数据,key的槽位计算公式为:slot_number=crc16(key)%16384,其中crc16为16位的循环冗余校验和函数。
  • 集群中的每个主节点都可以处理0个至16383个槽,当16384个槽都有某个节点在负责处理时,集群进入上线状态,并开始处理客户端发送的数据命令请求。

(5)哈希槽
  redis集群规定了16384个槽位,这些槽位将会平均分配给不同的节点。
  通过对key的取模16384,可获得一个槽位数,这个key将会保存到这个槽位上,然后再根据槽位对应的节点,将数据保存到指定节点中。
例如

  • set(‘a’,‘百度一下’)
  • 对a字符串做crc16算法,再对其取模16384,获得槽位数:15495
  • 再根据槽位数,获得指定节点(假设是C节点)
  • 连接C节点,进行set(‘a’,‘百度一下’)命令发送
  • 数据保存到C节点
  • get(‘a’)
  • 对a字符串做crc16算法,再对其取模16384,获得槽位数:15495
  • 再根据槽位数,获得指定节点(假设是C节点)
  • 连接C节点,进行get(‘a’)命令发送
  • 获得具体数据

(6)集群TCP端口

  • 每个Redis集群中的节点都需要打开两个TCP连接。一个连接用于正常的给Client提供服务,比如6379,还有一个额外的端口(通过在这个端口号上加10000)作为数据端口,比如16379
  • 第二个端口(本例中就是16379)用于集群总线,这是一个用二进制协议的点对点通信信道。这个集群总线(Cluster bus)用于节点的失败侦测、配置更新、故障转移授权,等等。
  • 客户端从来都不应该尝试和这些集群总线端口通信,它们只应该和正常的Redis命令端口进行通信。注意,确保在你的防火墙中开放着两个端口,否则,Redis集群节点之间将无法通信。

集群节点: 最少需要3个主节点,如果需要实现更加稳定,还需要3个从节点(避免主节点断线后集群终止)所以redis需要启动6个实例

(7)我们这里使用3台虚拟机,并且我要起一个三主三从cluster的Redis集群,所以我每台虚机上还是要起两个实例,但是同一个主从的不能都放到一台机器上,按照企业级的灾备方案去考量,为了分散主从,我们的主从对应关系如下,集群相关配置如下几点:

服务类型 是否主服务器 IP地址 端口
Redis 192.168.217.135 8001
Redis 192.168.217.135 8002
Redis 192.168.217.136 8003
Redis 192.168.217.136 8004
Redis 192.168.217.137 8005
Redis 192.168.217.137 8006

1.192.168.217.135:8001/8002 配置

(1)这里把相关配置及文件复制到新建文件夹进行配置,方便查看日志及管理,如下图:

在这里插入图片描述

(2)编辑redis_8001.conf配置文件,如下代码:

# 使得Redis服务器可以跨网络访问
bind 0.0.0.0
# 保护模式,默认是yes,表示不允许通过外界网络连接使用redis,如果需要通过网络访问redis,将该项改为no
protected-mode no
# 指定Redis监听端口,默认端口为6379
port 8001
# 设置deamon守护进程是否开启,就是后台以进程方式启动,默认是no
daemonize yes
# 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,我们可以通过pidfile指定文件位置或文件名
pidfile /usr/local/redis/cluster/redis_8001.pid
# 设置日志文件输出路径,默认输出到/dev/null/redis.log
logfile "/usr/local/redis/cluster/data/redis_8001.log"
# 7.指定本地数据库文件名,默认值为dump.rdb,Redis支持两种形式的持久化,一种是RDB快照(snapshotting)(默认开启RDB),另外一种是AOF(append-only-file)
dbfilename dump_8001.rdb
# 指定本地数据库存放目录,默认是./,表示Redis根目录
dir /usr/local/redis/cluster/

## 集群相关配置,默认都注释掉了
# 配置redis做为一个集群节点来启动,也就是开启集群
cluster-enabled yes
# 每个集群节点都有一个集群配置文件。它是由Redis节点自动创建和更新的。每个Redis集群节点都需要一个不同的集群配置文件。
# 注:确保在同一系统中运行的实例没有重叠的集群配置文件名。集群的配置,配置文件首次启动自动生成。
cluster-config-file nodes-8001.conf
# 群集节点超时是指节点在失败状态下必须不可到达的毫秒数。大多数其他内部时间限制是节点超时的倍数。默认15秒
cluster-node-timeout 15000

# 设置Redis连接密码,默认无密码。
# 建议密码设置复杂且长度长一些,因为redis的查询速度是非常快的,外部用户一秒内可以尝试多达150K个密码,所以密码要尽量长。
requirepass "admin@123"

(3)8001配置完毕后,把redis_8001.conf复制为redis_8002.conf配置,配置8002端口,需要更改端口及一些配置,保证端口与文件名不冲突:
cd /usr/local/redis/cluster
cp redis_8001.conf redis_8002.conf
vi redis_8002.conf,修改如下信息即可
port 8002
pidfile /usr/local/redis/cluster/redis_8002.pid
logfile "/usr/local/redis/cluster/data/redis_8002.log"
dbfilename dump_8002.rdb
cluster-config-file nodes-8002.conf

在这里插入图片描述

2.192.168.217.136:8003/8004 配置

(1)配置8003端口,复制文件到新目录如下:
方式1,使用scp命令,把192.168.217.135上的cluster文件夹复制到192.168.217.136服务器上,确保该服务器上安装有redis,然后直接更改端口及文件名称即可,命令如下:
scp -r cluster/ root@192.168.217.136:/usr/local/redis/
在这里插入图片描述

方式2,如下:

在这里插入图片描述

(2)编辑redis_8003.conf文件,配置参数如下:

# 使得Redis服务器可以跨网络访问
bind 0.0.0.0
# 保护模式,默认是yes,表示不允许通过外界网络连接使用redis,如果需要通过网络访问redis,将该项改为no
protected-mode no
# 指定Redis监听端口,默认端口为6379
port 8003
# 设置deamon守护进程是否开启,就是后台以进程方式启动,默认是no
daemonize yes
# 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,我们可以通过pidfile指定文件位置或文件名
pidfile /usr/local/redis/cluster/redis_8003.pid
# 设置日志文件输出路径,默认输出到/dev/null/redis.log
logfile "/usr/local/redis/cluster/data/redis_8003.log"
# 7.指定本地数据库文件名,默认值为dump.rdb,Redis 支持两种形式的持久化,一种是RDB快照(snapshotting)(默认开启RDB),另外一种是AOF(append-only-file)
dbfilename dump_8003.rdb
# 指定本地数据库存放目录,默认是./,表示Redis根目录
dir /usr/local/redis/cluster/

## 集群相关配置,默认都注释掉了
# 配置redis做为一个集群节点来启动,也就是开启集群
cluster-enabled yes
# 每个集群节点都有一个集群配置文件。它是由Redis节点自动创建和更新的。每个Redis集群节点都需要一个不同的集群配置文件。
# 注:确保在同一系统中运行的实例没有重叠的集群配置文件名。集群的配置,配置文件首次启动自动生成。
cluster-config-file nodes-8003.conf
# 群集节点超时是指节点在失败状态下必须不可到达的毫秒数。大多数其他内部时间限制是节点超时的倍数。默认15秒
cluster-node-timeout 15000

# 设置Redis连接密码,默认无密码。
# 建议密码设置复杂且长度长一些,因为redis的查询速度是非常快的,外部用户一秒内可以尝试多达150K个密码,所以密码要尽量长。
requirepass "admin@123"

(3)8003配置完毕后,把redis_8003.conf复制为redis_8004.conf配置,配置8004端口,需要更改端口及一些配置,保证端口与文件名不冲突:

cd /usr/local/redis/cluster
cp redis_8003.conf redis_8004.conf
vi redis_8004.conf,修改如下信息即可
port 8004
pidfile /usr/local/redis/cluster/redis_8004.pid
logfile "/usr/local/redis/cluster/data/redis_8004.log"
dbfilename dump_8004.rdb
cluster-config-file nodes-8004.conf

在这里插入图片描述

3.192.168.217.137:8005/8006 配置

(1)配置8005端口,复制文件到新目录如下:
方式1,使用scp命令,把192.168.217.135上的cluster文件夹复制到192.168.217.137服务器上,确保该服务器上安装有redis,然后直接更改端口及文件名称即可,命令如下:
scp -r cluster/ root@192.168.217.137:/usr/local/redis/
在这里插入图片描述

(2)编辑redis_8005.conf文件,配置参数如下:

# 使得Redis服务器可以跨网络访问
bind 0.0.0.0
# 保护模式,默认是yes,表示不允许通过外界网络连接使用redis,如果需要通过网络访问redis,将该项改为no
protected-mode no
# 指定Redis监听端口,默认端口为6379
port 8005
# 设置deamon守护进程是否开启,就是后台以进程方式启动,默认是no
daemonize yes
# 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,我们可以通过pidfile指定文件位置或文件名
pidfile /usr/local/redis/cluster/redis_8005.pid
# 设置日志文件输出路径,默认输出到/dev/null/redis.log
logfile "/usr/local/redis/cluster/data/redis_8005.log"
# 7.指定本地数据库文件名,默认值为dump.rdb,Redis 支持两种形式的持久化,一种是RDB快照(snapshotting)(默认开启RDB),另外一种是AOF(append-only-file)
dbfilename dump_8005.rdb
# 指定本地数据库存放目录,默认是./,表示Redis根目录
dir /usr/local/redis/cluster/

## 集群相关配置,默认都注释掉了
# 配置redis做为一个集群节点来启动,也就是开启集群
cluster-enabled yes
# 每个集群节点都有一个集群配置文件。它是由Redis节点自动创建和更新的。每个Redis集群节点都需要一个不同的集群配置文件。
# 注:确保在同一系统中运行的实例没有重叠的集群配置文件名。集群的配置,配置文件首次启动自动生成。
cluster-config-file nodes-8005.conf
# 群集节点超时是指节点在失败状态下必须不可到达的毫秒数。大多数其他内部时间限制是节点超时的倍数。默认15秒
cluster-node-timeout 15000

# 设置Redis连接密码,默认无密码。
# 建议密码设置复杂且长度长一些,因为redis的查询速度是非常快的,外部用户一秒内可以尝试多达150K个密码,所以密码要尽量长。
requirepass "admin@123"

(3)8005配置完毕后,把redis_8005.conf复制为redis_8006.conf配置,配置8006端口,需要更改端口及一些配置,保证端口与文件名不冲突:
cd /usr/local/redis/cluster
cp redis_8005.conf redis_8006.conf
vi redis_8006.conf,修改如下信息即可
port 8006
pidfile /usr/local/redis/cluster/redis_8006.pid
logfile "/usr/local/redis/cluster/data/redis_8006.log"
dbfilename dump_8006.rdb
cluster-config-file nodes-8006.conf

4.启动redis服务

(1)以上配置都配置完毕后,进入我们创建的cluster配置文件目录,使用命令启动,我这里进入8001服务器,分别启动8001和8002:
可以看到如下图,表示成功启动8001与8002

在这里插入图片描述

(2)我们剩余的192.168.217.136:8003/8004、192.168.217.137:8005/8006启动命令也是如第(1)一样,分别进入相应服务器进行启动。

5.创建集群

(1)以前 redis版本<5 使用ruby工具(在redis根目录src目录中)创建集群,现在 redis版本>=5 使用的是redis-cli客户端创建集群。
随便在一台服务器上创建集群即可,我这里在192.168.217.135服务器上创建集群,下面的命令就是分别将3台服务器上的cluster联系起来,形成一个整体的集群系统
cd /usr/local/redis/cluster/


# redis-cli  --cluster  create (中间的ip+端口)代表前3个是主节点,后3个是从节点 --cluster-replicas 1
# replicas 1 表示我们希望为集群中的每个主节点创建一个从节点
# 如果需要所有节点都是主节点,如下命令去掉 --cluster-replicas 1 即可

./redis-cli --cluster create 192.168.217.135:8001 192.168.217.136:8003 192.168.217.137:8005   192.168.217.135:8002 192.168.217.136:8004 192.168.217.137:8006 --cluster-replicas 1

(2)当出现如下图所示表示成功创建集群:

在这里插入图片描述

(3)测试集群,存储数据,随便在一台服务器上存储数据,我这里在192.168.217.135:8001上存储数据,如下表示集群已经正常工作:
cd /usr/local/redis/cluster/

# -h ip地址,-c连接集群,-p端口号,-a连接密码
# 在8001上存储name,存到了8003服务器上,表示集群正常工作
./redis-cli -h 192.168.217.135 -c -p 8001 -a admin@123

Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.217.135:8001> set name 'zhangsan'
-> Redirected to slot [5798] located at 192.168.217.136:8003
OK
192.168.217.136:8003> set age 100
-> Redirected to slot [741] located at 192.168.217.135:8001
OK
192.168.217.135:8001> 

(4)这里进入8001的redis中执行命令查看集群状态,如下图:
#查看集群信息
cluster info
#查看集群主备分配是否正确
cluster nodes

在这里插入图片描述

五、常见错误

异常1

# 如果redis.log中出现如下错误
WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.

解决方案,编辑sysctl.conf文件,vim /etc/sysctl.conf,最后一行添加以代码即可解决:
net.core.somaxconn= 1024
#编辑完成后,执行如下命令使文件生效
sysctl -p

异常2

 # 如果Redis从机的redis.log日志中出现如下错误
WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.

解决方案,编辑sysctl.conf文件,vim /etc/sysctl.conf,最后一行添加以代码即可解决:
vm.overcommit_memory=1
#编辑完成后,执行如下命令使文件生效
sysctl -p

异常3

 # 如果Redis从机的redis.log日志中出现如下错误
1364:S 13 Jun 2021 16:31:23.693 # Error condition on socket for SYNC: No route to host
1364:S 13 Jun 2021 16:31:23.703 * Connecting to MASTER 192.168.217.132:7003
1364:S 13 Jun 2021 16:31:23.704 * MASTER <-> REPLICA sync started

解决方案(1),查看防火墙是否开启,如开启请关闭,如下命令:
#查看防火墙状态,如果出现Active: active (running),表示开启
systemctl status firewalld.service
#关闭防火墙
systemctl stop firewalld.service
#设置开机禁用防火墙
systemctl disable firewalld.service

解决方案(2),如果防火墙开启不想关闭,可以使用防火墙开放端口,如下命令:
#查看有哪些端口已经开启
firewall-cmd --list-port
#开放7003端口,zone #作用域,add-port=7003/tcp #添加端口,格式为:端口/通讯协议,permanent #永久生效,没有此参数重启后失效
firewall-cmd --zone=public --add-port=7003/tcp --permanent
#重启防火墙
firewall-cmd --reload

异常4

 # 如果Redis从机的redis.log日志中出现如下错误
1561:X 13 Jun 2021 17:28:05.758 * Increased maximum number of open files to 10032 (it was originally set to 1024).

解决方案,编辑limits.conf文件,vim /etc/security/limits.conf,最后一行添加以代码即可解决:
* soft nofile 65535
* hard nofile 65535
编辑完成保存后,重启服务器或者命令 ulimit -a 使文件生效

六、在springBoot中使用Redis

(1)pom添加依赖

<!-- 也可以使用Jedis工具自定义配置链接,这里使用Redis官方依赖即可 -->
<!--<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.6.0</version>
</dependency>-->

<!-- 官方封装的一个操作redis的start工具,借助它我们可以很方便的直接使用RedisTemplate来操作redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

(2)在springBoot的.properties或.yml配置文件配置Redis链接信息,如下为哨兵模式配置:

##Redis数据库索引,默认为0
spring.redis.database=0
# Redis服务设置的密码,默认为空
spring.redis.password=admin@123
# Redis哨兵模式连接,只需要连接哨兵ip和端口即可
spring.redis.sentinel.nodes=192.168.217.132:27003,192.168.217.133:27004,192.168.217.134:27005
##连接超时时间(毫秒)
spring.redis.timeout=6000ms

## 连接池配置,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池
#连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=1000
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=2
##连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait=-1ms

(3)在springBoot的.properties或.yml配置文件配置Redis链接信息,如下为集群模式配置:

##Redis数据库索引,默认为0
spring.redis.database=0
# Redis服务设置的密码,默认为空
spring.redis.password=admin@123
# Redis集群模式连接,需要把所有Redis服务ip和端口写上
spring.redis.cluster.nodes=192.168.217.135:8001,192.168.217.135:8002,192.168.217.136:8003,192.168.217.136:8004,192.168.217.137:8005,192.168.217.137:8006
##连接超时时间(毫秒)
spring.redis.timeout=6000ms

## 连接池配置,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池
#连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=1000
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=2
##连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait=-1ms

(4)添加Redis配置类,修改springBoot默认的redis序列化方式,springBoot提供的spring-boot-starter-data-redis集成包提供2个bean,这里我们配置redisTemplate序列化即可,如下代码:
#用于存储对象
redisTemplate
#用于存储String类型,key与value都为String
stringRedisTemplate

package com.example.demo4springboot.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置类
 *
 * @version: java version 1.8
 * @author:Mr orange
 * @date: 2021-06-14-16:53
 */
@Configuration
public class RedisConfig {
    /**
     * 设置手动存的序列化
     * 把任何数据保存到redis时,都需要进行序列化,默认使用JdkSerializationRedisSerializer进行序列化。
     * 默认的序列化会给所有的key,value的原始字符前,都加了一串字符(例如:\xAC\xED\x00\),不具备可读性
     * 所以需要配置jackson序列化方式
     */
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        
        // 指定序列化输入的类型,类必须是非final修饰的。final修饰的类,比如String,Integer等,会抛出异常
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // value采用jackson序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的key采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // hash的value采用String的序列化方式
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

上面代码中的objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
作用是序列化时将对象全类名一起保存下来,设置之后的序列化结果如下:

[[
	"com.example.demo4springboot.model.User",
	{
		"name": "zhangsan",
		"age": "100"
	}
]]

不设置的话,序列化结果如下,将无法反序列化:

 {
	"name": "zhangsan",
	"age": "100"
}

(4)在代码中使用,如在service实现类中使用,如下代码,注入Redis配置即可:

package com.example.demo4springboot.service.impl;

import com.example.demo4springboot.entity.User;
import com.example.demo4springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 用户接口类
 *
 * @version: java version 1.8
 * @author:Mr orange
 * @date: 2021-06-14-16:47
 */
@Service
public class UserServiceImpl implements UserService {
    /**
     * 注入redisTemplate
     */
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 更新用户信息
     */
    @Override
    public void updateUser(User user) {
        user.setId(1);
        user.setSex("男");
        user.setName("张三");
        user.setAge(30);
        // 往数据库更新User
        // userDao.update(user);
       // 把user对象存储到Redis中
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        operations.set(user.getName(), user);
        // 还可以设置过期时间,这里设置3秒过期时间
        //operations.set(user.getName(), user, 3, TimeUnit.SECONDS);
    }

    /**
     * 获取用户信息
     */
    @Override
    public User getById(String Id) {
    	ValueOperations<String, Object> operations = redisTemplate.opsForValue();
    	User getUser = (User) operations.get(user.getName());
        Object userKey = operations.get("user");
        if (getUser != null) {
            // 从Redis中获取用户信息
            return getUser;
        }
        // 从数据库获取用户
        // user= userDao.getById(id);
        System.out.println("从数据库获取");
        return null;
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/78202.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!