问题描述:
最近遇到一个问题,在程序中,有一个百万级别的商品集合,需要过滤掉商品黑名 单,这个黑名单由运营同学手工配置,从原来的几个到几百个,再到上千个,导致现 在程序在过滤这块耗时太长。如何使得程序的过滤运行时间缩短呢? |
分析:
比如,我们的商品集合是Map<ID,Product> skuMap,ID为商品的ID,Product是商品类型 对象,大小是100W+,商品黑名单列表是每隔10MIN去读取一次形成long[] skus,数组的 元素放置的就是商品ID,数组大小是几千级别。 过滤方案: 方案一: 伪码: for(Product p : skuMap){ for(Long id : skus){ if(p.getId() == id){ //此商品被过滤掉 p.setFlag(false); } } } 这种方案,是最初的写法,想法比较简单,就是2层FOR循环。 但是,随着配置的黑名单越来越多,从最初的几个到几百个,最后到几千个级别,导致 这段代码运行时间发生波动,并且越来越长! 方案二: 如何缩短时间呢? 对于方案一,而言,100W+的商品中的每一个商品都必须对skus进行全部遍历才能完成过 滤逻辑,导致循环次数是 100W+ * 1000+ 级别,也就是10亿+的级别,循环的次数实在 是太多了!要缩短循环次数,才能缩短时间! 伪码: Arrays.sort(skus); for(Product p : skuMap){ int result = Arrays.binarySearch(skus,id); if(result < 0){ p.setFlag(false); } } 如果我们在拿到skus后,对skus内部的ID进行一次排序,然后每个商品对skus进行二分 查找。要知道1000+的黑名单,对于二分查找而言,最多查找11次就可以找到!也就是在 最坏的情况下,我们需要循环的次数变成了100W+ * 11,即1000W+的循环次数! 这样方案二,一下子将10亿+的循环次数缩短至1000W+。 虽然如此,但是程序的过滤时间,日志打印,竟然是300S+,这也无法接受! 方案三: 我们为什么要拿skuMap取遍历skus,为什么不用黑名单去过滤商品集合呢? 伪码: for(long sku : skus){ if(skuMap.containsKey(sku)){ skuMap.get(sku).setFlag(false); } } 外层循环,变成了黑名单,在内部要知道我们用的是HASHMAP查找,实际上,一下子,循环次数变成了1000+。程序的过滤时间也缩短至几秒钟! |