redis是什么
开源高性能键值对的内存数据库,可以用来做数据库、缓存、消息中间件等场景,是一种NoSQL(not-only sql,非关系型数据库)的数据库
使用方法
数据缓存是Redis最重要的一个场景,为缓存而生,在springboot中,一般有两种使用方式:
-
直接通过RedisTemplate使用
-
通过Spring Cache集成Redis(也就是注解的方式)
可能遇到的问题
数据一致性
分布式环境下,缓存和数据库容易出现数据一致性问题
缓存雪崩
超量请求导致缓存宕机,堆积的超量请求导致宕机服务器重启后立宕机
缓存穿透
大面积请求缓存和数据库中都没有的数据,数据库挂掉
缓存击穿
单个热点数据请求非常频繁,处于集中式访问现象,key失效(过期)时,大量的请求击穿了缓存
redis淘汰策略
redis持久化
Redis 持久化策略有两种:
-
RDB:快照形式是直接把内存中的数据保存到一个 dump 的文件中,定时保存,保存策略。
-
AOF:把所有的对 Redis 的服务器进行修改的命令都存到一个文件里,命令的集合。
-
Redis 默认是快照 RDB 的持久化方式。
如果非常关心你的数据,但仍然可以承受数分钟内的数据丢失,那么可以额只使用 RDB 持久。
AOF 将 Redis 执行的每一条命令追加到磁盘中,处理巨大的写入会降低Redis的性能,不知道你是否可以接受。
数据库备份和灾难恢复:定时生成 RDB 快照非常便于进行数据库备份,并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度快。
当然了,Redis 支持同时开启 RDB 和 AOF,系统重启后,Redis 会优先使用 AOF 来恢复数据,这样丢失的数据会最少。
redis主从复制
-
从节点执行 slaveof[masterIP][masterPort],保存主节点信息。
-
从节点中的定时任务发现主节点信息,建立和主节点的 Socket 连接。
-
从节点发送 Ping 信号,主节点返回 Pong,两边能互相通信。
-
连接建立后,主节点将所有数据发送给从节点(数据同步)。
-
主节点把当前的数据同步给从节点后,便完成了复制的建立过程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。
存在问题
-
一旦主节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。
-
主节点的写能力受到单机的限制。
-
主节点的存储能力受到单机的限制。
-
原生复制的弊端在早期的版本中也会比较突出,比如:Redis 复制中断后,从节点会发起 psync。
-
此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时,可能会造成毫秒或秒级的卡顿。
redis哨兵模式
该系统可以执行以下四个任务:
-
监控:不断检查主服务器和从服务器是否正常运行。
-
通知:当被监控的某个 Redis 服务器出现问题,Sentinel 通过 API 脚本向管理员或者其他应用程序发出通知。
-
自动故障转移:当主节点不能正常工作时,Sentinel 会开始一次自动的故障转移操作,它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点,这样人工干预就可以免了。
-
配置提供者:在 Redis Sentinel 模式下,客户端应用在初始化时连接的是 Sentinel 节点集合,从中获取主节点的信息。
redis应用
生产者
EventPublisher类下看
消息体类型可自定义,建议字段中带key或继承基础消息体
public class InformTimeoutMessage extends BaseMessage implements Serializable {
//消息体,BaseMessage中设置了key字段
@Serial
private static final long serialVersionUID = 9037159925423874938L;
private String bindId;
private String sid;
private String did;
@Override
public String key() {
return bindId;
}
}
同步
import org.apache.rocketmq.spring.core.RocketMQTemplate;
@Component
@RequiredArgsConstructor
public class EventPublisher {
@Getter
private final RocketMQTemplate rocketMQTemplate;
/**
* 同步发送函数
*
* @param topic 主题——一般是约定常量如:MQTopic.CHANGE_INFORM_STATE
* @param messageBody 消息体
* @param delayTime 延迟时间(单位:秒)
* @return 发送结果
*/
public boolean syncSendDelayTimeSeconds(String topic, String messageBody, long delayTime) {
//创建消息
Message<String> message = MessageBuilder.withPayload(messageBody).build();
if (log.isDebugEnabled()) {
log.debug("will be sync send message: {}", message);
}
//发送可延时的同步消息(主题=key,消息体=valued,延迟时间)
SendResult sendResult = rocketMQTemplate.syncSendDelayTimeSeconds(topic, message, delayTime);
return sendStatus(sendResult);
}
}
异步
/**
* 异步回调通用函数
*
* @param topic 主题
* @param messageBody 消息体
* @param timeout 超时时间
*/
public void asyncSendTo(String topic, String messageBody, long timeout) {
//创建消息
Message<String> message = MessageBuilder.withPayload(messageBody).build();
if (log.isDebugEnabled()) {
log.debug("will be async send message: {}", message);
}
// 发送异步消息
rocketMQTemplate.asyncSend(topic, message, new SendCallback() {
// 判断是否成功
@Override
public void onSuccess(SendResult sendResult) {
log.info("{} send success.", message.getPayload());
}
@Override
public void onException(Throwable e) {
log.error("{} send error, com.youyi.exception: {}", message.getPayload(), e);
}
}, timeout <= 0 ? DEFAULT_TIMEOUT : timeout);
}
消费者
@Component
@RequiredArgsConstructor
//通过监听器筛选主题消费
@RocketMQMessageListener(consumerGroup = "CHANGE_INFORM_STATE", topic = MQTopic.CHANGE_INFORM_STATE)
public class BindInformTimeoutConsumer implements RocketMQListener<InformTimeoutMessage> {
private final ModifyBindInformConsumerService service;
//消息格式与生产者消息格式保持一致
@Override
public void onMessage(InformTimeoutMessage message) {
//建议带日志,方便查看是否收到生产消息
log.info("\n绑定信息:{}", GsonUtil.toJson(message));
//消费操作
service.modifyInformTge(message);
}
}
创建redis项目
正常复制项目
配置redis接口和密码(查看密码)
redis操作接口是RedisTemplate,像mongo一样可以封装一下;RedisOps中有