项目修炼之路(5)高并发下优化Redis缓存效率

项目修炼之路(5)高并发下优化Redis缓存效率

标签: 优化 缓存 redis 大数据
10583人阅读 评论(3) 收藏 举报
分类:
架构(48) 大数据(27) 缓存(1)

目录(?)[+]

        最近,公司给了个优化任务,某个耗时的操作,在百亿的交易额下,处理异常缓慢,需要优化,以为每日发息做准备,在这里给大家介绍下我的优化思路,共同探讨下:


代码逻辑:

        通过用户id获取用户所在区域id,每次批量处理1千个用户,起20个线程处理。


第一步,加缓存

通过用户id获取用户所在区域id分两步实现(代码中已经标红),第一步通过用户获取城市id,第二部通过城市id获取区域id,使用上篇博客介绍的方法(项目修炼之路(4)aop+注解的自动缓存),给两个方法加入redis缓存。

[javascript] view plain copy
  1. @Override  
  2.     public PublicResult<HashMap<Integer, Integer>> getUserAreaFranchiseeIDS(List<Integer> uids) {  
  3.         PublicResult<HashMap<Integer, Integer>> result = new PublicResult<HashMap<Integer, Integer>>();  
  4.         HashMap<Integer, Integer> resultMap = new HashMap<Integer, Integer>();  
  5.   
  6.         long time;  
  7.         for(Integer uid :uids){  
  8.             Integer areaId = Integer.valueOf(0);  
  9.             try {  
  10.                 time=System.currentTimeMillis();  
  11.                 UserAreaFranchisee area =getUserAreaFranchisee(uid).getResult();  
  12.                 LOGGER.info("=getUserAreaFranchiseeIDS=>--.uid:["+uid+"].[get -- wmpsDayInterChange]getUserAreaFranchisee() -------------spen time:" + (System.currentTimeMillis()-time));  
  13.                 time=System.currentTimeMillis();  
  14.                 int id = 0;  
  15.                 if (area != null && area.getCityid() != null && area.getCityid().intValue() > 0) {  
  16.                     id = area.getCityid().intValue();  
  17.                      tpr = logicTongchengAreaService.getTongchengArea(Integer.valueOf(id));  
  18.                     if (tpr != null && tpr.isSuccess() && tpr.getResult() != null && tpr.getResult().getId() != null && tpr.getResult().getId() > 0) {  
  19.                         areaId = tpr.getResult().getId();  
  20.                     }  
  21.                 }  
  22.                 LOGGER.info("=getUserAreaFranchiseeIDS=>--..uid:["+uid+"].[get -- wmpsDayInterChange]getLogicTongchengAreaService() -------------spen time:" + (System.currentTimeMillis()-time));  
  23.   
  24.             }catch (Exception e){  
  25.                 LOGGER.error("=getUserAreaFranchiseeIDS=>",e);  
  26.             }  
  27.             resultMap.put(uid,areaId);  
  28.         }  
  29.   
  30.   
  31.         result.setSuccess(true);  
  32.         result.setResult(resultMap);  
  33.         return result;  
  34.     }  



第二步,合并结果

问题:加入缓存后,发现,当访问频繁时,两次访问加入的缓存不合理:1,value为对象,给每次取值增加反序列化过程,实际只需id即可;2,两次操作,最终只需一个结果,造成资源浪费。

优化后:二次缓存变为一次缓存,key与value均为简单string与Integer

[java] view plain copy
  1. @Override  
  2.     public PublicResult<String> getUserAreaFranchiseeIDS(ArrayList<Integer> uids) {  
  3.         PublicResult<String> result = new PublicResult<String>();  
  4.         HashMap<Integer, Integer> resultMap = new HashMap<Integer, Integer>();  
  5.         long time;  
  6.         for(Integer uid :uids){  
  7.             Integer areaId = Integer.valueOf(0);  
  8.             try {  
  9.                 time=System.currentTimeMillis();  
  10.                 areaId = userAreaFranchiseeService.getUserAreaIdByUid(uid);  
  11.                 LOGGER.info("=getUserAreaFranchiseeIDS=>--.uid:[" + uid + "].[get -- wmpsDayInterChange]getUserAreaIdByUid() -------------spen time:" + (System.currentTimeMillis() - time));  
  12.             }catch (Exception e){  
  13.                 LOGGER.error("=getUserAreaFranchiseeIDS=>",e);  
  14.             }  
  15.             resultMap.put(uid,areaId);  
  16.         }  
  17.         result.setSuccess(true);  
  18.         result.setResult(JSON.toJSONString(resultMap));  
  19.         return result;  
  20.     }  


第三步:批量读取

问题:redis为单线程,批量数据访问时,单个从redis拿数据的时间被延长,造成时间上的浪费,而且,浪费在网络上的时间比读数据时间要长

优化后:批量从redis获取一次获取,多次io改为一次io,拿不到的数据,才从数据库中读取,同时缓存到redis。

[java] view plain copy
  1. @Override  
  2.     public PublicResult<String> getUserAreaFranchiseeIDS(ArrayList<Integer> uids) {  
  3.         PublicResult<String> result = new PublicResult<String>();  
  4.         HashMap<Integer, Integer> resultMap = new HashMap<Integer, Integer>();  
  5.         long time;  
  6.         ArrayList<String> uidKeys = new ArrayList<String>();  
  7.         for(int i=0;i<uids.size();i++){  
  8.             uidKeys.add(i,RedisKeyUtils.USER_AREA_ID+ uids.get(i));  
  9.         }  
  10.         List<Integer> listAreas = RedisUtils.mget(uidKeys.toArray(),Integer.class);  
  11.         for(int i=0 ;i<uids.size();i++){  
  12.             Integer uid = uids.get(i);  
  13.             Integer areaId = Integer.valueOf(0);  
  14.             if(listAreas.get(i)==null){  
  15.                 try {  
  16.                     time=System.currentTimeMillis();  
  17.                     areaId = userAreaFranchiseeService.getUserAreaIdByUid(uid);  
  18.                     LOGGER.info("=getUserAreaFranchiseeIDS=>--.uid:[" + uid + "].[get -- wmpsDayInterChange]getUserAreaIdByUid() -------------spen time:" + (System.currentTimeMillis() - time));  
  19.   
  20.                 }catch (Exception e){  
  21.                     LOGGER.error("=getUserAreaFranchiseeIDS=>error uid:["+uid+"]",e);  
  22.                 }  
  23.                 listAreas.set(i,areaId);  
  24.             }  
  25.             areaId = listAreas.get(i);  
  26.             resultMap.put(uid,areaId);  
  27.         }  
  28.         result.setSuccess(true);  
  29.         result.setResult(JSON.toJSONString(resultMap));  
  30.         return result;  
  31.     }  



第四步:批量添加

问题:设置缓存周期后,每隔一段时间,读取数据几乎全从数据库读取,加上增加到redis的时间,会造成周期性读取缓慢。

优化后:时间限制拉长,判断是否能从redis获取一半的数据,如果不能,批量将数据缓存到redis(一次io),再走逻辑

[java] view plain copy
  1. @Override  
  2.     public PublicResult<String> getUserAreaFranchiseeIDS(ArrayList<Integer> uids) {  
  3.         PublicResult<String> result = new PublicResult<String>();  
  4.         HashMap<Integer, Integer> resultMap = new HashMap<Integer, Integer>();  
  5.         long time;  
  6.         ArrayList<String> uidKeys = new ArrayList<String>();  
  7.         for(int i=0;i<uids.size();i++){  
  8.             uidKeys.add(i,RedisKeyUtils.USER_AREA_ID+ uids.get(i));  
  9.         }  
  10.         List<Integer> listAreas = RedisUtils.mget(uidKeys.toArray(),Integer.class);  
  11.   
  12.         try {  
  13.             if (ListUtil.countNullNumber(listAreas) > listAreas.size() / 2) {  
  14.                 initRedisByUids(uids);  
  15.                 listAreas = RedisUtils.mget(uidKeys.toArray(), Integer.class);  
  16.             }  
  17.         }catch (Exception e){  
  18.             LOGGER.error("=getUserAreaFranchiseeIDS=>initRedisByUids error",e);  
  19.         }  
  20.   
  21.         for(int i=0 ;i<uids.size();i++){  
  22.             Integer uid = uids.get(i);  
  23.             Integer areaId = Integer.valueOf(0);  
  24.             if(listAreas.get(i)==null){  
  25.                 try {  
  26.                     time=System.currentTimeMillis();  
  27.                     areaId = userAreaFranchiseeService.getUserAreaIdByUid(uid);  
  28.                     LOGGER.info("=getUserAreaFranchiseeIDS=>--.uid:[" + uid + "].[get -- wmpsDayInterChange]getUserAreaIdByUid() -------------spen time:" + (System.currentTimeMillis() - time));  
  29.   
  30.                 }catch (Exception e){  
  31.                     LOGGER.error("=getUserAreaFranchiseeIDS=>error uid:["+uid+"]",e);  
  32.                 }  
  33.                 listAreas.set(i,areaId);  
  34.             }  
  35.             areaId = listAreas.get(i);  
  36.             resultMap.put(uid,areaId);  
  37.         }  
  38.         result.setSuccess(true);  
  39.         result.setResult(JSON.toJSONString(resultMap));  
  40.         return result;  
  41.     }  
  42.   
  43.     private boolean initRedisByUids(ArrayList<Integer> uids){  
  44.         boolean isSuccess = false;  
  45.         HashMap<String, Integer> resultMap =null;  
  46.         try {  
  47.             resultMap = ListUtil.getMaxAndMinInterger(uids);  
  48.             if(resultMap!=null && !resultMap.isEmpty()){  
  49.   
  50.                 List<UserAreaUidVo> listResult = userAreaFranchiseeService.getUserAreaIdPageByUid(resultMap.get(ListUtil.minNumKey), resultMap.get(ListUtil.maxNumKey));  
  51.                 if(listResult!=null && !listResult.isEmpty()){  
  52.                     HashMap<String ,List> hashMapForUid =uidToRedisKeyAndVlues(listResult);  
  53.                     RedisUtils.mset(hashMapForUid.get(RedisKeys).toArray(),hashMapForUid.get(RedisValues).toArray(),RedisKeyUtils.USER_AREA_ID_TIME);  
  54.                     isSuccess=true;  
  55.                 }  
  56.             }  
  57.         }catch(Exception e){  
  58.             LOGGER.error("=initRedisByUids=>",e);  
  59.         }  
  60.   
  61.         return isSuccess;  
  62.     }  
  63.   
  64.     private HashMap<String ,List> uidToRedisKeyAndVlues(List<UserAreaUidVo> listUserArea){  
  65.         HashMap<String ,List> hashMapForUid = new HashMap<String ,List>();  
  66.         List<String> keys = new ArrayList<String>(listUserArea.size());  
  67.         List<Integer> values = new ArrayList<Integer>(listUserArea.size());  
  68.         for(int i=0;i<listUserArea.size();i++){  
  69.             keys.add( RedisKeyUtils.USER_AREA_ID + listUserArea.get(i).getUid());  
  70.             values.add(listUserArea.get(i).getAreaid() == null ? 0 : listUserArea.get(i).getAreaid());  
  71.         }  
  72.         hashMapForUid.put(RedisKeys,keys);  
  73.         hashMapForUid.put(RedisValues,values);  
  74.         return hashMapForUid;  
  75.     }  

总结:

        在工作中,我们会遇到各种难题,实际这些难题,帮助我们提升了自己的解决问题能力外,还帮助我们制造了一种奇妙的东西,叫思路,或者叫框架,就是再有类似问题时,我们会映射过来,我是不是解决过,不仅仅局限在代码端,在生活和处理社会问题时,实际是相通的!

        所以,代码积累的不仅仅是工作经验,还有生活经验!


附录:工具类:

[java] view plain copy
  1. public class ListUtil {  
  2.   
  3.     public static String maxNumKey ="max";  
  4.     public static String minNumKey ="min";  
  5.     /** 
  6.      * 按照某大小对list分页 
  7.      * @param targe 
  8.      * @param size 
  9.      * @return 
  10.      */  
  11.     public static List<List>  splitList(List targe,int size) {  
  12.         List<List> listArr = new ArrayList<List>();  
  13.         //获取被拆分的数组个数  
  14.         int arrSize = targe.size()%size==0?targe.size()/size:targe.size()/size+1;  
  15.         for(int i=0;i<arrSize;i++) {  
  16.             List  sub = new ArrayList();  
  17.             //把指定索引数据放入到list中  
  18.             for(int j=i*size;j<=size*(i+1)-1;j++) {  
  19.                 if(j<=targe.size()-1) {  
  20.                     sub.add(targe.get(j));  
  21.                 }  
  22.             }  
  23.             listArr.add(sub);  
  24.         }  
  25.         return listArr;  
  26.     }  
  27.   
  28.     /** 
  29.      * 统计list中为null的元素个数 
  30.      * @param listTest 
  31.      * @return 
  32.      */  
  33.    public static long countNullNumber(List listTest){  
  34.        long  count=0;  
  35.        for(int i=0;i<listTest.size();i++){  
  36.            if(listTest.get(i)==null){  
  37.                count++;  
  38.            }  
  39.        }  
  40.        return count;  
  41.    }  
  42.   
  43.     /** 
  44.      * 统计list中为null的元素个数 
  45.      * @param listTest 
  46.      * @return 
  47.      */  
  48.     public static HashMap getMaxAndMinInterger(List<Integer> listTest)throws Exception{  
  49.         if(listTest==null || listTest.isEmpty()){  
  50.             throw new Exception("=ListUtil.getMaxAndMinInterger=> listTest is null");  
  51.         }  
  52.         HashMap<String,Integer> result = new HashMap<String,Integer>();  
  53.         Integer  maxNum=null;  
  54.         Integer minNum=null;  
  55.         for(int i=0;i<listTest.size();i++){  
  56.             if(!(listTest.get(i)==null)){  
  57.                 if(maxNum==null){  
  58.                     maxNum=listTest.get(i);  
  59.                 }  
  60.   
  61.                 if(maxNum<listTest.get(i)){  
  62.                     maxNum=listTest.get(i);  
  63.                 }  
  64.   
  65.                 if(minNum==null){  
  66.                     minNum=listTest.get(i);  
  67.                 }  
  68.                 if(minNum>listTest.get(i)){  
  69.                     minNum=listTest.get(i);  
  70.                 }  
  71.             }  
  72.         }  
  73.         if(maxNum==null || minNum == null){  
  74.             throw new Exception("=ListUtil.getMaxAndMinInterger=> listTest is null");  
  75.         }  
  76.         result.put(maxNumKey,maxNum);  
  77.         result.put(minNumKey,minNum);  
  78.         return result;  
  79.     }  
  80.   
  81.   
  82. }  

猜你喜欢

转载自blog.csdn.net/zxl2016/article/details/79729173