设为首页 加入收藏

TOP

Redis面试题(七)
2023-09-23 15:44:12 】 浏览:215
Tags:Redis
it.SECONDS); } finally { updateProductLock.unlock(); }finally { hotCacheCreateLock. unlockO); } return product; }
public Product update(Product product) {
	Product productResult = null;
	RLock updateProductLock = redisson.getLock( name LOCK_PROOUCT_UPOATE_PREFIX + product.getId())updateProductLock.lock();
	try {
		productResult =productDao. update(product);
		redisUtil.set( key:.RedisKeyPrefixConst.PROOUCT_CACHE + productResult.getTd(),JSOM . to3SONString(productResult)
		genProductCacheTimeout(),TimeUnit.SECONDS);
	}finally {
		updateProductLock.unloek(;
	}
	return productResult;
}

优化性能

大部分场景读多写少

  • 基于读写锁做优化。

对同一个key加两个锁,一个读锁一个写锁,都是读的时候可以并行执行,有写操作的时候加写锁,和读锁互斥,读的线程都要等待

RReadWriteLock readWIiteLock = 
	nedisson.getReadlWIniteLock(LOCK_PRODUCT_UPDATE_PREFIX + productIo);
RLock writeLock = readWriteLock.writeLock();

  • tryLock(time,unit);
    • time是最大等待锁的时间,超过这个时间就不等了
    • unit是时间单位

time秒之后就不等了

利用多级缓存架构解决缓存雪崩问题

可以用jvm缓存,比如hashmap

public static HashMap<String,Product>  productMap=new HashMap<>();
private Product getProductFromCache(String productCacheKey) {
    Product product = null;
    product = productMap.get(productCacheKey);if (product != null) {
    
    return product;
	}
	// 逻辑代码:使用redis缓存再查数据
}

mysql和redis怎么保持数据一致性

一般情况下,Redis用来实现应用和数据库之间读操作的缓存层,主要目的是减少数据库IO,还可以提升数据的IO性能。
当应用程序需要去读取某个数据的时候,首先会先尝试去 Redis里面加载,如果命中就直接返回。如果没有命中,就从数据库查询,查询到数据后再把这个数据缓存到Redis里面。
在这样一个架构中,会出现一个问题,就是一份数据,同时保存在数据库和 Redis里面,当数据发生变化的时候,需要同时更新Redis和 Mysql,由于更新是有先后顺序的,并且它不像Mysql中的多表事务操作,可以满足ACID特性。所以就会出现数据一致性问题。
在这种情况下,能够选择的方法只有几种。

  • 先更新数据库,再更新缓存
  • 先删除缓存,再更新数据库

如果先更新数据库,再更新缓存,如果缓存更新失败,就会导致数据库和 Redis中的数据不一致。
如果是先删除缓存,再更新数据库,理想情况是应用下次访问Redis 的时候,发现Redis里面的数据是空的,就从数据库加载保存到Redis里面,那么数据是一致的。但是在极端情况下,由于删除Redis和更新数据库这两个操作并不是原子的,所以这个过程如果有其他线程来访问,还是会存在数据不一致问题。
所以,如果需要在极端情况下仍然保证Redis和 Mysql的数据一致性,就只能采用最终一致性方案。
比如基于RocketMQ的可靠性消息通信,来实现最终一致性。
还可以直接通过Canal组件,监控Mysql中 binlog 的日志,把更新后的数据同步到Redis 里面。
因为这里是基于最终一致性来实现的,如果业务场景不能接受数据的短期不一致性,那就不能使用这个方案来做。
以上就是我对这个问题的理解。

RocketMQ是怎么实现数据一致性的

在面过的几家大厂中,几乎每轮的面试官(没写错,几乎是每轮面试官)都问了同样一个问题:你们的系统是分布式的系统吗?
答:是。
面试官:那么你们分布式的系统是如何解决分布式事务这个问题的呢?也就是如何保证数据的一致性。
答:我们的系统中通过 RocketMQ 的事务消息来保证数据的最终一致性。
面试官:那你说说它是如何来保证数据的最终一致性的?
答:分两部分来回答,第一部分先回答事务消息的实现流程,第二部分解释为什么它能保证数据的最终一致性。

事务消息的实现流程
image.png

  1. 首先服务 A 发送一个半事务消息(也称 **half **消息)至 MQ 中。
  • 为什么要先发送一个 half 消息呢?这是为了保证服务 A 和 MQ 之间的通信正常,如果无法正常通信,则服务 A 可以直接返回一个异常,也就不用处理后面的逻辑的了。
  1. 如果 half 消息发送成功,MQ 收到这个 half 消息后,会返回一个 success 响应给服务 A。

  2. 服务 A 接收到 MQ 返回的 success 响应后,开始处理本地的业务逻辑

  3. 提交/commit本地事务

  • 如果服务 A 本地事务提交成功,则会向 MQ 中发送 commit,表示将 half 消息提交,MQ 就会执行第 5 步操作;
  • 如果服务 A 本地事务提交失败,则直接回滚本地事务,并向 MQ 中发送 rollback,表示将之前的 half 消息进行回滚,MQ 接收到 rollback 消息后,就会将 half 消息删除。
  1. MQ 如果 commit,则将 half 消息写入到磁盘。.
  • 如果 MQ 长时间没有接收到 commit 或者 rollback 消息,例如:服务 A 在处理本地业务时宕机了,或者发送的 commit、rollback 因为在弱网环境,数据丢失了。那么 MQ 就会在一定时间后尝试调用服务 A 提供的一个接口,通过这个接口来判断 half 消息的状态。所以服务 A 提供的接口,需要实现的业务逻辑是:通过数据库中对应数据的状态来判断,之前的 half 消息对应的业务是否执行成功。如果 MQ 从这个接口中得知 half 消息执行成功了,那么 MQ 就会将 half 消息持久化到本地磁盘,如果得知没有执行成功,那么就会将 half 消息删除。
  1. 服务 B 从 MQ 中消费到对应的消息。
  2. 服务 B 处理本地业务逻辑,然后提交本地事务。

如何保证数据的最终一致性
实现流程说完了,可能你现在有各种各样的疑惑?

Q: half 消息是个啥?
A: 它和我们正常发送的普通消息是一样的,都是存储在 MQ 中,唯一不同的是 half 在 MQ 中不会立马被消费者消费到,除非这个 half 消息被 commit 了。(至于为什么未 commit 的 half 消息无法被消费者读取到,这是因为在 MQ 内部,对于事务消息而言,在 commit 之前,会先放在一个内部队

首页 上一页 4 5 6 7 下一页 尾页 7/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Spring 多线程的事务处理 下一篇主动写入流对@ResponseBody注解的..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目