Redis介绍
Redis的产⽣背景
2008年 萨尔瓦多——开发⼀个进⾏⽹站实时统计软件项⽬(LLOOGG),⽬的实时统计功能需要频繁的进⾏数据库的读写(对数据库的读写要求很⾼—数千次/s),MySQL满⾜不了项⽬的需求,萨尔瓦多就使⽤C语⾔⾃定义了⼀个数据存储系统—Redis。后来萨尔瓦多不满⾜仅仅在LLOOGG这个项⽬中⽤redis,就对redis进⾏
产品化
并进⾏开源,以便让更多的⼈能够使⽤
Redis使⽤
Redis就是⼀个⽤C语⾔开发的、基于内存结构进⾏ 键值对 数据存储的、⾼性能的、⾮关系型NoSQL数据库
Redis可以做什么
数据库访问压⼒
:为了降低对数据库的访问压⼒,当多个⽤户请求相同的数据时,我们可以将第⼀次从数据库查询到数据进⾏缓存(存储在内存中),以减少对数据库的访问次数⾸⻚数据的加载效率
:将⼤量的且不经常改变的数据缓存在内容中,可以⼤幅度提⾼访问速度集群部署下的商品超卖
:分布式事务⽤户登录
:分布式会话
Redis⽀持的数据类型
redis是基于键值对进⾏数据存储的,但是value可以是多种数据类型
- string 字符串
- hash 映射
- list 列表(队列)
- set 集合
- zset ⽆序集合
Redis特点
- 基于内存存储,数据读写效率很⾼
- Redis本身⽀持持久化
- Reids虽然基于key-value存储,但是⽀持多种数据类型
- Redis⽀持集群、⽀持主从模式
Redis应⽤场景
缓存
:在绝⼤多数的互联⽹项⽬中,为了提供数据的访问速度、降低数据库的访问压⼒,我们可以使⽤redis作为缓存来实现- 点赞、排⾏榜、计数器等功能:对数据实时读写要求⽐较⾼,但是对数据库的⼀致性要求并不是太⾼的功能场景
分布式锁
:基于redis的操作特性可以实现分布式锁功能分布式会话
:在分布式系统中可以使⽤redis实现 session (共享缓存)消息中间件
:可以使⽤redis实现应⽤之间的通信
Redis的优缺点
优点
- redis是基于内存结构,性能极⾼(读 110000次/秒,写 81000次/秒)
- redis基于键值对存储,但是⽀持多种数据类型
- redis的所有操作都是原⼦性,可以通过lua脚本将多个操作合并为⼀个院⼦操作(Redis的事务)
- reids是基于单线程操作,但是其多路复⽤实现了⾼性能读写
缺点
- 缓存数据与数据库数据必须通过两次写操作才能保持数据的⼀致性
- 使⽤缓存会存在缓存穿透、缓存击穿及缓存雪崩等问题,需要处理
- redis可以作为数据库使⽤进⾏数据的持久存储,存在丢失数据的⻛险
Redis安装及配置
Redis安装
Redis安装及配置
下载Redis
cd /home
wget http://download.redis.io/releases/redis-5.0.5.tar.gz
安装redis
yum -y install gcc
解压redis安装包
tar -zxvf redis-5.0.5.tar.gz
解压之后进⼈到redis-5.0.5⽬录
cd redis-5.0.5
编译
make MALLOC=libc
安装
make install
启动redis,
当我们完成redis安装之后,就可以执⾏redis相关的指令
redis-server
在打开一个命令窗口,打开客户端,
启动redis操作客户端(命令⾏客户端)
redis-cli
Redis配置
使⽤
redis-server
指令启动redis服务的时候,可以在指令后添加redis配置⽂件的路径,以设置redis 是以何种配置进⾏启动
redis-server redis-6380.conf & ## redis以redis-6380.conf⽂件中的配置来启动
如果不指定配置⽂件的名字,则按照redis的默认配置启动(默认配置≠redis.conf)
我们可以通过创建redis根⽬录下redis.conf
来创建多个配置⽂件,启动多个redis服务
&
代表后台启动
redis-server redis-6380.conf &
redis-server redis-6381.conf &
常⽤redis配置
## 设置redis实例(服务)为守护模式,默认值为no,可以设置为yes
daemonize no
## 设置当前redis实例启动之后保存进程id的⽂件路径
pidfile /var/run/redis_6379.pid
## 设置redis实例的启动端⼝(默认6379)
port 6380
## 设置当前redis实例是否开启保护模式
protected-mode yes
## 设置允许访问当前redis实例的ip地址列表
bind 127.0.0.1
## 设置连接密码
requirepass 123456
## 设置redis实例中数据库的个数(默认16个,编号0-15)
databases 16
## 设置最⼤并发数量
maxclients
## 设置客户端和redis建⽴连接的最⼤空闲时间,设置为0表示不限制
timeout 0
Redis基本使⽤
Redis存储的数据结构
Redis是以键值对形式进⾏数据存储的,但是value⽀持多种数据类型
string常⽤指令
设置值/修改值,如果key存在则进⾏修改
set key value
取值
get key
批量添加
mset k1 v1 [k2 v2 k3 v3 ...]
批量取值
mget k1 [k2 k3...]
⾃增和⾃减
# 在key对应的value上⾃增 +1
incr key
# 在key对应的value上⾃减 -1
decr key
# 在key对应的value上+v
incrby key v
# 在key对应的value上-v
decrby key v
添加键值对,并设置过期时间(TTL)
setex key time(seconds) value
设置值,
如果key不存在则成功添加,如果key存在则添加失败(不做修改操作)
setnx key value
在指定的key对应value拼接字符串
append key value
获取key对应的字符串的⻓度
strlen key
hash常⽤指令
向key对应的hash中添加键值对
hset key field value
从key对应的hash获取field对应的值
hget key field
向key对应的hash结构中批量添加键值对
hmset key f1 v1 [f2 v2 ...]
从key对应的hash中批量获取值
hmget key f1 [f2 f3 ...]
在key对应的hash中的field对应value上加v
hincrby key field v
获取key对应的hash中所有的键值对
hgetall key
获取key对应的hash中所有的field
hkeys key
获取key对应的hash中所有的value
hvals key
检查key对应的hash中是否有指定的field
hexists key field
获取key对应的hash中键值对的个数
hlen key
向key对应的hash结构中添加f-v,如果field在hash中已经存在,则添加失败
hsetnx key field value
list常⽤指令
存储数据
# 在key对应的列表的左侧添加数据value
lpush key value
# 在key对应的列表的右侧添加数据value
rpuhs key value
获取数据
# 从key对应的列表的左侧取⼀个值
lpop key
# 从key对应的列表的右侧取⼀个值
rpop key
修改数据,
修改key对应的列表的索引位置的数据(索引从左往右,从0开始)
lset key index value
查看key对应的列表中索引从start开始到stop结束的所有值
lrange key start stop
查看key对应的列表中index索引对应的值
lindex key index
获取key对应的列表中的元素个数
llen key
从key对应的列表中截取key在[start,stop]范围的值,不在此范围的数据⼀律被清除掉
ltrim key start stop
从k1右侧取出⼀个数据存放到k2的左侧
rpoplpush k1 k2
set常⽤指令
存储元素 :在key对应的集合中添加元素,可以添加1个,也可以同时添加多个元素
sadd key v1 [v2 v3 v4...]
遍历key对应的集合中的所有元素
smembers key
随机从key对于听的集合中获取⼀个值(出栈)
spop key
交集
sinter key1 key2
并集
sunion key1 key2
差集
sdiff key1 key2
从key对应的集合中移出指定的value
srem key value
检查key对应的集合中是否有指定的value
sismember key value
zset常⽤指令
zset 有序不可重复集合
存储数据(score存储位置必须是数值,可以是float类型的任意数字;member元素不允许重复)
zadd key score member [score member...]
查看key对应的有序集合中索引[start,stop]数据——按照score值由⼩到⼤(start 和 stop指的不是score,⽽是元素 在有序集合中的索引)
zrange key start top
查看member元素在key对应的有序集合中的索引
zscore key member
获取key对应的zset中的元素个数
zcard key
获取key对应的zset中,score在[min,max]范围内的member个数
zcount key min max
从key对应的zset中移除指定的member
zrem key6 member
查看key对应的有序集合中索引[start,stop]数据——按照score值由⼤到⼩
zrevrange key start stop
key相关指令
查看redis中满⾜pattern规则的所有的key(keys *)
keys pattern
查看指定的key谁否存在
exists key
删除指定的key-value对
del key
获取当前key的存活时间(如果没有设置过期返回-1,设置过期并且已经过期返回-2)
ttl key
设置键值对过期时间
expire key seconds
pexpire key milliseconds
取消键值对过期时间
persist key
db常⽤指令
redis的键值对是存储在数据库中的—db
redis中默认有16个db,编号 0-15
切换数据库
select index
将键值对从当前db移动到⽬标db
move key index
清所有数据库的k-v
flushall
清空当前数据库数据
flushdb
查看当前db中k-v个数
dbsize
获取最后⼀次持久化操作时间
lastsave
Redis的持久化
Redis是基于内存操作,但作为⼀个数据库也具备数据的持久化能⼒;但是为了实现⾼效的读写操作,并不会 即时进⾏数据的持久化,⽽是按照⼀定的规则进⾏持久化操作的
持久化策略Redis提供了2中持久化策略:
RDB (Redis DataBase)
AOF(Append Only File)
RDB (Redis DataBase)
在满⾜特定的redis操作条件时,将内存中的数据以数据快照的形式存储到rdb⽂件中
原理
RDB是redis默认的持久化策略,当redis中的写操作达到指定的次数、同时距离上⼀次持久化达到指定的时间 就会将redis内存中的数据⽣成数据快照,保存在指定的rdb⽂件中。
- 默认触发持久化条件:
- 900s 1次:当操作次数达到1次,900s就会进⾏持久化
- 300s 10次:当操作次数达到10次,300s就会进⾏持久化
- 60s 10000次:当操作次数达到10000次,60s就会就⾏持久化
我们可以通过修改redis.conf⽂件,来设置RDB策略的触发条件:
## rdb持久化开关
rdbcompression yes
## 配置redis的持久化策略
save 900 1
save 300 10
save 60 10000
## 指定rdb数据存储的⽂件
dbfilename dump.rdb
RED持久化细节分析
缺点
- 如果redis出现故障,存在数据丢失的⻛险,丢失上⼀次持久化之后的操作数据;
- RDB采⽤的是数据快照形式进⾏持久化,不适合实时性持久化;
- 如果数据量巨⼤,在RDB持久化过程中⽣成数据快照的⼦进程执⾏时间过⻓,会导致redis卡顿,因此 save时间周期设置不宜过短;
优点
- 但是在数据量较⼩的情况下,执⾏速度⽐较快;
- 由于RDB是以数据快照的形式进⾏保存的,我们可以通过拷⻉rdb⽂件轻松实现redis数据移植
AOF(Append Only File)
Apeend Only File,当达到设定触发条件时,将redis执⾏的写操作指令存储在aof⽂件中,Redis默认未开启aof 持久化
原理
Redis将每⼀个成功的写操作写⼊到aof⽂件中,当redis重启的时候就执⾏aof⽂件中的指令以恢复数据
配置
## 开启AOF
appendonly yes
## 设置触发条件(三选⼀)
appendfsync always ## 只要进⾏成功的写操作,就执⾏aof
appendfsync everysec ## 每秒进⾏⼀次aof
appendfsync no ## 让redis执⾏决定aof
## 设置aof⽂件路径
appendfilename "appendonly.aof"
AOF细节分析:
- 也可以通过拷⻉aof⽂件进⾏redis数据移植
- aof存储的指令,⽽且会对指令进⾏整理;⽽RDB直接⽣成数据快照,在数据量不⼤时RDB⽐较快
- aof是对指令⽂件进⾏增量更新,更适合实时性持久化
- redis官⽅建议同时开启2中持久化策略,如果同时存在aof⽂件和rdb⽂件的情况下aof优先
Java应⽤连接Redis
设置redis允许远程连接
Java应⽤连接Redis,⾸先要将我们的Redis设置允许远程连接
修改redis-6379.conf
cd /home/redis-5.0.5
vim redis-6379.conf
## 关闭保护模式
protected-mode no
## 将bind注释掉(如果不注释,默认为 bind 127.0.0.1 只能本机访问)
# bind 127.0.0.1
## 密码可以设置(也可以不设置)
requirepass 123456
启动redis
redis-server redis-6379.conf
连接
在普通Maven⼯程连接Redis
使⽤jedis客户端连接
添加Jedis依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
使⽤案例
public static void main(String[] args) {
Product product = new Product("101", "AD钙奶", 5);
//1.连接redis
Jedis jedis = new Jedis("192.168.88.142", 6379);
//2.操作
String s = jedis.set(product.getProductId(), new Gson().toJson(product));
System.out.println(s);
//3.关闭连接
jedis.close();
}
在SpringBoot⼯程连接Redis
- Spring Data Redis依赖中,提供了⽤于连接redis的客户端:
- RedisTemplate
- StringRedisTemplate
创建springBoot应⽤
配置redis
application.yml⽂件配置redis的连接信息
spring:
redis:
host: 192.168.88.142
port: 6379
database: 0
password: 123456
使⽤redis客户端连接redis
直接在service中注⼊
RedisTemplate
或者StringRedisTemplate
,就可以使⽤此对象完成redis操作
Spring Data Redis
不同数据结构的添加操作
string
,添加数据 set key value
stringRedisTemplate.boundValueOps(product.getProductId()).set( jsonstr);
hash
stringRedisTemplate.boundHashOps("products").put(product.getProductId(),jsonstr);
list
stringRedisTemplate.boundListOps("list").leftPush("ccc");
set
stringRedisTemplate.boundSetOps("s1").add("v2");
zset
stringRedisTemplate.boundZSetOps("z1").add("v1",1.2);
string类型的操作⽅法
添加数据 set key value
stringRedisTemplate.boundValueOps(product.getProductId()).set( jsonstr);
添加数据时指定过期时间 setex key 300 value
stringRedisTemplate.boundValueOps("103").set(jsonstr,300);
设置指定key的过期时间 expire key 20
stringRedisTemplate.boundValueOps("103").expire(20, TimeUnit.SECONDS);
添加数据 setnx key value
Boolean absent = stringRedisTemplate.boundValueOps("103").setIfAbsent(jsonstr);
不同数据类型的取值操作
string
String o = stringRedisTemplate.boundValueOps("103").get();
hash
Object v = stringRedisTemplate.boundHashOps("products").get("101");
list
String s1 = stringRedisTemplate.boundListOps("list").leftPop();
String s2 = stringRedisTemplate.boundListOps("list").rightPop();
String s3 = stringRedisTemplate.boundListOps("list").index(1);
set
Set<String> vs = stringRedisTemplate.boundSetOps("s1").members();
zset
Set<String> vs2 = stringRedisTemplate.boundZSetOps("z1").range(0, 5);
使⽤Redis做缓存使⽤存在的问题
使⽤redis做为缓存在⾼并发场景下有可能出现缓存击穿、缓存穿透、缓存雪崩等问题
缓存击穿
缓存击穿
:⼤量的并发请求同时访问同⼀个在redis中不存在的数据,就会导致⼤量的请求绕过redis同时并发 访问数据库,对数据库造成了⾼并发访问压⼒
使⽤
双重检测锁
解决缓存击穿
问题
@Service
public class IndexImgServiceImpl implements IndexImgService {
@Autowired
private IndexImgMapper indexImgMapper;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private ObjectMapper objectMapper = new ObjectMapper();
/**
* 查询轮播图
* @return
*/
@Override
public ResultVO listIndexImgs() {
List<IndexImg> indexImgList = null;
try{
// 1000个并请求,请求轮播图
//string 结构缓存轮播图信息
String imgsStr = stringRedisTemplate.opsForValue().get("indexImgs");
// 1000个请求查询到redis中的数据都是null
if (imgsStr != null){
// 从redis中获取到了 值
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, IndexImg.class);
indexImgList = objectMapper.readValue(imgsStr,javaType);
}else{
//从redis中没有获取到数据
// 1000个请求都会进入else (service类在spring容器中是单例的,1000个并发会启动1000个线程来处理,但是公用一个service实例)
synchronized (this){
// 第二次查询redis
String s = stringRedisTemplate.opsForValue().get("indexImgs");
if(s == null){
// 这1000个请求中,只有第一个请求,再次查询redis时,依然为null
indexImgList = indexImgMapper.listIndexImgs();
if (indexImgList != null) {
System.out.println("------------------并发解决:查询数据库了");
//写入redis
stringRedisTemplate.boundValueOps("indexImgs").set(objectMapper.writeValueAsString(indexImgList));
// 设置过期时间,为一天
stringRedisTemplate.boundValueOps("indexImgs").expire(1, TimeUnit.DAYS);
}else{
// 解决 redis的缓存穿透
List<IndexImg> arr = new ArrayList<>();
stringRedisTemplate.boundValueOps("indexImgs").set(objectMapper.writeValueAsString(arr));
// 设置过期时间,为 10秒
stringRedisTemplate.boundValueOps("indexImgs").expire(1, TimeUnit.SECONDS);
}
}else {
System.out.println("------------------双重同步锁-并发解决:查询--redis--了");
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, IndexImg.class);
indexImgList = objectMapper.readValue(s,javaType);
}
}
}
}catch (JsonProcessingException e){
e.printStackTrace();
}
//返回数据
if(indexImgList != null){
return new ResultVO(ResStatus.OK,"success",indexImgList);
}else{
return new ResultVO(ResStatus.NO,"fail",null);
}
}
}
缓存穿透
缓存穿透
:⼤量的请求⼀个数据库中不存在的数据,⾸先在redis中⽆法命中,最终所有的请求都会访问数据 库,同样会导致数据库承受巨⼤的访问压⼒。
解决⽅案
:当从数据库查询到⼀个null时,写⼀个⾮空的数据到redis,并设置过期时间
indexImgs = indexImgMapper.listIndexImgs();
if(indexImgs != null) {
String s = objectMapper.writeValueAsString(indexImgs);
stringRedisTemplate.boundValueOps("indexImgs").set(s);
stringRedisTemplate.boundValueOps("indexImgs").expire(1, TimeUnit.DAYS);
} else {
//当从数据库查询数据为null时,保存⼀个⾮空数据到redis,并设置过期时间
stringRedisTemplate.boundValueOps("indexImgs").set("[]");
stringRedisTemplate.boundValueOps("indexImgs").expire(10, TimeUnit.SECONDS);
}
缓存雪崩
缓存雪崩
:缓存⼤量的数据集中过期,导致请求这些数据的⼤量的并发请求会同时访问数据库
解决⽅案:
- 将缓存中的数据设置成不同的过期时间
- 在访问洪峰到达前缓存热点数据,过期时间设置到流量最低的时段
Redis⾼级应⽤
使⽤redis作为缓存数据库使⽤⽬的是为了提升数据加载速度、降低对数据库的访问压⼒,我们需要保证redis 的可⽤性
- 主从配置
- 哨兵模式
- 集群配置
主从配置
主从配置:在多个redis实例建⽴起主从关系,当
主redis
中的数据发⽣变化, 从redis
中的数据也会同步变化
- 通过主从配置可以实现redis数据的备份(
从redis
就是对主redis
的备份),保证数据的安全性; - 通过主从配置可以实现redis的
读写分离
主从配置
启动三个redis实例
在redis-5.0.5⽬录下创建 msconf ⽂件夹
cd /home/redis-5.0.5
mkdir msconf
拷⻉redis.conf⽂件 到 msconf⽂件夹 —> redis-master.conf
cat redis.conf |grep -v "#" | grep -v "^$" > msconf/redis-master.conf
修改 redis-master.conf 端⼝及远程访问设置
cd /home/redis-5.0.5/msconf
vim redis-master.conf
将 redis-master.conf 拷⻉两份分别为:redis-slave1.conf,redis-slave2.conf
sed 's/6380/6381/g' redis-master.conf > redis-slave1.conf
sed 's/6380/6382/g' redis-master.conf > redis-slave2.conf
修改redis-slave1.conf redis-slave2.conf,设置“跟从”—192.168.88.142 6380
slaveof 192.168.88.142 6380
vim redis-slave1.conf
vim redis-slave2.conf
启动三个redis实例
cd /home/redis-5.0.5/msconf
redis-server redis-master.conf &
redis-server redis-slave1.conf &
redis-server redis-slave2.conf &
成功启动
测试主从
连接
插入数据测试
主机
从1
从2
哨兵模式
哨兵模式:⽤于监听主库,当确认主库宕机之后,从备库(从库)中选举⼀个转备为主
哨兵模式配置
⾸先实现三个redis实例之间的主从配置(如上)
创建并启动三个哨兵。
拷⻉sentinel.conf⽂件三份:sentinel-26380.conf sentinel-26382.conf sentinel-26382.conf
创建sentinelconf⽬录。
拷⻉sentinel.conf⽂件到 sentinelconf⽬录:sentinel-26380.conf
mkdir /home/redis-5.0.5/sentinelconf
cat sentinel.conf | grep -v "#" | grep -v "^$" > sentinelconf/sentinel-26380.conf
进入文件夹
cd /home/redis-5.0.5/sentinelconf
编辑 sentinelconf/sentinel-26380.conf⽂件
vim sentinel-26380.conf
port 26380
daemonize no
pidfile /var/run/redis-sentinel-26380.pid
logfile ""
dir /tmp
# 此处配置默认的主库的ip和端口,最后的数字是哨兵数量的1半多1个
sentinel monitor mymaster 192.168.88.142 6380 2
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
protected-mode no
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
执行复制
cd /home/redis-5.0.5/sentinelconf
sed 's/26380/26381/g' sentinel-26380.conf > sentinel-26381.conf
sed 's/26380/26382/g' sentinel-26380.conf > sentinel-26382.conf
启动 哨兵前
先启动我们之前的主从
cd /home/redis-5.0.5/msconf
redis-server redis-master.conf
cd /home/redis-5.0.5/msconf
redis-server redis-slave1.conf
cd /home/redis-5.0.5/msconf
redis-server redis-slave2.conf
再依次启动三个哨兵:
cd /home/redis-5.0.5/sentinelconf
redis-sentinel sentinel-26380.conf
cd /home/redis-5.0.5/sentinelconf
redis-sentinel sentinel-26381.conf
cd /home/redis-5.0.5/sentinelconf
redis-sentinel sentinel-26382.conf
查看是否启动了一个方法
测试哨兵模式
我们先把主 - 6380
给关闭,ctrl+c
就可以关闭的,当我把主 - 6380
关闭的时候从 - 6381 和 从 - 6382
控制台连接不上了,出现错误了,在等一下我们的哨兵就会出现作用的
去到我们哨兵的控制台后,等了一会我发现1-26380,2-23681,3-23682
等哨兵,已经开始工作了,也推举出来了
问答时间:如果6380回来了,还是不是老大?
答:不是
集群配置
⾼可⽤:保证redis⼀直处于可⽤状态,即时出现了故障也有备⽤⽅案保证可⽤性
⾼并发:⼀个redis实例已经可以⽀持多达11w并发读操作或者8.1w并发写操作;但是如果对于有更⾼并发需 求的应⽤来说,我们可以通过
读写分离
、集群配置
来解决⾼并发问题
Redis集群
- Redis集群中每个节点是对等的,⽆中⼼结构
- 数据按照slots分布式存储在不同的redis节点上,节点中的数据可共享,可以动态调整数据的分布
- 可扩展性强,可以动态增删节点,最多可扩展⾄1000+节点
- 集群每个节点通过主备(哨兵模式)可以保证其⾼可⽤性
集群搭建
进入文件夹,并且创建文件夹
cd /home/redis-5.0.5
mkdir cluster-conf
创建文件
cat redis.conf | grep -v "#"|grep -v "^$" > cluster-conf/redis-7001.conf
cd /home/redis-5.0.5/cluster-conf/
编辑文件
vim redis-7001.conf
# bind 127.0.0.1
protected-mode no
port 7001
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 15000
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile /var/run/redis_7001.pid
loglevel notice
logfile ""
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump_7001.rdb
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no
appendfilename "appendonly_7001.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
拷⻉6个⽂件,端⼝分别为 7001-7006
cd /home/redis-5.0.5/cluster-conf
sed 's/7001/7002/g' redis-7001.conf > redis-7002.conf
sed 's/7001/7003/g' redis-7001.conf > redis-7003.conf
sed 's/7001/7004/g' redis-7001.conf > redis-7004.conf
sed 's/7001/7005/g' redis-7001.conf > redis-7005.conf
sed 's/7001/7006/g' redis-7001.conf > redis-7006.conf
启动
cd /home/redis-5.0.5/cluster-conf
redis-server redis-7001.conf &
redis-server redis-7002.conf &
redis-server redis-7003.conf &
redis-server redis-7004.conf &
redis-server redis-7005.conf &
redis-server redis-7006.conf &
ps -ef | grep redis
测试集群
创建集群
redis-cli --cluster create 192.168.88.142:7001 192.168.88.142:7002 192.168.88.142:7003 192.168.88.142:7004 192.168.88.142:7005 192.168.88.142:7006 --cluster-replicas 1
redis有一个哈希槽的概念,默认是 16384
个
连接集群
redis-cli -p 7001 -c
查看集群状态
redis-cli --cluster info 192.168.88.142:7001
平衡节点的数据槽数
redis-cli --cluster rebalance 192.168.88.142:7001
迁移节点槽
redis-cli --cluster reshard 192.168.88.142:7001
删除节点
redis-cli --cluster del-node 192.168.88.142:7001 4cc04a0f3c3deb3a77db79d8a2e11d30988e041b
添加节点
可以看到 7001不在了,所以我们在把他起来,在加入集群
redis-cli --cluster add-node 192.168.88.142:7001 192.168.88.142:7002
出现问题,那就解决问题:
解决方法:
1、将需要新增的节点下aof、rdb等本地备份文件删除,同时将新Node的集群配置文件删除,
即:删除你redis.conf里面cluster-config-file所在的文件;
rm nodes-7001.conf
rm dump_7001.rdb
启动
redis-server redis-7001.conf &
再次添加新节点如果还是报错,则登录新Node对数据库进行清除
FLUSHDB
清空完之后再次执行命令
redis-cli --cluster add-node 192.168.88.142:7001 192.168.88.142:7002
我有认真的看了一下为什么我还是出现问题,我们看集群信息才知道,7001这个,已经加入了
SpringBoot应⽤连接集群
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置集群节点
spring:
redis:
cluster:
nodes: 192.168.88.142:7001,192.168.88.142:7002,192.168.88.142:7003
max-redirects: 3
操作集群
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RedisDemo3Application.class)
class RedisDemo3ApplicationTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void contextLoads() {
//stringRedisTemplate.boundValueOps("key1").set("value1");
String s = stringRedisTemplate.boundValueOps("key1").get();
System.out.println(s);
}
}
Redis淘汰策略
Redis是基于内存结构进⾏数据缓存的,当内存资源消耗完毕,想要有新的数据缓存进来,必然要从Redis的 内存结构中释放⼀些数据。如何进⾏数据的释放呢?---- Redis的淘汰策略
Redis提供的8中淘汰策略
- volatile-lru —> 从设置了过期时间的数据中淘汰最久未使⽤的数据.
- allkeys-lru —> 从所有数据中淘汰最久未使⽤的数据.
- volatile-lfu —> 从设置了过期时间的数据中淘汰使⽤最少的数据.
- allkeys-lfu —> 从所有数据中淘汰使⽤最少的数据.
- volatile-random —> 从设置了过期时间的数据中随机淘汰⼀批数据.
- allkeys-random —> 从所有数据中随机淘汰⼀批数据.
- volatile-ttl —> 淘汰过期时间最短的数据.
- noeviction —> 不淘汰任何数据,当内存不够时直接抛出异常.