安装
cd /home/software
解压
tar -zxvf redis-6.2.6.tar.gz
安装gcc编译环境
yum install gcc-c++
安装redis
cd /home/software/redis-6.2.6
make && make install
配置redis
设置开机自启动
utils下,拷贝redis_init_script到/etc/init.d目录,目的要把redis作为开机自启动
cd /home/software/redis-6.2.6/utils
cp redis_init_script /etc/init.d/
设置配置文件目录
创建 /usr/local/redis,用于存放配置文件
mkdir /usr/local/redis
拷贝redis配置文件
拷贝到 /usr/local/redis 下
cp redis.conf /usr/local/redis
修改核心配置文件
- 修改redis.conf这个核心配置文件,修改 daemonize no -> daemonize yes,目的是为了让redis启动在linux后台运行
- 修改redis的工作目录,建议修改为: /usr/local/redis/working,名称随意(创建working目录)
mkdir /usr/local/redis/working
- 修改如下内容,绑定IP改为 0.0.0.0 ,代表可以让远程连接,不收ip限制
- 为redis启动脚本添加执行权限,随后运行启动redis
cd /home/software/redis-6.2.6/utils
chmod 777 redis_init_script
./redis_init_script start
- 检查redis进程
ps -ef | grep redis
设置redis开机自启动
设置redis开机自启动,修改 redis_init_script,添加如下内容
#chkconfig: 22345 10 90
#description: Start and Stop redis
随后执行如下操作:
chkconfig redis_init_script on
重启服务器(虚拟机)后,再看进程:
进入redis
新开一个命令窗口,相当于起一个客户端请求服务端
cd /home/software/redis-6.2.6/src
./redis-cli -p 6379
缓存数据,减少mysql压力
发布订阅
持久化
RDB
RDB:每隔一段时间,把内存中的数据写入磁盘的临时文件,作为快照,恢复的时候把快照文件读进内存中。如果宕机重启,那么内存里的数据肯定会没有的,那么再次启动redis后,则会恢复。
修改配置
vim /usr/local/redis/redis.conf
重启redis
/etc/init.d/redis_init_script stop
/etc/init.d/redis_init_script start
AOF
介绍
1、引子
RDB会丢失最后一次备份的rdb文件,但是其实也无所谓,其实也可也以忽略不计,毕竟是缓存丢了就丢了,但是如果追求数据的完整性,那么就得考虑使用AOF了。
2、AOF特点
1.以日志的形式来记录用户请求的写操作。读操作不记录,因为写操作会存储。
2.文件已追加的形式而不是修改的形式。
3.redis的aof恢复其实就是把追加的文件从开始到结尾读取执行写操作。
3、优势
1.AOF更加耐用,可以以秒级别为单位备份,如果发生问题,也只会丢失最后一秒的数据,大大增加了可靠性和数据完整性。所以AOF可以每秒备份一次,使用fsync操作。
2.以log日志形式追加,如果磁盘满了,会执行redis-check-aof工具。
3.当数据太大的时候,redis可以在后台自动重新AOF。当redis继续把日志追加到老的文件中去时,重写也是非常安全的,不会影响客户端的读写操作。
4.AOF日志包含的所有写操作,会更加便于redis的解析恢复。
4、劣势
1.相同的数据,同一份数据。AOF比RDB大
2.针对不同的同步机制,AOF会比RDB慢,因为AOF每秒都会备份做写操作,这样相对于RDB来说就略低。每秒备份fsync就没问题,但是如果客户端的每次写入就做一次备份fsync,那么redis的性能就会下降。
3.AOF发生过bug,就是数据恢复的时候数据不完整,这样显得AOF会比较脆弱,容易出现bug,因为AOF没有RDB那么简单,但是为了反之bug的产生,AOF就不会根据旧的指令去重构,而是根据当时缓存中的数据指令去重构,这样就更加健壮和可靠了
案例
修改配置
//vim /usr/local/redis/redis.conf
把no修改为yes
重启redis,自动生成aof文件
演示
- 生成一些key,然后删除
- 停止redis服务器
- 删除aof文件最后一行
- 重启redis服务器
- 查看key是否恢复
步骤1
步骤2
/etc/init.d/redis_init_script stop
步骤3
步骤4
/etc/init.d/redis_init_script start
步骤5
数据重新恢复
总结
到底采用RDB还是AOF呢?
1.如果你能接受一段时间的缓存丢失,那么就可以使用RDB
2.如果你对实时性的数据比较在意,那么就用AOF
3.使用RDB和AOF结合一起做持久化,RDB做冷备,可以在不同时期对不同版本做恢复,AOF做热备,保证数据仅仅只有1秒的损失。当AOF破损不可用了,那么再用RDB恢复,这样就做到了两者的相结合,也就是说redis恢复会先加载AOF,如果AOF有问题会加载RDB,这样就达到了冷热备份的目的。
主从复制(读写分离)
创建1主2从虚拟机
配置
从节点
//vim /usr/local/redis/redis.conf
增加mastart节点和端口号
info replicatio
重启redis,并查看
设置key,查看是否同步
从节点禁止重新设置值
哨兵模式
1、引子
Master挂了,如何保证可用性,实现继续读写
2、什么是哨兵
Sentinel(哨兵)是用于监控Redis集群中Master状态的工具;是Redis高可用解决方案;哨兵可以监视一个或者多个redis master服务,以及这些master的所有从服务;当某个master服务宕机后,会把这个master下的某个服务升级为master来替代已宕机的master继续工作。
操作
复制sentinel.conf到指定目录
cp sentinel.conf /usr/local/redis/
修改sentinel.conf
启动三台虚拟机
master
cd /usr/local/redis/
redis-sentinel sentinel.conf
总结
master挂了以后,由于哨兵监控,剩余slave会进行选举,选举后其中一个成为master,当原来的master恢复后,它会成为slave
缓存击穿
缓存穿透: 查询的key在redis中不存在,对应的id在数据库也不存在,此时被非法用户进行攻击,大量的请求会直打在db上,造成宕机,从而影响整个系统,
解决方案:把空的数据也缓存起来,比如空字符串,空对象,空数组或list
解决前
List<CategoryVO> list = new ArrayList<>();
String catsStr = redisOperator.get("subCat:" + rootCatId);
if (StringUtils.isBlank(catsStr)) {
list = categoryService.getSubCatList(rootCatId);
if (list != null && list.size() > 0) {
redisOperator.set("subCat:" + rootCatId, JsonUtils.objectToJson(list));
}
} else {
list = JsonUtils.jsonToList(catsStr, CategoryVO.class);
}
return IMOOCJSONResult.ok(list);
正常情况,数据库中真实存在这条数据,redis会做缓存
不正常情况,数据库中不存在这条数据,redis发生缓存穿透
解决后
List<CategoryVO> list = new ArrayList<>();
String catsStr = redisOperator.get("subCat:" + rootCatId);
if (StringUtils.isBlank(catsStr)) {
list = categoryService.getSubCatList(rootCatId);
if (list != null && list.size() > 0) {
redisOperator.set("subCat:" + rootCatId, JsonUtils.objectToJson(list));
} else {
redisOperator.set("subCat:" + rootCatId, JsonUtils.objectToJson(list), 5*60);
}
} else {
list = JsonUtils.jsonToList(catsStr, CategoryVO.class);
}
return IMOOCJSONResult.ok(list);
缓存穿透之布隆过滤器
缓存雪崩
批量的key同时失效,全部请求都到数据库,导致崩溃
批量查询key
代码for循环
public String get(String key) {
return (String) redisTemplate.opsForValue().get(key);
}
@GetMapping("/getALot")
public Object getALot(String... keys) {
List<String> resutl = new ArrayList<>();
for (String k:keys) {
resutl.add(redisOperator.get(k));
}
return resutl;
}
redis原生批量查询
public List<String> mget(List<String> keys) {
return redisTemplate.opsForValue().multiGet(keys);
}
@GetMapping("/mget")
public Object mget(String... keys) {
List<String> keysList = Arrays.asList(keys);
return redisOperator.mget(keysList);
}
pipeline管道批量查询
public List<Object> batchGet(List<String> keys) {
// nginx -> keepalive
// redis -> pipeline
List<Object> result = redisTemplate.executePipelined(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisConnection src = (StringRedisConnection) connection;
for (String k : keys) {
src.get(k);
}
return null;
}
});
return result;
}
@GetMapping("/batchGet")
public Object batchGet(String... keys) {
List<String> keysList = Arrays.asList(keys);
return redisOperator.batchGet(keysList);
}
总结
- multiGet只能获取批量string,pipeline支持类型更丰富
- pipeline类似keeplive