欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 宝鼎售后问题提交 | 后台管理


新闻资讯

MENU

软件开发知识

千亿 KV 数据存 CAD加密 储和查询方案

点击: 次  来源:劳务派遣管理系统 时间:2017-11-07

原文出处: 任何忧伤,都抵不外世界的瑰丽

配景

md5是不行解密的. 凡是网站http://www.cmd5.com/宣称的解密都是有一个MD5到值的映射数据库(彩虹表).

做法是提前将数据用MD5加密,然后生存成MD5到原数据的映射干系,解密时只要查询MD5对应的值就可以了.

千亿 KV 数据存 CAD加密 储和查询方案

业务数据快要1000亿,估算下来或许占用6T. 由于MD5的数据是32位,并且每一位都属于0-f.
假如直接查询生成的6T数据,速度预计很慢. 于是想到分区, 好比以32位MD5的前几位沟通的作为一个分区,
查询时首先将MD5路由到指定的分区, 再查询这个分区的所有数据,这样每个分区的数据量就会少许多.
原始文件data.txt(最后两个字段暗示MD5的前四位):

111111111111111,001e5a2b1c68d7b7dddddddddddddddc,00,1e
222222222222222,01271cc012464ae8ccccccccccccccce,01,27

Hive分区(×)

姑且表和分区表:

CREATE EXTERNAL TABLE `mob_mdf_tmp`(
  `mob` string,
  `mdf` string,
  `mdf_1` string,
  `mdf_2` string
  )
ROW FORMAT delimited fields terminated by ','
LOCATION 'hdfs://tdhdfs/user/tongdun/mob_mdf_tmp';

CREATE EXTERNAL TABLE `mob_mdf`(
  `mob` string,
  `mdf` string
  )
PARTITIONED BY (
  mdf_1 string,
  mdf_2 string)
stored as parquet
LOCATION 'hdfs://tdhdfs/user/tongdun/mob_mdf';

将原始文件导入到姑且表(可能用hive的load呼吁),然后读取姑且表,加载数据到分区表

#!/bin/sh
file=$1
/usr/install/hadoop/bin/hadoop fs -put $file /user/tongdun/mod_mdf_tmp
#LOAD DATA LOCAL INPATH 'id.txt' INTO TABLE id_mdf PARTITION(mdf_1='ab',mdf_2='cd');
#LOAD DATA LOCAL INPATH 'id.txt' INTO TABLE id_mdf_tmp;

/usr/install/apache-hive/bin/hive -e "
set hive.exec.dynamic.partition=true; 
set hive.exec.dynamic.partition.mode=nonstrict; 
SET hive.exec.max.dynamic.partitions=100000;
SET hive.exec.max.dynamic.partitions.pernode=100000;
set mapreduce.map.memory.mb=5120;
set mapreduce.reduce.memory.mb=5120;
INSERT into TABLE mod_mdf PARTITION (mdf_1,mdf_2) SELECT mod,mdf,mdf_1,mdf_2 FROM mod_mdf_tmp;
msck repair table mod_mdf;
"

问题:将原始文件导入到HDFS是很快的,基天职分钟搞定.可是转换身分区的Hive表,速度起慢无比. %><%

AWK剧本处理惩罚分区

A.原始文件首先拆分成一级文件,再拆分成二级文件(×)

一级拆分: awk -F, ‘{print >> $3}’ data.txt
上面的awk呼吁会凭据第三列即MD5的前两个字符分组生成差异的文件. 好比生成00,01文件.
然后举办二级拆分: 遍历所有的一级文件, 生成二级文件. 好比001e.txt, 0127.txt.

nums=('0' '1' '2' '3' '4' '5' '6' '7' '8' '9' 'a' 'b' 'c' 'd' 'e' 'f')
for n1 in ${nums[@]};
do
  for n2 in ${nums[@]};
  do
    var=$n1$n2
    awk -F, '{OFS=",";print $1,$2 >> $3_$4".txt"}' $var
  done
done
echo "end."

缺点: 每个数据文件都必需在本身的范畴内生成一级文件, 然后在本身的一级文件基本上生成二级文件.
最后所有的二级文件要归并为一个文件. 较量贫苦, %><%

B.原始文件直接生成两级拆分文件

直接拆分成两级的: awk -F, ‘{OFS=”,”;print $1,$2 >> $3_$4″.txt”}’ data.txt
利益: 由于有多个原始数据文件, 执行同样的awk呼吁, 生成最终功效不需要任那里理惩罚.
问题: 大文件分组,速度较量慢,并且不像上面的分成两次,0000.txt文件并不会立即有数据生成.
同样尚有一个问题: 假如多个文件一起追加>>数据, 会发生斗嘴,即写到同一行.

C.切分原始大文件(×)

对原始大文件(20G~100G)先split: split -C 2014m $file,再举办上面的二级拆分进程.
功效: 27G切分成2G一个文件, 耗时538s. 估算6T数据需要500h~20D. %><%

paldb@linkedin(×)

linkedin开源的paldb声称对付写一次的kv存储读取机能很好. 可是一个严重的问题是不支持在已有的db文件中新增数据.

Can you open a store for writing subsequent times?
No, the final binary file is created when StoreWriter.close() is called.

所以要读取所有的原始文件后,不能一个一个文件地处理惩罚. 这期间StoreWriter要一直打开,下面是索引文件的代码:

//直接读取所有原始文件, 生成paldb
public static void indexRawFile(String[] files) throws Exception{
    List<String> prefix = generateFile();

    //提前筹备好Writer
    Map<String,StoreWriter> maps = new HashMap();
    for(String pref : prefix){
        StoreWriter writer = PalDB.createWriter(new File(folder + pref + ".paldb"));
        maps.put(pref, writer);
    }

    for(String filepath : files){
        File file = new File(folder + filepath);
        BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));
        BufferedReader reader = new BufferedReader(new InputStreamReader(fis,"utf-8"),5*1024*1024);// 用5M的缓冲读取文本文件

        String line = "";
        while((line = reader.readLine()) != null){
            String[] data = line.split(",");
            //按照前两位, 确定要利用哪个Writer. 沟通2位前缀的记录写到同一个db文件里
            String prefData = data[2];
            maps.get(prefData).put(data[1], data[0]);
        }
        fis.close();
        reader.close();
    }

    for (Map.Entry<String, StoreWriter> entry : maps.entrySet()) {
        entry.getValue().close();
    }
}