1. 背景
在一次实现Spark需求时,需要实现repartitionAndSortWithinPartitions算子,具体代码见: 浅谈 repartitionAndSortWithinPartitions
这里需要先对数据进行分区, 刚开始使用的方法是直接对字符串进行hashCode,
但是后来使用这个算子实现的功能并不能满足PM的需求,就开始查BUG
2. 查找问题
HashPartitioner分区冲突
刚开始问题定位到了hashCode冲突上,因为需要hash的字符串(暂且叫logid)很长,且有许多特殊字符.查到当有特殊字符且字母分大小写时,hash冲突率很高.
此处直接将.hashcode改为了纯数字md5方式.这里直接贴一个scala版本纯数字md5的方法供大家参考.
def getMD5(str: String): Long = {
val id_map = Map("a" -> "1", "b" -> "2", "c" -> "3", "d" -> "4", "e" -> "5", "f" -> "6", "g" -> "7")
// 第一步,获取MessageDigest对象,参数为MD5表示这是一个MD5算法
val md5 = MessageDigest.getInstance("MD5")
//md5.update("aayz".getBytes("UTF-8"))
val array = md5.digest(str.getBytes("UTF-8"))
val bigInt = new BigInteger(1, array).toString(16).substring(8, 24) // resultStr
var strm = ""
for (i <- bigInt) {
if (id_map.contains(i.toString)) strm += id_map(i.toString) else strm += i
}
strm.toLong
}
hashMap中hash冲突
将hashcode修改为md5后发现需求还未达到要求, 就继续查找问题. 发现在此处使用到了HashMap,每个partition中数据量过大,同样会出现Hash冲突的问题.
将HashMap中的key进行md5,然后重新塞入
3. 总结
当HashMap的Key是较长的字符串且数据量较大时,极容易出现Hash冲突,此时建议将Key做进一步处理, 如MD5后再塞回,可降低Hash冲突率,减少Hash重复的数量