封面型网站怎么做的,最好的网站设计,网站建设详细讲解 狐灵,进入公众号显示网络异常目录
一、Mysql与Redis同步数据是否存在延迟呢#xff1f;
二、如何保证一致性#xff1f;
2.1、第一种方式#xff1a;手动编码
2.2、第二种方式#xff1a;MQ异步更新
2.3、第三种方式#xff1a;binlog同步数据
2.4、第四种方式#xff1a;双写一致性
2.5、第五…目录
一、Mysql与Redis同步数据是否存在延迟呢
二、如何保证一致性
2.1、第一种方式手动编码
2.2、第二种方式MQ异步更新
2.3、第三种方式binlog同步数据
2.4、第四种方式双写一致性
2.5、第五种方式使用Redis的事务支持
三、总结 一、Mysql与Redis同步数据是否存在延迟呢
数据同步过程中会存在短暂的延迟这属于正常的现象在分布式架构中很难实现数据强一致性但你不能延迟太久。弱一致性 主从之间数据允许不一致性强一致性 主从之间数据必须一致性 如果实现 成本是非常高会设计到一些锁的技术。最终一致性短暂的数据延迟是允许的但是最终数据是需要一致。在分布式中做数据同步需要经过网络传输的网络传输数据需要一定的时间所以数据短暂的延迟是允许的但是最终数据一定达成一致。 延迟是很难避免的优化 减少延迟的时间。 公司中数据 同步延迟 优化在10-30毫秒。 二、如何保证一致性
2.1、第一种方式手动编码
更新mysql数据再手动清除 Redis 缓存 之后再请求的时候因为Redis没有缓存了所以重新查询数据库最新的数据再手动同步到Redis中。
这种方式可以实现但效率不高。
耦合性太大因为是同步当请求redis没有缓存时查询数据库数据存到redis中那么在缓存到redis过程中redis宕机了那么必然会触发重试机制导致影响接口的响应速度。 2.2、第二种方式MQ异步更新 首先用户发布请求去更新数据我们先更新Mysql数据库更新成功后再采用异步的形式投递消息放到我们的MQ中间件中然后通过MQ的消费者去订阅我们的MQ的主题获取到消息再异步去同步到Redis中。
// 更新MySQL
userMapper.update(user);
// 发送消息
rabbitTemplate.convertAndSend(updateUser, user.getId());
然后在消息消费者中更新Redis。RabbitListener(queues updateUser)
public void updateUser(String userId) {User user userMapper.selectById(userId);redisTemplate.opsForValue().set(user_ user.getId(), user);
}优点具有解耦性。
缺点延迟概率很大。如果我们的MQ消费者没有及时获取到消息那么也就不会及时的更新Redis导致Mysql和Redis数据不一致而且MQ也可能因为各种原因丢失消息。 2.3、第三种方式binlog同步数据
订阅 mysql binlog 文件 异步的形式同步到 redis 中canal 框架。
首先我们要知道Mysql集群的原理 假如说现在的集群是一主一从一般主节点用来做增删改操作从节点用来做查询操作。从节点订阅主节点当主节点的数据发生改变时会给从节点发送binlog文件从节点通过binlog文件进行数据更新同步。
那么同理我们Mysql与Redis的一致性能不能也这么做呢 我们使用canalserver端来伪装mysql从节点订阅mysql主节点。
优点前两种方式都是业务层面上编码去同步数据当我们绕过代码手动从数据库直接改数据那么就无法同步。使用binlog方式是全局的方式即使应用程序崩溃也不会丢失binlog因此能够保证最终的数据一致性。
缺点canalserver端需要暴露出ip和端口号然后我们单独的项目再连接canalserver端意味着如果我们的项目是单机版本的话同步的效率并不高因为是单个线程去同步的。
假如我现在高并发的环境下写数据到mysql主每秒写个几万次我们只有一个单独的项目连接canalserver端那么每次只能写一条到redis那这个效率可太低了。 2.4、第四种方式双写一致性
首先什么是双写
先更新数据库再同步更新redis这就是双写。
但是这种情况会有个很严重的bug就是在多线程的情况下会导致数据不一致。
比如有t1和t2两个线程现在redis和mysql的数据都为clay
t1线程更新DBt2线程也更新DB但由于update操作一般条件都带着主键id所以具有行锁t1更新时t2在阻塞。t1线程更新DB数据改为zhangsan并释放行锁。t2线程获取行锁开始更新DB数据改为lisi这时DB数据为lisi。t2线程继续更新redis缓存数据位lisi。t1线程更新redis缓存数据为zhangsan这时redis数据为zhangsan。
从步骤上来看此时redis与mysql数据不一致。
那么如何解决呢
可以使用分布式锁当然如果你要是单机版本项目使用synchronized也可以。
分布式锁解决多个线程同时执行双写业务逻辑最终只会有一个获取到分布式锁线程才可以执行没有获取到分布式锁线程则阻塞等待这样确保线程执行双写不会被其他线程干扰但是效率非常低。 2.5、第五种方式使用Redis的事务支持
Redis提供了事务Transaction支持可以将一系列的操作作为一个原子操作执行。我们可以利用Redis的事务来实现MySQL和Redis的原子更新。
redisTemplate.execute(new SessionCallbackObject() {Overridepublic Object execute(RedisOperations operations) throws DataAccessException {// 开启事务operations.multi();// 更新MySQLuserMapper.update(user);// 更新Redisoperations.opsForValue().set(user_ user.getId(), user);// 执行事务operations.exec();return null;}
});使用Redis事务可以确保MySQL和Redis的更新在同一事务中执行避免了中间出现不一致的情况。但需要注意的是Redis的事务并非严格的ACID事务可能存在部分成功的情况。 三、总结
根据具体的业务需求和系统环境选择合适的方案可以提高数据一致性的可靠性。然而每种方案都有其优缺点和适用场景需要综合考虑权衡。