使用关系型数据库会频繁的进行I/O操作,Redis作为Nosql数据库,所有的数据都存放在运行内存中,减少对磁盘的访问。Redis数据存储结构类似哈希表结构,队列元素固定16384个,称之为槽。业务需求通常使用mysql结合redis进行边路缓存机制。
〇、redis的几种数据类型
虽然数据类型有很多种,但是使用redis时一般key和value都直接使用Json格式的String类型存储。所以重点只看设置String类型的调用方法。
String:
set
get
setex 设置key存活时间
setnx 锁
Set
Sorted Set
Hash表
List
Stream
一、 两种储存机制:RDB和AOF
RDB:快照
rdb是redis默认的存储机制;
触发规则后将内存数据存储到硬盘的.rdb文件中:
save 900 1
save 300 10
save 60 10000这个规则是指每900秒内至少1次修改,每300s至少10次修改......
在意外情况发生时会丢失最后一次触发条件存储之后的所有内存数据
AOF:日志
aof默认关闭,当打开aof时,默认优先级比rdb高;
aof是指将redis执行的修改命令记录到硬盘的以.aof为后缀的日志文件中,redis服务异常关闭可以调用日志命令重新执行一遍,恢复到上一次redis数据库发生异常时的所有数据状态。
二、spring框架整合redis
0)redis项目结构
config目录 配置redis模板类对象
dao目录 封装对redis方法的调用
1)添加依赖文件并在springBoot配置中修改redis的主机地址ip,端口号port默认6379
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.11</version> </parent> <dependencies> <!--redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--redis启动器依赖--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> </dependencies>
2)创建配置类RedisConfig
2.0)redisTemplate对象
内部封装了序列化器对象,redis连接工厂对象。
调用此对象可以方便的操作redis中的数据
2.1) 实现对redisTemplate对象下的序列化器对象配置
使用redis中直接存储数据是该数据二进制字节码,如果再直接获取该数据也是二进制字节码。
这里我不使用序列化器存储键值对
需要使用序列化器转换 :
rediskey的序列化器通常使用StringRedisSerializer
redisValue的序列化器通常使用GenericJackson2JsonRedisSerializer
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
3)封装redis的调用函数接口RedisDao。
public interface RedisDao {
<T> void setObj(String key,T value);
<T> void setObjAndTime(String key,T value, int expire, TimeUnit timeUnit);
<T> boolean setIfAbsent(String key,T value);
<T> boolean setIfAbsent(String key,T value, int expire, TimeUnit timeUnit);
<T> T getObj(String key);
boolean expire(String key,int expire, TimeUnit timeUnit);
Long getExpire(String key);
boolean delKey(String key);
boolean exists(String key);
}
4)接口实现类RedisDaoImpl
@Repository
public class RedisDaoImpl implements RedisDao {
@Autowired
private RedisTemplate redisTemplate;
@Override
public <T> void setObj(String key, T value) {
redisTemplate.opsForValue().set(key,value);
}
@Override
public <T> void setObjAndTime(String key, T value, int expire, TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key,value,expire,timeUnit);
}
@Override
public <T> boolean setIfAbsent(String key, T value) {
return redisTemplate.opsForValue().setIfAbsent(key,value);
}
@Override
public <T> boolean setIfAbsent(String key, T value, int expire, TimeUnit timeUnit) {
return redisTemplate.opsForValue().setIfAbsent(key,value,expire,timeUnit);
}
@Override
public <T>T getObj(String key) {
return (T) redisTemplate.opsForValue().get(key);
}
@Override
public boolean expire(String key, int expire, TimeUnit timeUnit) {
return redisTemplate.expire(key,expire,timeUnit);
}
@Override
public Long getExpire(String key) {
return redisTemplate.getExpire(key);
}
@Override
public boolean delKey(String key) {
return redisTemplate.delete(key);
}
@Override
public boolean exists(String key) {
return redisTemplate.hasKey(key);
}
}
5)测试一下
三、主从复制
一个主redis有多个从redis,每个从redis也可以作为主,也就是从也可以有从
主具备写的能力;
从只具备读的能力;
四、哨兵
单哨兵:
用来监视所有redis服务器是否正常运行,主噶了,哨兵 乒没有响应会直接将从变为主。
实际开发中会配置多哨兵
五常见redis问题
5.1 脑裂:
有时候因为网络延时,哨兵没有及时接到 主服务器的 乓,造成主服务器假死,这时候需要用不同网段的多个哨兵共同监视。当哨兵检测到主死亡达到一定数量时,哨兵间会发起投票,切换主服务器。
5.2 缓存穿透
访问不存在的数据,多次请求关系型数据库mysql而不是缓存数据库redis
5.3 缓存击穿
访问数据有效期失效,多次请求关系型数据库mysql而不是缓存数据库redis
5.4 缓存雪崩
访问数据大量失效,多次请求关系型数据库mysql而不是缓存数据库redis
5.5 边路缓存机制
删除数据比更新数据简单和有效。
先删除缓存,再更新缓存。避免出现还未更新的垃圾数据被其他人读到
六 Redis淘汰策略 :
1)淘汰时机
消极
读取数据时检测到过期的删除
积极
周期性检测过期数据
主动
内存不够,主动删除
2)淘汰算法
LRU
最近使用过的数据不容易删除
LFU
没怎么访问的数据删除掉
FIFO
先进先出
七、在linux中模拟一个假集群
1)创建模板文件
cd /usr/local mkdir redis-cluster cd redis-cluster vim redis-cluster.tmpl
2)粘贴模板文件信息,修改ip
port ${PORT} # 设置外部网络连接redis服务 protected-mode no # 开启集群 cluster-enabled yes # Redis群集节点每次发生更改时自动保留群集配置(基本上为状态)的文件 cluster-config-file nodes.conf # 节点超时时间 cluster-node-timeout 5000 # 当前IP地址 cluster-announce-ip 192.168.146.130 cluster-announce-port ${PORT} cluster-announce-bus-port 1${PORT} # 持久化方式 appendonly yes
3)使用shell创建6个目录
for port in `seq 7000 7005`; do \ mkdir -p ./${port}/conf \ && PORT=${port} envsubst < ./redis-cluster.tmpl > ./${port}/conf/redi s.conf \ && mkdir -p ./${port}/data; \ done
4)创建桥接网路
docker network create redis-net docker network ls
5)创建并启动6个容器
for port in `seq 7000 7005`; do \ docker run -d -ti -p ${port}:${port} -p 1${port}:1${port} \ -v /usr/local/redis-cluster/${port}/conf/redis.conf:/usr/local/etc/re dis/redis.conf \ -v /usr/local/redis-cluster/${port}/data:/data \ --name redis-${port} --net redis-net \ --sysctl net.core.somaxconn=1024 redis:6.2.6 redis-server /usr/local/ etc/redis/redis.conf; \ done
6)查看容器信息
docker inspect redis-7000 redis-7001 redis-7002 redis-7003 redis-7004 r edis-7005 | grep IPAddress
7)创建集群
docker exec -it redis-7000 bash
redis-cli --cluster create \ 172.18.0.2:7000 \ 172.18.0.3:7001 \ 172.18.0.4:7002 \ 172.18.0.5:7003 \ 172.18.0.6:7004 \ 172.18.0.7:7005 \ --cluster-replicas 1
8)进入集群
redis-cli -c -p 7000