其他
亿级流量高并发下,缓存与数据库不一致,咋办?
脚本之家
你与百万开发者在一起
相信只要是个稍微像样点的互联网公司,或多或少都有自己的一套缓存体系。
只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,遂笔者想在这和大家聊一聊:如何解决一致性问题?
如何保证缓存与数据库双写一致性,也是现在 Java 面试中面试官非常喜欢问的一个问题!
一般来说,如果允许缓存可以稍微跟数据库偶尔有不一致,也就是说如果你的系统不是严格要求缓存+数据库必须保持一致性的话,最好不要做这个方案。
值得注意的是,串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求(土豪请自觉无视此提醒)。
/**
* 请求异步处理的service实现
* @author Administrator
*
*/
("requestAsyncProcessService")
public class RequestAsyncProcessServiceImpl implements RequestAsyncProcessService {
public void process(Request request) {
try {
// 先做读请求的去重
RequestQueue requestQueue = RequestQueue.getInstance();
Map<Integer, Boolean> flagMap = requestQueue.getFlagMap();
if(request instanceof ProductInventoryDBUpdateRequest) {
// 如果是一个更新数据库的请求,那么就将那个productId对应的标识设置为true
flagMap.put(request.getProductId(), true);
} else if(request instanceof ProductInventoryCacheRefreshRequest) {
Boolean flag = flagMap.get(request.getProductId());
// 如果flag是null
if(flag == null) {
flagMap.put(request.getProductId(), false);
}
// 如果是缓存刷新的请求,那么就判断,如果标识不为空,而且是true,就说明之前有一个这个商品的数据库更新请求
if(flag != null && flag) {
flagMap.put(request.getProductId(), false);
}
// 如果是缓存刷新的请求,而且发现标识不为空,但是标识是false
// 说明前面已经有一个数据库更新请求+一个缓存刷新请求了,大家想一想
if(flag != null && !flag) {
// 对于这种读请求,直接就过滤掉,不要放到后面的内存队列里面去了
return;
}
}
// 做请求的路由,根据每个请求的商品id,路由到对应的内存队列中去
ArrayBlockingQueue<Request> queue = getRoutingQueue(request.getProductId());
// 将请求放入对应的队列中,完成路由操作
queue.put(request);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取路由到的内存队列
* @param productId 商品id
* @return 内存队列
*/
private ArrayBlockingQueue<Request> getRoutingQueue(Integer productId) {
RequestQueue requestQueue = RequestQueue.getInstance();
// 先获取productId的hash值
String key = String.valueOf(productId);
int h;
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
// 对hash值取模,将hash值路由到指定的内存队列中,比如内存队列大小8
// 用内存队列的数量对hash值取模之后,结果一定是在0~7之间
// 所以任何一个商品id都会被固定路由到同样的一个内存队列中去的
int index = (requestQueue.queueSize() - 1) & hash;
System.out.println("===========日志===========: 路由内存队列,商品id=" + productId + ", 队列索引=" + index);
return requestQueue.getQueue(index);
}
}
Cache Aside Pattern
最初级的缓存不一致问题及解决方案
比较复杂的数据不一致问题分析
读请求长时阻塞
读请求并发量过高
多服务实例部署的请求路由
热点商品路由问题导致请求倾斜
作者:中华石杉,多年BAT架构经验倾囊相授。个人微信公众号:石杉的架构笔记(ID:shishan100)
更多精彩
在公众号后台对话框输入以下关键词
查看更多优质内容!
女朋友 | 大数据 | 运维 | 书单 | 算法
大数据 | JavaScript | Python | 黑客
AI | 人工智能 | 5G | 区块链
机器学习 | 数学 | 留言送书
觉得不错,请把这篇文章分享给你的朋友
转载 / 投稿请联系:Panda-nian
更多精彩,点击菜单栏“文章”进行查看
● 马化腾凌晨4点看产品,马云一年飞行1000小时:成年人的牛逼谈何容易