最近有需求需要将推荐系统的日志数据接入到 Hbase 中 ,
最初的 RowKey 实际方案为 : 时间 + 手机号 + 推荐唯一标识 ,但是由于该数据量级很大 ( 1亿条/天 ) ,经常由于热点写入问题导致程序停止 ,因此设计了一种预分区方案来解决热点写入问题
散列 RowKey 设计方案为 : 根据原 RowKey 获取散列值 + 原 RowKey
获取散列值思路 :

  1. 首先要保证相同的 RowKey 得到的散列值相同 ,由此想到了 MD5 加密
  2. MD5 加密是否是均匀的散列 ? 这个答案为 : 是 ,这个是别人的 文章 : MD5 散列函数的结果是均匀分布吗?
  3. 如果直接以 MD5 做散列值的话 存在两点问题 : RowKey 很长 ,抽取数据时不方便
  4. 根据前文可以知道 ,MD5 的每个字符都是均匀分布的 ,因此可以取 MD5 中的一个字符来做散列值
  5. 由于 MD5 有 0 - 9 和 a - f 共 16 种字符 ,可以转化为 000 - 016 这种形式 ( 具体转换形式通过 ASCII )

散列工具类 :

  /**
   * 根据传入的RowKey做MD5散列,然后取第一个字符的ASCII码并进行处理,返回16个分区的RowKey
   * 使用该散列方法应该配合HBase預分區
   *
   * SPLITS => ['000','001','002','003','004','005','006','007','008','009','010','011','012','013','014','015','016']
   *
   * @param rowKey 需要做散列的RowKey
   * @return 已经散列过的RowKey
   */
  def getHashRowKey(rowKey: String): String = {
    val md5 = MessageDigest.getInstance(CodeConstant.MD5)
    val encoded = md5.digest(rowKey.getBytes)
    val array = encoded.map("%02x".format(_)).mkString.take(1).toCharArray
    var int = array(0).toInt
    if (int < 97) int = int - 48 else int = int - 97 + 10
    f"$int%3d".replaceAll(" ", "0") + rowKey
  }

Hbase 建表语句 :

create 'multi:t_wx_recommend_log_test', 'cf1' , SPLITS => ['000','001','002','003','004','005','006','007','008','009','010','011','012','013','014','015','016']

写入情况如下 ,由此可见散列还是蛮均匀的 :

分日提取数据 SQL ( Hbase 的 Hive 映射表 ) :

INSERT OVERWRITE TABLE trace.t_test_wx_recommend_log PARTITION (opera_day = 20200406)
SELECT recommend_type,
       opera_time,
       phone_number,
       activity_id,
       rule_id,
       channel_code,
       recommend_position,
       goods1,
       goods2,
       goods3,
       goods4,
       goods5,
       goods6,
       goods7,
       goods8,
       goods9,
       goods10,
       goods11,
       goods12,
       goods13,
       goods14,
       goods15,
       goods16,
       goods17
FROM (
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00020200406'
           AND key < '00020200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00120200406'
           AND key < '00120200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00220200406'
           AND key < '00220200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00320200406'
           AND key < '00320200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00420200406'
           AND key < '00420200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00520200406'
           AND key < '00520200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00620200406'
           AND key < '00620200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00720200406'
           AND key < '00720200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00820200406'
           AND key < '00820200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '00920200406'
           AND key < '00920200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '01020200406'
           AND key < '01020200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '01120200406'
           AND key < '01120200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '01220200406'
           AND key < '01220200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '01320200406'
           AND key < '01320200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '01420200406'
           AND key < '01420200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '01520200406'
           AND key < '01520200407'
         UNION ALL
         SELECT *
         FROM trace.t_test_wx_recommend_log_map
         WHERE key >= '01620200406'
           AND key < '01620200407') TEMP;

时至今日,你依旧是我的光芒。