您的位置:首页 > 其它

优惠券读服务优化

2015-11-27 15:10 260 查看
优惠券读服务优化

前几天成功解决了发券时的并发问题,参考这里。但是用户领取得卡一多,读取速度也慢了下来,用户领取了200张卡以后,取得自己所有的卡需要5s左右。

主要业务场景,查询用户卡包,得到用户所有的卡券。



- 用户根据id分在不同的表。

- 卡券根据不同的商户,分在不同的表。

先用用户id拿到用户和卡的对应关系。

再根据每个卡的商户和 卡券id查询对应的卡券,因为卡分在不同的表,不能成批拿卡,只能串行拿。

拼装卡模板相关信息返回。

第一步,建立卡券的缓存系统。

以商户和卡券Id在添加一个字符前缀做key,把卡缓存在redis里,并在卡券状 态变更后,作废钓redis里对应的卡券。

这样做后,构建一个200张卡的卡包,第一次还是需要5秒,第二次访问只需要1s。大并发后响应时间更长。这显然不能满足要求。

第二步,建立用户的缓存系统。

在有了卡券的缓存后,另外以用户id为主键,对每一个用户查询的返回结果,再做一级缓存,形成二级缓存机制。用户卡有变动的时候,作废缓存。

有了二级缓存,还是200张卡,第一次访问需要5s,第二次响应在200ms。100个并发访问在400ms左右。

缓存预刷机制。

有了二级缓存之后,由于每次卡有变动,用户访问卡包构建cache在1s以上,会影响体验,我又加入了预刷缓存机制,在用户卡变动后,作废掉用户级别的缓存,和变动的单个卡的缓存,随即抛出一个用户的id进入队列,队列的尾端拿到用户的主键后,访问用户卡包构建缓存,这样当用户再次访问卡包的时候,响应体验提升很大。

第三步,并行构建卡券

在没有任何缓存的情况下,一次访问200个卡券的卡包在6s以上。

在只有卡券这一级缓存的情况下,构建用户返回结果,也需要1s以上。

因为取200个卡券的执行是单个串行执行的,分析下来这里是性能的瓶颈。优化方案是: 在构建200个卡券的时候,每一批卡开一个线程去构建,200个卡,可以同时开20到40个线程去构建,来缩短用户卡多的时候,这里的时间消耗。

这样做后,先禁用用户级别的缓存,来压力测试这里的性能。

在没有任何cache的情况下,单次访问200个卡的卡包,响应 时间在500ms,在有卡缓存没有用户缓存的情况下,单次的响应时间在200ms,构建用户级别缓存的时间已经大大缩短了。但在高并发的情况下,会报错,一查是因为redis的连接不够了,每个线程需要20-40个redis连接,所以redis要扩大连接数。

下面是多线程部分代码,对象的名字做了处理,并除去了业务逻辑。

/**
* Created by haoli
*/
if (itemsList != null
&& CollectionUtils.isNotEmpty(itemsList)) {
int totalCount = itemsList.size();
int taskNumber = (totalCount%GROUP_COUNT == 0 ? totalCount/GROUP_COUNT:totalCount/GROUP_COUNT+1);
List<FutureTask<List<YourObject>>> ft = new ArrayList<FutureTask<List<YourObject >>>();
for(int i=0; i<taskNumber ; i++) {
int toSize = ((i+1)*GROUP_COUNT>totalCount-1 ?totalCount : (i+1)*GROUP_COUNT);
List<ItemObject> tempList  = itemsList.subList(i*GROUP_COUNT,toSize);
SubQueryYourObjectTask task = new SubQueryYourObjectTask (tempList,this.yourService);
FutureTask<List<YourObject >> ftItem = new FutureTask<List<YourObject >>(task);
ft.add(ftItem);
ThreadPoolExeService.execute(ftItem);
}
for(FutureTask<List<YourObject >> ftItem : ft){
try {
totals.addAll(ftItem.get());
} catch (InterruptedException e) {
log.error(e.toString());
} catch (ExecutionException e) {
log.error(e.toString());
}
}


上面用到的子任务类代码

/**
* Created by haoli
*/
public class SubQueryYourObjectTask implements Callable<List<YourObject >> {

private List<ItemObject > parameter;

private YourService yourService ;

public SubQueryYourObjectTask (List<ItemObject > rlist, YourService yours){
parameter = rlist;
this.yourService = yours ;
}

@Override
public List<YourObject > call() throws Exception {
List<YourObject > results= new ArrayList<YourObject >();
for(ItemObject cbur :parameter ) {
results .add(this.yourService .getXXXById(cbur ));
}
return results ;
}

}


优化到现在,不论是第一次还是之后的访问,都不慢了,在有二级缓存的情况下,cpu和连接资源消耗也不会太大。而且有缓存预刷机制,卡包数据更新后也不会慢了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: