简介
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。
下载地址:https://github.com/microsoftarchive/redis/releases
安装
第一步,先在Windows上下载 Redis, 下载地址:https://redis.io/download>,传输到Centos的/data/redis目录下,或者在/data/redis目录下使用wget命令:wget https://download.redis.io/releases/redis-7.0.2.tar.gz
mkdir -r /data/redis
cd /data/redis
wget https://download.redis.io/releases/redis-7.0.2.tar.gz
第二步,解压,然后进入到解压目录,执行如下命令:
tar -zxvf redis-7.0.2.tar.gz
第三步,进入到redis-7.0.2目录下执行如下命令,编译并检查安装环境:
cd /data/redis/redis-7.0.2
make
如果执行 make令报错,redis是由C语言开发,因此安装之前必须要确保服务器已经安装了gcc,那么执行如下命令,安装 c 语言的编译器:
yum install -y cpp binutils glibc glibc-kernheaders glibc-common glibc-devel gcc
执行完如上命令,再试着执行 make 命令,如果还是报错,再执行如下命令:
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash
执行完如上的命令,再执行 make 命令,还是报错,就执行如下命令:
make MALLOC=libc
解决如上的问题的方式参照网址:https://blog.csdn.net/realize_dream/article/details/106483499
第四步,执行如下命令进行安装:
make install PREFIX=/data/redis
## 注意PREFIX=/data/redis指定安装目录,可指定可不指定,如果不指定默认是在/data/redis/redis-7.0.2/src/目录下,如果指定则会在/data/redis目录下创建一个bin文件夹,会安装在/data/redis/bin目录下
第五步,验证是否安装成功:
# 启动redis:进入src目录下执行如下命令
cd /data/redis/redis-7.0.2/src/
./redis-server
# 如果想让redis的日志在后台进行运行,进入src目录下执行执行如下命令
cd /data/redis/redis-7.0.2/src/
./redis-server &
# 指定配置配置文件的方式启动redis,进入src目录下执行执行如下命令
cd /data/redis/redis-7.0.2/src/
./redis-server ./redis.conf
[]
# 启动完成后连接进入redis,进入src目录下执行执行如下命令
cd /data/redis/redis-7.0.2/src/
./redis-cli
# 启动完成后远程连接进入redis,进入src目录下执行执行如下命令
cd /data/redis/redis-7.0.2/src/
./redis-cli -a password -h ip
# 进入redis后退出,执行执行如下命令
quit
# 关闭redis服务,进入src目录下执行执行如下命令
cd /data/redis/redis-7.0.2/src/
./redis-cli shutdown
第六步:配置环境变量(如果想在任何位置下启动redis,可进行此步骤,如果只想在/data/redis/redis-7.0.2/src/
目录下启动可忽略此步骤)
cp /etc/profile /etc/profile.back
vim /etc/profile
## 在文件末尾添加如下内容
export REDIS_HOME=/data/redis/redis-7.0.2/src
export PATH=$PATH:$REDIS_HOME
执行完上述命令后,可在任意目录下执行如下命令进行校验环境变量是否配置成功
redis-server /data/redis/redis-7.0.2/redis.conf &
redis-cli
redis-cli shutdown
第七步,设置开机自启,编辑/etc/systemd/system/redis.service文件,执行如下命令,该步骤根据需要配置
vim /etc/systemd/system/redis.service
## 在文件末尾添加如下内容
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/data/redis/redis-7.0.2/src/redis-server /data/redis/redis-7.0.2/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
第八步,远程客户端连接,redis配置文件中只允许在本机中访问(redis服务在哪台机器上启动就只能在哪台机器上访问)不允许远程连接,如果需要远程访问执行如下命令
cd /data/redis/redis-7.0.2
vim redis.conf
在vim状态下直接输入/bind查找bind字符串,找到bind 127.0.0.1 -::1
,将其修改为
#bind 127.0.0.1 -::1
## 允许任何IP访问
bind 0.0.0.0
开放redis的6379
端口,并重新加载防火墙
firewall-cmd --zone=public --add-port=6379/tcp --permanent && firewall-cmd --reload
使用
Redis的优势
快:Redis非常快,每秒可执行大约110000次的设置(SET)操作,每秒大约可执行81000次的读取/获取(GET)操作。
支持丰富的数据类型:Redis支持开发人员常用的大多数数据类型,例如列表,集合,排序集和散列等等。这使得Redis很容易被用来解决各种问题,因为我们知道哪些问题可以更好使用地哪些数据类型来处理解决。
操作的原子性:所有Redis操作都是原子操作,这确保如果两个客户端并发访问,Redis服务器能接收更新的值。
很多使用工具:Redis是一个多实用工具,可用于多种用例,如:缓存,消息队列(Redis本地支持发布/订阅),应用程序中的任何短期数据,例如,web应用程序中的会话,网页命中计数等。
数据类型
一共有九种,常见的有五种
A. 字符串类型 (String)(字符串的值,最大长度为512M)
## [特点:一个redis的key只对应一个value]
## [应用场景:点击数量、访问人数、手机号验证码]
B. 列表类型(List) (列表的最大长度为2^32 -1个元素,大概40亿)
## [特点:value有序(有序是指:按存入的顺序),数据可以重复,一个redis的key可以对应多个value]
## [应用场景:评论功能]
C. 集合类型(Set)
## [特点:value是无序的(无序是指:没有按存入的顺序),value不可以重复,一个redis的key可以对应多个value]
## [应用场景:点赞功能]
D. 有序集合类型(zset)
## [特点:对value进行了排序,value不可以重复,一个redis的key可以对应多个value]
## [应用场景:排行榜、百度热搜]
E. 散列类型(Hash) (map)
## [特点:一个redis的key可以对应多个value,每个value有是以K-V键值对的形式存在]
## [应用场景:对象信息的存储]
F.存储地理位置信息(Geo) (了解)
常用数据类型的操作
keys * : 获取数据库的所有的键。 ***
exists key: 判断某个键是否存在,返回表示存在,0表示部存在。
type key: 获取键的类型(string,hash,list,set,zset)
help @string/list/set/zset/hash 查看某种数据类型的所有相关命令的使用
select [0 ~ 16] 选择redis的数据库,redis有16个数据库,不选择的话默认使用的是第一个数据库0
flushall:清空所有的的数据
String类型的操作
set key value: 设置或者覆盖值
get key: 根据键取值
del key [key1, key2,key3,...]: 删除某个键
incr key : 将对应的键的值,递增1,前提是该value必须是个整数
decr key : 将对应的键的值,递减1,前提是该value必须是个整数
expire key 时间(秒):设置key的存活时间,单位为秒
set key value EX 60: 表示给key设置value,然后指定过期时间是60S
ttl code: 查看存活时间。 (TTL Time To Live)
setnx key value: 如果不存在就设置。(redis设置分布式锁)
set key value EX 秒 NX: 如果不存在就设置值,并且指定过期时间
List类型的操作
lpush key value: 往左侧中设置值
rpush key value: 往右侧插入值
lrange key start end: 取集合中索引在[start, end]之间的值
例:lrange aa 0 2 lrange aa 0 -1
llen key: 获取集合的长度
lpop key: 移除并返回首元素
rpop key: 移除并返回尾元素
lrem key count value: 移除列表中count个值为value的数据。当count为0,移除所有。(了解)
ltrim key start end: 保留指定区域的元素,其他全部删除。 (了解)
lset key index value: 设置索引为index的值为value. (了解)
lindex key index: 获取索引为index的元素。(了解)
Set类型的操作
sadd key member [memerb..]: 往集合中添加元素,返回添加成功的个数
smembers key: 返回集合中所有的元素
srem key member: 删除元素
sismember key member: 判断member是否存在, 存在返回1,不存在返回0
scard key: 返回集合中的个数
srandmember key: 从集合中随机返回一个值(了解)
spop key: 移除并返回一个随机的member(了解)
smove src destination member: 将src中member元素移动到destination中(了解)
sinter key key: 对集合求交集(了解)
sunion key key: 对两个集合求并集(了解)
sdiffstore destination key1 key2: 差集运算并存储到集合中(了解)
sinterstore destination key1 key2: 交集存储到集合中(了解)
sunionstore destionation key1 key2: 并集存储到集合中(了解)
Zset类型的操作
zadd key score value [score1 value1]: 添加(每个value添加的时候需要给出一个对应的分数score,zset是依据这个分数对value进行排序的)
zscore key value: 获取分数
zrange key start end: 获取索引从start开始,到end结束的所有的元素
zrange key start end withscores: 查询索引从start开始,到end结束的所有元素名和分数
zrange key 0 100 byscore withscores limit 0 2: 查询分数在 [0,100] 之间所有的元素,然后分页(背)
zcard key: 获取元素的个数
zcount key min max: 获取在指定分数范围内的元素的个数。闭区间[min, max]
zrem key value1 [value2]: 删除元素
zrank key value: 返回value在key中的下标
zincrby key num value: 给value对应的score的原有值的基础上增长num[新的score=原有的score+num]
zrevrange key 2 3: 倒序排列,然后去取下标在[2, 3]区间的元素
zrangebyscore key min max limit index length; 分页,min是最低分,max最高分,index是索引,length是长度。 (了解)
zrangebyscore key begin end: 查询分数在[begin,end]区间的所有值,根据分数排序。(了解)
zremrangebyscore key min max: 移除分数在[min,max]之间的数据,返回移除的个数。(了解)
zremrangebyrank key begin end: 移除索引在[begin,end]之间的数据。(了解)
Hash类型的操作
hset key field value: 设置值, 如果存在相同的Key,对应的值会覆盖之前的
hmset key field value filed value: 设置多个值
hget key field: 取值
hexists key field: 是否存在
hgetall key: 获取集合中所有的元素
hdel key field: 删除字段
hkeys key: 获取所有的key
hvals key: 获取所有的字段值
hlen key: 获取字段的数量
hsetnx key field value : 不存在的时候设置值
配置
配置密码
第一步,打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步,在vim状态下直接输入/requirepass查找requirepass,找到#requirepass foobared
,在下面一行插入如下命令保存并退出
requirepass password
启动
cd /data/redis/redis-7.0.2/src
./redis-server /data/redis/redis-7.0.2/redis.conf &
连接(进入)
cd /data/redis/redis-7.0.2/src
## [本机连接,没有-h默认指向的是本机127.0.0.1]
./redis-cli -a password
## [远程连接]
./redis-cli -a password -h ip
持久化
Redis有两种数据持久化策略:RDB(Redis DataBase)和 AOF (Append Only File),所谓的持久化是将内存中的数据写入到磁盘中,即在指定目录下生成一个dump.rdb文件。
RDB(Redis DataBase)策略
RDB 是 Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启后会通过加载dump.rdb文件来恢复数据。RDB存的是数据:name->张三
如果需要修改RDB(Redis DataBase)策略,请按照如下命令执行>>>>注意:此处不建议修改,默认的策略即可保障我们的需求
Redis进行RDB持久化的命令有两种:
1、save
该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。
显然该命令对于内存比较大的实例会造成长时间阻塞,这是致命的缺陷,为了解决此问题,Redis提供了第二种方式。
2、bgsave
执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。fork 会消耗一定时间,并且父子进程所占据的内存是相同的,当 Redis 键值较大时,fork 的时间会很长,这段时间内 Redis 是无法响应其他命令的。
基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令。Redis 会单独创建(fork)一个子进程进行持久化,会先将数据写入一个临时文件中,待持久化过程结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程不进行任何 IO 操作,这就确保的极高的性能。如果需要大规模的数据的恢复,且对数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加高效。RDB 唯一的缺点是最后一次持久化的数据可能会丢失。
修改数据持久化方案
第一步,打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步,在vim状态下直接输入/Save the DB to disk查找Save the DB to disk字符串,找到后在# save 3600 1 300 100 60 10000
下一行插入如下命令
## [save 秒 数据发生改变的条数]
## [save 60秒 5条数据发生改变]
save 60 5
## [在60秒内如果有5条数据发生改变就进行一次持久化(将数据写入到磁盘中)]
修改RDB数据持久化位置
第一步,打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步,在vim状态下直接输入/dbfilename查找dbfilename字符串,找到后将dbfilename dump.rdb
修改为如下内容
## [dbfilename 文件名]
dbfilename myfile.rdb
## redis的默认工作目录是"./"即"redis.conf"所在的目录
## 在vim状态下输入/The working directory可以查看
AOF(Append Only File)策略
AOF是Redis的另一种数据持久化方案,默认是不开启的。它的出现是为了弥补RDB的不足(数据的不一致性——内存中的数据和磁盘中的数据不一致),它所采用的是日志的形式来记录每个写操作,并追加到日志文件中。Redis重启后,会根据日志文件中的内容将所有的写指令从前往后的执行一次来完成数据的恢复。AOF存的是命令(操作):set name zhangsan
开启AOF数据持久化策略
第一步,打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步,在vim状态下直接输入/appendonly查找appendonly字符串,找到后将appendonly no
修改为如下内容
# appendonly no
appendonly yes
修改AOF数据持久化位置
如果需要修改AOF数据化持久化位置,请按照如下命令执行>>>>注意:此处不建议修改,默认文件位置是Redis的工作目录
第一步,打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步,在vim状态下直接输入/appendfilename查找appendfilename字符串,找到后将appendfilename "appendonly.aof"
修改为如下内容
# appendfilename "appendonly.aof"
appendfilename "my.aof"
## redis的默认工作目录是"./"即"redis.conf"所在的目录
## 在vim状态下输入/The working directory可以查看
修改AOF数据持久化方案
第一步,打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步,在vim状态下直接输入/appendfsync查找appendfsync字符串,找到后将appendfilename "appendonly.aof"
修改为如下内容
# appendfsync always [每一次的写操作都同步追加到.aof文件中,即写一个追加一个,每一次的数据变化都会立刻追加到到磁盘文件中。优点:安全,能较好地保证数据的完整性和一致性(内存和磁盘中的数据一致),缺点:性能较差]
# appendfsync everysec [redis默认和推荐的方式,每秒追加一次,即到了一秒就将用户在这一秒内所有的写操作追加到.aof文件中,没到一秒不会追加。优点:比always性能好,缺点:还是会有数据丢失的风险(如果时间没到一秒,redis发生宕机,那么在这一秒内用户所有的写操作都不会被追加),写操作和追加操作是异步的]
# appendfsync no [不同步,无意义,和不开启AOF没啥区别]
# appendfsync [ always | every | no ]三选一
appendfsync everysec
任何一种策略不可能无懈可击,都各自有各自的风险
AOF的瘦身机制
# 尽情期待,小编正在整理ing
灾难备份
redis的灾难备份:将Redis的Master机器的数据备份到Slave机器上
第一步:在Master机器上打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步:在vim状态下直接输入/bind查找bind字符串,换行添加如下内容::
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
# bind 0.0.0.0表示任何机器都可以连接
bind 0.0.0.0
第三步:在vim状态下直接输入/requirepass查找requirepass字符串,找到后换行添加如下内容:,配置密码:
# requirepass 密码
# 密码,三台redis的密码必须是一致:1.应用本身需要配置密码;2.从节点同步数据需要密码,因为节点的身份会发生变化。
requirepass password
从节点:
第一步:在Slave机器上打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步:在vim状态下直接输入/replicaof查找replicaof字符串,找到后换行添加如下内容
## [replicaof Master机器IP Master机器redis端口]
## replicaof表示***的副本
replicaof 192.168.1.100 6379
第三步:设置访问Master所需的密码,在vim状态下直接输入/masterauth查找masterauth字符串,找到后换行添加如下内容
## [masterauth 密码]
masterauth password
Redis的高可用
高可用性: 指的是通过尽量缩短日常维护操作和突发的系统崩溃所导致的停机时间,以提高系统和应用的可用性。Redis出现故障后不会影响到整个项目的正常运行。高可用一般来说有两个含义:一是数据尽量不丢失,二是保证服务尽可能可用。
Redis作为内存数据库,本身的特点就是必须保证高可用。如果部署一台机器,是非常容易出现单台机器挂掉的情况,一旦挂掉就会导致整个项目的不可用。在现实的使用过程当中,Redis为了实现高可用,通常的做法有三种部署模式:主从模式,哨兵模式,集群模式。
主从复制
Redis的主从复制类似于Redis的灾难备份,通过将Master节点中的数据备份到Slave机器上。主从复制模式中,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,以此来保证Redis一台机器出故障后不会影响到整个项目的正常运行。
主从复制包括全量复制,增量复制两种。一般当slave第一次启动连接master,或者认为是第一次连接,就采用全量复制然后继续判断,当后续发现出现了master节点的数据变化以后,就会将相关的指令发送到slave,从而在slave节点将指令在执行一遍,从而实现增量复制
Redis主从复制结构如下图所示,主节点(master)负责读写,从节点(slave)负责读。这个系统的运行依靠三个主要的机制:
- 当一个 master 实例和一个 slave 实例连接正常时, master 会发送一连串的命令流来保持对 slave的更新,以便于将自身数据集的改变复制给 slave ,包括客户端的写入、key 的过期或被逐出等等。
- 当 master 和 slave 之间的连接断开之后,因为网络问题、或者是主从意识到连接超时, slave 重新连接上 master并会尝试进行部分重同步:这意味着它会尝试只获取在断开连接期间内丢失的命令流。
- 当无法进行部分重同步时, slave 会请求进行全量重同步。这会涉及到一个更复杂的过程,例如 master需要创建所有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到 slave 。
主从模式优点:
- 实现读写分离,降低master节点的读数据压力,提高系统的性能。写操作交给master节点,读操作交给slave节点,提供多个副本。
- 配置简单,容易搭建。只需要在slave节点上维护master节点的地址信息就可实现。
- 数据拥有备份,一定程度上保障了数据的安全性和可靠性
主从模式缺点:
- 当master节点宕机时,由于无法选择哪一个slave节点当master节点,人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,无法达到真正意义上的高可用。
- 所有的写数据的压力都集中在master节点,没有解决master节点写的压力。
配置主从模式
主节点:
第一步:在Master机器上打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步:在vim状态下直接输入/bind查找bind字符串,找到后换行添加如下内容::
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 -::1
# bind 0.0.0.0表示任何机器都可以连接
bind 0.0.0.0
第三步:在vim状态下直接输入/requirepass查找requirepass字符串,找到后换行添加如下内容,配置密码:
# requirepass 密码
# 密码,三台redis的密码必须是一致:1.应用本身需要配置密码;2.从节点同步数据需要密码,因为节点的身份会发生变化。
requirepass password
第四步:由于主节点的身份会发生变化,还需配置认证主节点的密码(虽然当前的身份用不上,但是身份如果变为从节点后,连接主节点的话是需要认证密码的),在vim状态下直接输入/masterauth查找masterauth字符串,找到修改为如下内容:
# 认证主节点的密码,原因在于它的身份可能会发生变化
# masterauth 主节点密码
masterauth password
从节点:
注意:从节点的配置都一样
第一步:在Slave机器上打开/data/redis/redis-7.0.2/redis.conf文件
cd /data/redis/redis-7.0.2
vim redis.conf
第二步:由于从节点的身份可能会变为主节点,还需将从节点配置为任何机器都可连接,在vim状态下直接输入/bind查找bind字符串,找到后换行添加如下内容:
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
# bind 0.0.0.0表示任何机器都可以连接
bind 0.0.0.0
第三步:由于从节点的身份可能会变为主节点,还需配置从节点的密码(当从节点变成主节点后,其他节点连接需要密码认证),在vim状态下直接输入/requirepass查找requirepass字符串,找到后换行添加如下内容:
# requirepass 密码
# 密码,三台redis的密码必须是一致:1.应用本身需要配置密码;2.从节点同步数据需要密码,因为节点的身份会发生变化。
requirepass password
第六步:设置访问Master所需的密码,在vim状态下直接输入/masterauth查找masterauth字符串,找到后换行添加如下内容
## [masterauth 密码]
masterauth password
第五步:在vim状态下直接输入/replicaof查找replicaof字符串,找到后换行添加如下内容
## [replicaof Master机器IP Master机器redis端口]
## replicaof表示***的副本
replicaof 192.168.1.100 6379
第六步:查看并验证主节点、从节点的信息
# 第一步:连接到redis
src/redis-cli -p 6379
# 第二步:查看主节点、从节点的信息
info replication
哨兵模式
为了解决主从复制模式中,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址
,Redis从2.8开始正式提供了Redis Sentinel(哨兵)哨兵模式,它可以记录所有主节点和从节点的信息并监视所有主节点和从节点的状态,当被监视的主节点出现故障时,会通过投票机制,将某个从节点升级为新的主节点,并将所有的从节点连接到主节点。
哨兵模式的三个作用:
- 监控(Monitoring):不断地检查master和salve是否运行正常。master存活检测、master和slave运行情况检测。
- 通知(Notification):当被监控的某个节点出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障转移(Automaticfailover):断开master与slave之间的连接,选取一个salve作为master,将其他slave连接到新的master,并告知客户端新的节点地址
一个哨兵对Redis节点进行监控,一旦哨兵发生故障,主节点也出现故障,就无法选举出新的主节点从而导致整个项目的不可运行,因此,可以使用多个哨兵来进行监控Redis节点,并且各个哨兵之间还会进行监控。
对等节点集群理论:
- 集群的节点为奇数个,
可以最快的实现主节点的选举(偶数会较大程度出现打平状态)
。 - 在绝大多数的应用进行集群部署的时候,底层都秉持一个观点:集群中超过半数(包括半数)的节点不可用,那么整个集群是不可用的。1台和2台效果相同;3和4台效果一样。
配置哨兵
注意:三个哨兵在地位上是对等的,配置也是一样的
第一步:打开/data/redis/redis-7.0.2/sentinel.conf文件
cd /data/redis/redis-7.0.2
vim sentinel.conf
第二步:在vim状态下直接输入/bind查找bind字符串,找到后换行添加如下内容:
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
# bind 0.0.0.0表示任何机器都可以连接
bind 0.0.0.0
第三步:在vim状态下直接输入/sentinel monitor mymaster查找sentinel monitor mymaster字符串,找到后换行添加如下内容:
# 哨兵最原始状态下,要监听的主节点的:
# mymaster 是redis给主节点的取的默认的别名[由于在配置文件中引用了该别名,不建议修改,一旦修改其他引用的地方都必须修改]
# 192.168.1.100 6379 主接地那的ip和端口
# 2 表示至少有两台sentinel同意你是主, 那么才能成为主节点
sentinel monitor mymaster 192.168.1.100 6379 2
第三步:由于需要监视主节点,还需配置认证主节点的密码,在vim状态下直接输入/auth-pass查找auth-pass字符串,找到后换行添加如下内容:
# 主节点的密码
sentinel auth-pass mymaster password
开启哨兵
第一步:进入到redis的工作目录
# redis的工作目录是redis.conf所在的目录
cd /data/redis/redis-7.0.2
第二步:执行如下命令
./redis-sentinel ./sentinel.conf &
第三步:查看并验证哨兵信息中的主节点、从节点的信息
# 第一步:连接哨兵
src/redis-cli -p 26379
# 第二步:查看哨兵信息
info sentinel
哨兵模式下的问题:
脑裂问题
脑裂就是redis集群中出现多个master,导致出现该问题的原因在于 sentinel 与之前的 master 出现网络隔离,但是有应用还是可以和之前的master进行数据的读写;因为sentinel和之前的master出现网路隔离,他们会选举出一个新的master,就出现脑裂问题。如果之前的master网络问题被解决之后,sentinel会将其作为新选举出的master的slave存在,可能到数据不一致。
解决方案:设置——需要在每个哨兵中都去配置至少需要几个从节点才能成为主节点写入数据
# 当前节点如果为主节点,至少需要一台从节点;如果它是master, 但是它没有从节点,只能读不能写
min-replicas-to-write 1
# 表示主节点查找自己从节点的时间,目的是为了避免查找从节点的时间过长
min-replicas-max-lag 10
容量问题
哨兵模式下,数据的存储上限取决于Master的容量
解决方案:集群——水平扩容
https://www.cnblogs.com/cfzy/p/15502994.html
集群模式
Redis3.0加入了Redis的集群模式,实现了数据的分布式存储,对数据进行分片,将不同的数据存储在不同的master节点上面,从而解决了海量数据的存储问题。
Cluster 群集由多个redis服务器组成的分布式网络服 务群集,群集之中有多个master主节点,每一个主节点都可读可写,节点之间会相互通信,两两相连,redis群集无中心节点。
Redis也内置了高可用机制,支持N个master节点,每个master节点都可以挂载多个slave节点,当master节点挂掉时,集群会提升它的某个slave节点作为新的master节点。
哈希槽概念: Redis集群中引入了哈希槽的概念,Redis集群有16384个哈希槽,进行set操作时,每个key会通过CRC16校验后再对16384取模来决定放置在哪个槽,搭建Redis集群时会先给集群中每个master节点分配一部分哈希槽。比如当前集群有3个master节点,master1节点包含0 ~ 5500号哈希槽,master2节点包含5501~ 11000号哈希槽,master3节点包含11001~16384号哈希槽,当我们执行存取数据时,集群会对使用CRC16算法对key进行计算并对16384取模,假如 CRC16(key) % 16384 = 777,那么这个key就分配在master1节点上,然后直接到这个对应的节点上进行存取操作。
配置集群
以192.168.1.100这台机器为例子,来创建两台redis服务,需要在 $REDIS_HOME 目录下创建两个配置文件,分别是:
- redis-6379.conf 作为本机上Redis的主节点的配置文件
bind 0.0.0.0
# 端口是默认端口 6379
# 要保证每台机器的密码都是一样,因为主节点间要相互认证;主从的间的身份也可能会发生变化;
requirepass admin
# 认证主节点,因为主节点的身份可能会发生变化
masterauth admin
# rdb文件的文件名,因为每台机器有两台redis,所以名字一定不能一样
dbfilename dump-6379.rdb
# 表示开启集群
cluster-enabled yes
# 集群的信息,一台机器的两台redis不能相同,内容包含其它节点的状态,持久化变量等,会自动生成在上面配置的dir目录下
# 当Redis节点以集群模式启动时,会首先寻找是否有集群配置文件,如果有则使用文件中的配置启动,如果没有,则初始化配置并将配置保存到文件中。集群配置文件由Redis节点维护,不需要人工修改。
cluster-config-file nodes-6379.conf
- redis-6389.conf作为192.168.1.102这台机器上的Redis的从节点的配置文件。
bind 0.0.0.0
# 端口
port 6389
# 要保证每台机器的密码都是一样,因为主节点间要相互认证;主从的间的身份也可能会发生变化;
requirepass admin
# 认证主节点,因为主节点的身份可能会发生变化
masterauth admin
# rdb文件的文件名,因为每台机器有两台redis,所以名字一定不能一样
dbfilename dump-6389.rdb
# 表示开启集群
cluster-enabled yes
# 集群的信息,一台机器的两台redis不能相同,内容包含其它节点的状态,持久化变量等,会自动生成在上面配置的dir目录下
# 当Redis节点以集群模式启动时,会首先寻找是否有集群配置文件,如果有则使用文件中的配置启动,如果没有,则初始化配置并将配置保存到文件中。集群配置文件由Redis节点维护,不需要人工修改。
cluster-config-file nodes-6389.conf
开启集群
上述配置文件配置好之后,依次启动6个Redis服务
cd /data/redis/redis-7.0.2
./redis-server redis-6379.conf
./redis-server redis-6389.conf
上面虽然启动了6台redis,但是他们之间是独立运行的服务,并没有去构成集群,所有需要来创建集群,创建集群的命令如下所示:
src/redis-cli -a admin --cluster create 192.168.213.100:6379 192.168.213.101:6379 192.168.213.102:6379 192.168.213.100:6389 192.168.213.101:6389 192.168.213.102:6389 --cluster-replicas 1
进入到 redis 的客户端,然后可以查看 redis 的集群信息:
# 进入到redis, -c是因为目前是集群模式
src/redis-cli -c -a admin
# 查看集群信息
cluster nodes
https://blog.csdn.net/a745233700/article/details/112691126
Redis+数据库
思路:查询数据时,先去Redis中查询,如果Redis中有直接将结果返回给前端,如果Redis中没有再去数据库中查询,将从数据库中查询到的数据放入Redis,再将查询到的数据返回给前端。
缓存击穿
缓存击穿: 有大量用户同时去请求一个Redis中没有的热点数据,那么所有的查询都会打到数据库,造成数据库压力过大。[缓存中没有,数据库有]
解决方案: 控制并发,只让一个查询打到数据库,将从数据库中查询到的数据放入Redis。[控制并发,只能让一个人查询数据库]
缓存穿透
缓存穿透: 就算控制住并发,第一个用户从数据库中查询到的还是null,Redis中依然没有该数据,那么其他的查询还是都会打到数据库,造成数据库压力过大。[Redis中没有,数据库中也没有]
解决方案: 将数据库中查询到的数据放一个 “null” 字符串到Redis中,但是这样可能会导致一个问题,就是redis中存在大量的 “null” 字符串的问题,利用布隆过滤器来解决。
缓存雪崩
缓存雪崩: 同一时间大量的key同时过期,某一高峰期又有大量的请求查询已经过期的key,导致所有的数据查询都打到数据库去了。[同一时间缓存大量失效]
解决方案: key的过期时间不要设置成固定值,让key的失效时间随机。expire key random(34)
public String getInfo(String id){
//先组装redis的key
String redisKey = TEST# + id ;
//先从redis中查找
String info = stringRedisTempleate.opsForValue().get(redisKey);
//如果redis中没查询到结果从数据库中查询
//缓存击穿--控制并发 //缓存穿透--设置"null"字符串 //缓存雪崩--设置随机过期时间
if (info == null) {
//如何控制并发
//为什么不能用this,如果使用this就会将所有调用该方法的线程阻塞
synchronized(id.intern()){ //intern()方法是将调用该方法的字符串指向字符串常量池
//经典的双空判断--因为是高并发场景,如果第一个线程执行到if,时间片段到了,第二个线程进来,执行到if时间片段也到了...那么就有大量的线程拿到的info是null,到时候就会有大量的线程会打到数据库中查询,因此需要再进行一次空判断
//再从redis中查一遍
info = stringRedisTempleate.opsForValue().get(redisKey);
if (info == null) {
Map<String,String> infoMap = infoService.getInfo();
//如果查询到结果就再redis中放一份,如果没查到就放一个"null"字符串
if (infoMap == null){
stringRedisTempleate.opsForValue().set(redisKey,"null","随机过期时间");
return "null";
}else{
String result=JSONObject.toJSONString(infoMap);
stringRedisTempleate.opsForValue().set(redisKey,result,"随机过期时间");
return result;
}
}else {
return info;
}
}
}
}
Redis内存淘汰策略
过期键删除策略
redis会将所有设置了过期淘汰的键存储到一个集合中,对于这种需要到期删除的key,redis是通过过期键删除的两种策略: 1. 定时删除;2. 惰性删除;
-
定时删除策略:
1、redis的底层有一个线程,该线程每100毫秒会扫描一次集合; 2、每次从集合中随机抽取20个key,然后判断是否已经过期,如果过期就删除; 3、如果从随机抽取的20个key中,有超过1/4的key过期,再重复第二步; 4、直到随机抽取的20个key中,过期的key的数量少于1/4时,才会等100毫秒后再重复第一步.
定时删除,使用随机的方式是因为,如果每次进行一个全部key的判断,会导致CPU性能损耗过大。
-
惰性删除策略:
1、因为定时删除肯定存在着有些key过期没有被删除掉,于是就有了惰性删除; 2、所谓的惰性删除就是当用户使用的时候再去判断当前的key是否过期,如果过期就直接删除掉
内存淘汰策略
定时删除和惰性删除这两种配合的情况下,可能会导致有些键过期还是没有被删除(既没扫描到,也没有使用的key);于是就有了内存的淘汰策略,总共有八种
八种内存淘汰策略的前提是在redis配置中设置了最大内存: maxmemory
内存淘汰策略主要是值这样的一个场景:当达到redis的最大内存限制,添加新的数据已经添加不进去的时候,redis会按照一定的策略将一些数据淘汰掉,为新增的数据腾位置
1、直接报错,啥也不干:
no-eviction: 添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就返回error,然后啥也不干
2、从所有的key中淘汰一些最近未使用的key:
allkeys-lru:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会扫描所有的key,淘汰一些最近未使用的key
3、从设置了过期时间的key中淘汰一些最近未使用的key:
volatile-lru:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,扫描那些设置里过期时间的key,淘汰一些最近未使用的key
4、从所有的key中随机淘汰一些key:
allkeys-random:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会扫描所有的key,随机淘汰一些key
5、从设置了过期时间的key中随机淘汰一些key:
volatile-random:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,扫描那些设置里过期时间的key,随机淘汰一些key
6、从设置了过期时间的key中淘汰一些将要过期的key:
volatile-ttl:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,扫描那些设置里过期时间的key,淘汰一些即将过期的key
7、对所的key使用LFU算法淘汰一些key:
volatile-lfu:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会淘汰一些设置了过期时间的,使用LFU算法淘汰一些key(有些key虽然最近没有被使用,在接下来可能会被使用,就不会删除)
8、对设置了过期时间的key使用LFU算法淘汰一些key:
allkeys-lfu:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会扫描所有的key,使用 LFU这种算法淘汰一些key(有些key虽然最近没有被使用,在接下来可能会被使用,就不会删除)
整理:
默认的是当新的添加数据会导致超出redis最大内存限制,不会删除任何key, 直接报错;
还有就是对所有的key:淘汰最近未使用的、随机淘汰、LFU算法淘汰
还有就是对设置了过期时间的key:淘汰最近未使用的、随机淘汰、淘汰将要过期的、LFU算法
布隆过滤器
布隆过滤器解决的问题:为了解决缓存穿透的问题,数据库中没有的数据我们会在redis中存入"null"字符串,布隆过滤器的出现就是为了解决redis中出现大量"null"字符串的问题。
布隆过滤器原理:
使用布隆过滤器:
第一步,先导依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
第二步,将数据库中的数据根据id或者主键提前做好映射
//第二个参数:bloomFilter的长度
//第三个参数:bloomFilter的误判率,误判率越低hash的次数越多
private BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), 1000000, 0.03);
/**
* @PostConstruct相当于 init-method=""
* 在spring中凡是碰到方法名中带有Post的,都表示在XXXX之后
* 典型代表:BeanPostProcessor、BeanFactoryPostProcessor
*/
@PostConstruct
public void initBloomFilter(){
//查询所有的id得到一个集合,如果数据量过大可采用分页查询的方式
List<Long> ids = productInforMationService.selectAllId();
ids.forEach(id - > {
bloomFilter.put(id.toString);
});
}
第三步,通过布隆过滤器判断是否存在,如果存在就按照Redis那一套正常的流程走,如果不存在就返回空
注意:bloomFilter中有不能说明数据库中一定有,存在误判情况:不同的值hash出来的结果对应的桶位可能一样
if(bloomFilter.mightContain(id)){
...
}else{
return "null";
}
具体可参考:https://zhuanlan.zhihu.com/p/419610569
布隆过滤器+分布式锁整合
一、原生的分布式锁
package com.example.demo.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.mapper.ProductMapper;
import com.example.demo.model.ProductModel;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
//StringRedisTemplate和RedisTemplate的区别
//StringRedisTemplate是按String方式序列化的,是可以直接识别的
//RedisTemplate是按JDK的方式序列化的,是以字节数组显示不可直接识别的
private StringRedisTemplate redisTemplate;
private BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8),10000000,0.03);
//提前做好数据的映射
public void initBloomFilter(){
QueryWrapper<ProductModel> queryWrapper = new QueryWrapper<>();
Page<ProductModel> everyPageProductModels = productMapper.selectPage(new Page<>(1, 10), queryWrapper);
List<ProductModel> records = everyPageProductModels.getRecords();
for (ProductModel productModel : records){
String productId = String.valueOf(productModel.getId());
bloomFilter.put(productId);
}
}
public String findProductInfoById(long id){
String redisKey = "PRODUCT#INFO#"+id;
if (bloomFilter.mightContain(String.valueOf(id))){
//1、通过布隆过滤器判断数据库中是否存在
//A、如果布隆过滤器中存在
//1、从redis中查询
//2、判断redis中是否存在
//A、如果不存在,去数据库中查询
//1、注意:去数据库中查询时要控制并发,防止缓存击穿
//A、注意:如果项目部署在多个服务器上控制并发时还需使用分布式锁--setnx[如果不存在就添加,如果存在就不添加]
//B、注意:为了防止死锁还需给锁设置过期时间
//C、注意:为了防止程序在过期时间内还未执行完,还需用到一个看门狗watchDog程序给锁续命
//D、注意:拿到锁后还需要从Redis中再获取一次数据,防止在等锁的过程中某个拿到锁的线程向Redis中存放了数据[经典的双空判断]
//2、注意:由于boolmFilter存在误判,还需继续判断数据库中是否存在该数据
//A、如果数据库中也没有需要在redis中存入一个”null“字符串,防止缓存穿透
//B、如果数据库中存在就讲该值存入redis,并设置一个随机过期时间,防止缓存血崩
//B、如果存在,直接将结果返回
//B、如果存在直接返回结果
//2、如果布隆过滤器中不存在,直接放回一个"null"
String productInfo = redisTemplate.opsForValue().get(redisKey);
Thread watchDog = null ;
if (productInfo == null) {
try {
Boolean hasLock = redisTemplate.opsForValue().setIfAbsent("LOCK#" + id, "1", 30, TimeUnit.SECONDS);
if (hasLock){
watchDog = new Thread(()->{
while (true){
redisTemplate.expire("LOCK#" + id,30,TimeUnit.SECONDS);
try {
Thread.sleep(1000);
}catch (Exception e){
}
}
});
watchDog.start();
return this.commonCode(id,redisKey);
}else {
do {
hasLock = redisTemplate.opsForValue().setIfAbsent("LOCK#" + id, "1", 30, TimeUnit.SECONDS);
}while (!hasLock);
watchDog = new Thread(()->{
while (true){
redisTemplate.expire("LOCK#" + id,30,TimeUnit.SECONDS);
try {
Thread.sleep(1000);
}catch (Exception e){
}
}
});
watchDog.start();
return this.commonCode(id,redisKey);
}
}finally {
watchDog.interrupt();
redisTemplate.delete("LOCK#" + id);
}
}else {
return productInfo;
}
}else{
return "null";
}
}
public String commonCode(long id,String redisKey){
String productInfo = redisTemplate.opsForValue().get(redisKey);
if (productInfo == null){
QueryWrapper<ProductModel> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id",id);
ProductModel productModel = productMapper.selectOne(queryWrapper);
if (productModel != null){
redisTemplate.opsForValue().set(redisKey,productModel.getProductInfo(),Math.round(Math.random()*43200+43200),TimeUnit.SECONDS);
return productModel.getProductInfo();
}else {
redisTemplate.opsForValue().set(redisKey,"null",Math.round(Math.random()*43200+43200),TimeUnit.SECONDS);
return "null";
}
}else{
return productInfo;
}
}
}
二、Redisson提供的分布式锁
第一步、引入依赖
<!--============================================redis相关依赖开始============================================-->
<!-- redis的依赖 -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-redis</artifactId>-->
<!-- </dependency>-->
<!-- redis连接池的依赖 -->
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<!-- <dependency>-->
<!-- <groupId>org.apache.commons</groupId>-->
<!-- <artifactId>commons-pool2</artifactId>-->
<!-- <version>2.7.0</version>-->
<!-- </dependency>-->
<!-- redission依赖 有了redisson依赖需要将redis的依赖注释掉-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.20.0</version>
</dependency>
<!--============================================redis相关依赖结束============================================-->
第二步、配置yml文件
spring:
redis:
# host: 192.168.0.1
# password: password
# # lettuce 莴苣:这是redis的连接池
# lettuce.:
# pool:
# min-idle: 2
# max-active: 8
redisson:
file: classPath:redis-singleServerConfig.yml
第三步、新建一个redis-singleServerConfig.yml文件配置Redisson连接
singleServerConfig:
address: "redis://192.168.1.100:6379"
connectionMinimumIdleSize: 4
password: redis123
#netty,底层用netty都是高性能的代名词
nettyThreads: 4
#通信方式是nio
transportMode: NIO
第四步、使用
package com.example.demo.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.mapper.ProductMapper;
import com.example.demo.model.ProductModel;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
public class RedissionService {
@Autowired
private ProductMapper productMapper;
@Autowired
private RedissonClient redissonClient;
@Autowired
private StringRedisTemplate redisTemplate;
private BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8),10000000,0.03);
public void initBloomFilter(){
QueryWrapper<ProductModel> queryWrapper = new QueryWrapper<>();
Page<ProductModel> productModelPage = productMapper.selectPage(new Page<>(1, 10), queryWrapper);
List<ProductModel> records = productModelPage.getRecords();
records.forEach(record -> bloomFilter.put(String.valueOf(record.getId())));
}
public String getProductInfoById(long id){
String redisKey = "PRODUCT#INFO#"+id;
if (bloomFilter.mightContain(String.valueOf(id))){
String productInfo = redisTemplate.opsForValue().get(redisKey);
if (productInfo == null){
//=========================重点!================================
RLock lock = redissonClient.getLock(redisKey);
//重点:lock方法是一个阻塞方法,如果没有获取到锁则会一直尝试获取锁
lock.lock();
//=========================重点!================================
try {
productInfo = redisTemplate.opsForValue().get(redisKey);
if (productInfo == null){
QueryWrapper<ProductModel> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id",id);
ProductModel productModel = productMapper.selectOne(queryWrapper);
productInfo = productModel.getProductInfo();
if (productInfo != null){
redisTemplate.opsForValue().set(redisKey,productInfo,Math.round(Math.random()*43200+43200), TimeUnit.SECONDS);
return productInfo;
}else {
redisTemplate.opsForValue().set(redisKey,"null",Math.round(Math.random()*43200+43200), TimeUnit.SECONDS);
return "null";
}
}else {
return productInfo;
}
}finally {
if (lock.isLocked()){
lock.unlock();
}
}
}else {
return productInfo;
}
}else {
return "null";
}
}
}
实现分布式锁的方式不只有Redisson,比如:
1、可利用MySQL主键和唯一键的唯一性实现分布式锁。
Redis的事物
参考:https://blog.csdn.net/shark_chili3007/article/details/120884205
SpringBoot整合Redis
单机版:
第一步,先导依赖
<!-- redis的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis连接池的依赖 -->
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
第二步,在.yml文件中配置连接池
spring:
redis:
host: 192.168.0.1
password: password
# lettuce 莴苣:这是redis的连接池
lettuce.:
pool:
min-idle: 2
max-active: 8
第三步,代码中直接注入 RedisTemplate 、StringRedisTemplate 即可使用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class RedisService{
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public void getInfo(String value) {
//stringRedisTemplate中5种常见的OpsFor分别是:opsForValue、opsForList、opsForHash、opsForSet、OpsForZSet
ListOperations<String, String> opsListOperations = stringRedisTemplate.opsForList();
opsListOperations.leftPush("address",value);
List<String> address = opsListOperations.range("address", 0, -1);
System.out.println(address);
}
}
哨兵模式:
第一步:导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
第二步:配置哨兵
spring:
redis:
sentinel:
nodes: 192.168.1.120:26379,192.168.1.121:26379,192.168.1.122:26379
# 主节点会发生变化,用sentinel.conf文件中的别名
master: mymaster
password: password
第三步:代码中直接注入 RedisTemplate 、StringRedisTemplate 即可使用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class RedisService{
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public void getInfo(String value) {
//stringRedisTemplate中5种常见的OpsFor分别是:opsForValue、opsForList、opsForHash、opsForSet、OpsForZSet
ListOperations<String, String> opsListOperations = stringRedisTemplate.opsForList();
opsListOperations.leftPush("address",value);
List<String> address = opsListOperations.range("address", 0, -1);
System.out.println(address);
}
}
集群模式
第一步:导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
第二步:配置集群
spring:
redis:
cluster:
nodes: 192.168.1.110:6379,192.168.1.111:6379,192.168.1.112:6379
password: password
第三步:代码中直接注入 RedisTemplate 、StringRedisTemplate 即可使用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class RedisService{
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public void getInfo(String value) {
//stringRedisTemplate中5种常见的OpsFor分别是:opsForValue、opsForList、opsForHash、opsForSet、OpsForZSet
ListOperations<String, String> opsListOperations = stringRedisTemplate.opsForList();
opsListOperations.leftPush("address",value);
List<String> address = opsListOperations.range("address", 0, -1);
System.out.println(address);
}
}
Redis.config配置文件说明
bind 0.0.0.0 # 测试环节,任何地址都可连接
port 6379 # 修改成对应的端口号
daemonize yes # 后台运行
pidfile /var/run/redis_6379.pid # pid文件
logfile "./redis.log" # 日志
dir /usr/local/redis/data # 设置文件数据存储路径
rename-command FLUSHALL adminFLUSHALL # 禁止危险命令
rename-command FLUSHDB adminFLUSHDB # 禁止危险命令
# rename-command KEYS adminKEYS # 禁止危险命令
rename-command SCAN adminSCAN # 禁止危险命令
rename-command CONFIG "" # 信息安全:防止redis被非法连接后,使用config命令对操作系统进行控制;另:要求redis使用非root帐号运行
maxclients 100000 # 集群支持的最大连接
timeout 60 # 设置空闲连接超时时间
maxmemory 6000M # Redis默认无限使用服务器内存,为防止极端情况下导致系统内存耗 尽,建议所有的Redis进程都要配置maxmemory, 要保证机器本身有30%左右的闲置内存
maxmemory-policy volatile-lru # 内存剔除策略logfile “” 默认为空,则在控制台打印,否则输出日志到指定的log文件
save "" # 通用配置,关闭RDB持久化,只使用AOF。
appendonly yes # 开启 aop 备份
masterauth 0NZcyqRqLUteci7D # 设置 master 节点的认证密码
requirepass 0NZcyqRqLUteci7D # 设置 redis 连接密码
appendfilename "appendonly-8379.aof" # 设置持久化文件名称
appendfsync always # 每写一条 备份 一次
cluster-enabled yes # 开启 Redis Cluster
cluster-config-file nodes-6379.conf # 记录集群信息,不用手动维护,Redis Cluster 会自动维护
cluster-node-timeout 2000 # Cluster 集群节点连接超时时间,如果集群规模小,都在同一个网络环境下,可以配置的短些,更快的做故障转移。
cluster-require-full-coverage no # 设置为no,默认为yes,故障发现到自动完成转移期间整个集群是不可用状态,对于大多数业务无法容忍这种情况,因此要设置为no,当主节点故障时只影 响它负责槽的相关命令执行,不会影响其他主节点的可用性。只要有结点宕机导致16384个槽没全被覆盖,整个集群就全部停止服务,所以一定要改为no
no-appendfsync-on-rewrite yes # 设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,提高redis写入性能
protected-mode no # 允许外部主机访问
slowlog-log-slower-than 1000 # 慢查询日志,用于性能分析,生产环境可设置为1000(微妙)
slowlog-max-len 1000 # 保存慢查询的队列长度 ,设置为1000
cluster-slave-validity-factor 0 # 设置为0,如果master slave都挂掉, slave跟master失联又超过这个数值*timeout的数值, 就不会发起选举了。如果设置为0,就是永远都会尝试发起选举,尝试从slave变为mater
参考:https://www.cnblogs.com/miaocbin/p/11356358.html