`
dingjob
  • 浏览: 181205 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Map的HashCode做缓存key值引发的重大bug

阅读更多

现象:

计费和账户的交互通过Map来交互,基本数据格式如下{"pp900_88",20,"pp900_61",2……}

在不同的取值情况下,较多数据返回了相同的价格结果,导致计算价格错误。

 

应用场景:

产品计算价格时,使用cache缓存了价格结果数据,cache的key值是传入map的hashCode,本意是要实现完全相同的Map传入值从缓存取数据,减少数据库的访问。

 

 

原因分析:

 

一。通过如下代码模拟线上应用:

  public static void main(String[] args) {
        HashMap map = new HashMap();

        for(int i = 0 ; i < 100 ; i ++){

            HashMap m1 = new HashMap();

            m1.put("pp900_88", i);

            m1.put("pp900_59", 30);

            m1.put("pp900_62", 6);

            m1.put("pp900_63", 4);

            m1.put("pp900_60", "y");

            m1.put("pp900_61", i);

            int hs = m1.hashCode();

            map.put(hs, map.get(hs)+","+i);
            //            System.out.println(i+"==="+m1.hashCode());
        }
        System.out.println(map.size());
        System.out.println(map);
    }

 结果如下:

 

{-479160017=null,56,57,58,59,60,61,62,63,
 -479160049=null,40,41,42,43,44,45,46,47, 
 -479160033=null,48,49,50,51,52,53,54,55,
 -479160129=null,0,1,2,3,4,5,6,7,64,65,66,67,68,69,70,71,
 -479160097=null,16,17,18,19,20,21,22,23,80,81,82,83,84,85,86,87,
 -479160113=null,8,9,10,11,12,13,14,15,72,73,74,75,76,77,78,79,
 -479160065=null,32,33,34,35,36,37,38,39,96,97,98,99, 
 -479160081=null,24,25,26,27,28,29,30,31,88,89,90,91,92,93,94,95}

 

可以看到,重复是很有规律的,连续7、8个数据都是重复的,当然在真实情况下重复概率不会这么高(因为Map的其他key-value值不太可能完全相同)

 

二。分析hashCode的产生

HashMap的hashCode根据key和value值来计算hashCode,最后将各个元素的hashCode值相加,即

 public final int hashCode() {
            return (key==null   ? 0 : key.hashCode()) ^
                   (value==null ? 0 : value.hashCode());
        }

 

以  m1.put("pp900_88", 1);  m1.put("pp900_61", 1);和

     m1.put("pp900_88", 2);  m1.put("pp900_61", 2);为例子:得到的key-Value的hashCode值如下

 

key                   key的hashCode   value的hashCode  Map的hashCode(key^value)

  pp900_88        -79859962             1                       -79859961

  pp900_61        -79860031             1                       -79860032
  pp900_88        -79859962             2                       -79859964
  pp900_61        -79860031             2                       -79860029
 
显然根据Map的hash值算法, Map的hashCode相加:第一行+第二行=第三行+第四行,这样产生重复数据也在所难免了,因为hashCode本来就不保证不同的输入值不会产生相同的结果。JSL的约束是对于相同的对象,必须产生相同的hashCode。
结论和改进措施:
1.不建议对结果进行缓存,结果缓存会带来很多问题,比如哪些数据变更需要刷新哪些缓存,缓存最好对原始纪录值进行缓存。
2.key值不采用hashCode算法,直接改为使用各个key-value的String拼接字符串。
 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics