0.0 亿级商品并发响应
- LVS+Nginx 集群,前后分离,静态资源放入Nginx下,网关Nginx限流
- 大数据数据压缩
- 分流限流(防刷,限制每个用户/s请求次数,令牌等,黑白名单),容灾降级(网络容灾,服务降级等等)
- 异步(mq)+并行(多线程)处理
- 多级缓存架构(本地缓存,redis缓存)
- 分库分表,es搜索引擎
- 服务弹性化,多机房多活,监控
- 多种压测方案
1.0 多级缓存架构图
2.0 秒杀库存超卖解决方案
3.0 redis 访问倾斜/数据倾斜解决方案
4.0 redis 缓存与数据库不一致问题
5.0 下单自动取消—saas系统多租户id,取消时间不是统一的,自定义—如果是rabbitmq 可以用插件delayed;
30分钟和1分钟,看各自项目如何设置。如果有些商家设置时间是9999分钟时,过大,这种可以配合下xxljob定时任务,每天执行,如果有当天的任务,就放进rabbitmq里面;—这种看各自服务的设置
6.0 redis缓存穿透
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求。
解决:
1:接口限流与熔断,降级
2:对于像ID为负数或者ID远远超过定义的大小的非法请求数据直接过滤掉
3:存在的id,放入redis布隆过滤器BitMap(假如有几亿条数据,一次性存入里面也不合理,可以根据hash算法模式,存入多个bigMap里面)
4:数据库和缓存都查询不到的情况下,把空值存入到缓存里面,增加短效时间内部
7.0 redis缓存击穿
缓存击穿,加入一个key是热点,当这个key在失效的瞬间,大批量的请求直接请求数据库,
解决:
1、设置热点数据永远不过期。
2、接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些 服务 不可用时候,进行熔断,失败快速返回机制。
3、使用互斥锁,通过redis的setnx实现互斥锁,使得访问进数据库,只有一个请求
8.0 redis 雪崩
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机
解决:
1、缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
2、接口限流与熔断,降级
3、如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
4、设置热点数据永远不过期。
9.0 如何提高调用第三方接口—请求合并
1:假如并发量多的情况,比如A服务调B服务商品,假如A并发量很大,每次请求A就去调B服务,对B服务压力也大,就推荐,A可以队列arrayblockingqueue+线程completablefuture,每隔5ms,去重商品ID,拿到ID集,在请求B服务,对B服务性能就大大提高,也降低网络io请求
2:项目本身也可以这样开发,假如对商品查询要求高,假如每次都进入服务器,增加Redis,mysql 性能压力,可以定时收集商品ID集来做查询;
@Autowired
private ICeShiService ceShiService;
private CountDownLatch countDownLatch = new CountDownLatch(1);
@Test
void contextLoads() {
System.out.println("ssssss");
for (long i = 0; i < 1000L; i++) {
long finalI = i;
Thread thread = new Thread(() -> {
try {
countDownLatch.await();
ceShiService.ceShi(finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
countDownLatch.countDown();
thread.start();
}
try {
Thread.sleep(100000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class CeShiDemo{
Long id;
CompletableFuture<String> completableFuture;
}
ArrayBlockingQueue<CeShiDemo> arrayBlockingQueue = new ArrayBlockingQueue<CeShiDemo>(1024*1024);
Object object = new Object();
@Override
public String ceShi(long id) {
CompletableFuture<String> a = new CompletableFuture();
CeShiDemo ceShiDemo = new CeShiDemo();
ceShiDemo.id = id;
ceShiDemo.completableFuture = a;
try {
arrayBlockingQueue.put(ceShiDemo);
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = "ssss";
try {
s = a.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return s ;
}
@Override
public String ceShi2(long id) {
sleepFF();
return null;
}
private ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
@PostConstruct
public void init(){
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
synchronized(object) {
int size = arrayBlockingQueue.size();
System.out.println(System.currentTimeMillis()+";;;"+size+"长度");
if (size > 0) {
List<CeShiDemo> list = new ArrayList<>();
sleepFF();
for (int i = 0; i < size; i++) {
CeShiDemo poll = arrayBlockingQueue.poll();
// todo 调用第三方接口 这里也可以用批量请求模式,可以改造
//返回数据,唤醒信息,如果是列表,返回对应数据回去
poll.completableFuture.complete("我回来了" + poll.id);
}
} else {
return;
}
}
}
},100,100,TimeUnit.MILLISECONDS);
}
public void sleepFF(){
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}