背景
md5是不可解密的. 通常网站http://www.cmd5.com/宣称的解密都是有一个MD5到值的映射数据库(彩虹表).
做法是提前将数据用MD5加密,然后保存成MD5到原数据的映射关系,解密时只要查询MD5对应的值就可以了.
业务数据将近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 + f