踩坑情景:
我们有一张4亿条、300+字段的HBASE表需要针对30个字段在SOLR中建立二级索引,
在刷历史数的时候没有分批,直接运行了批量建索引的MR程序,导致SOLR直接被冲垮,无法提供服务。
原因:
由于需要处理的数据太多,并且当时还有几个实时往HBASE进数且往solr实时索引的表正在运行,
软提交设置的是15秒,这就会导致Lily不停地往solr commit数据,而MR批量导入的数据又过多,
SOLR就直接崩溃了,现在的配置为软提交1分钟,硬提交5分钟
解决办法:
我们的HBASE表是加盐的,有100个预分区,00-99,因此在运行批量建历史索引的MR程序是直接按分区来就行了,按预分区加上--hbase-start-row和--hbase-end-row 就行了。
--reducers设置为0意味着没有reduce,只有Map,Map直接读取HBASE表数据处理好之后直接输出到SOLR建上索引。
--go-live则是说solr会将离线索引的数据merge到running的collection中。
当然也可以指定reducers,指定之后便会将数据处理成solr索引的格式的文件,然后将该文件直接merge到solr中,至于哪种方式好我也不好说,我比较倾向于reducer为0吧。
详细信息可以看链接[Cloudera的解释还是很清楚的](https://www.cloudera.com/documentation/enterprise/latest/topics/search_hbase_batch_indexer.html#concept_z1c_cg3_ms)
脚本如下:
`echo "##########00 -- 10 start############"
echo `date`
hadoop --config /etc/hadoop/conf \
jar /opt/cloudera/parcels/CDH/lib/hbase-solr/tools/hbase-indexer-mr-job.jar \
--conf /etc/hbase/conf/hbase-site.xml \
--hbase-indexer-file $HOME/hbase-indexer/*****/morphline-hbase-mapper.xml \
--morphline-file morphlines.conf \
--zk-host *******:2181/solr \
--hbase-start-row 00 \
--hbase-end-row 10 \
--collection collectionName\
--reducers 0 \
--go-live
echo "###########00 -- 10 end#############"
wait
echo "##########10 -- 20 start############"
echo `date`
hadoop --config /etc/hadoop/conf \
jar /opt/cloudera/parcels/CDH/lib/hbase-solr/tools/hbase-indexer-mr-job.jar \
--conf /etc/hbase/conf/hbase-site.xml \
--hbase-indexer-file $HOME/hbase-indexer/*****/morphline-hbase-mapper.xml \
--morphline-file morphlines.conf \
--zk-host *******:2181/solr \
--hbase-start-row 10 \
--hbase-end-row 20 \
--collection collectionName\
--reducers 0 \
--go-live
echo "###########10 -- 20 end#############"
wait`
- 2号坑----Morphlines对于Int类型的处理问题
踩坑情景:
首先需要明确一点HBASE中都是Binary,没有类型之分。
我们有一些Int类型的字段有时候会没数,若一上来就没数是没问题的,hbase中不会有这条记录。
但悲催的是从有数变成没数,这时这个字段便会成为空。
此时Lily根据morphlines的映射关系做处理时这条记录就进不去solr了,原因就是空无法转成int类型。
解决办法:
1、morphlines中有个findReplace,可以根据一些条件做判断转换,不过我没用,链接奉上:
[有兴趣的可以看看](http://kitesdk.org/docs/1.1.0/morphlines/morphlines-reference-guide.html#findReplace)
2、我们这边int用的比较少,也没几个字段,就牺牲性能了,改成了String类型。
- 3号坑----对于时间类型的处理问题
踩坑情景:
时间类型复杂多样,数据链路较长,数据源有MySQL、ORACLE、SQLServer、SAP等,时间格式也是各种各样,
前面ETL工作做的不够精细,没有处理成统一的格式。刚开始将HIVE的数据往HBASE中bulkload时也没注意这方面,
直接导致了后面从HBASE往SOLR中导的时候很多记录处理失败没建上索引。
解决办法:
在HIVE中做统一的时间格式,实时数据也要与HIVE中的时间格式保持一致。这个很重要,要不然以后用起来太麻烦了。
要提前规划好。在Morphlines配置convertTimestamp的时候多想一些自己系统可能出现的情况,我们这边用了四种:
即inputFormats中的四种:带时分秒的,只有年月日的,为空的,带毫秒的。这四种目前基本满足我们的需求了。
{
convertTimestamp {
field : "hsicrm_registrationtime"
inputFormats : ["yyyy-MM-dd HH:mm:ss","yyyy-MM-dd","","yyyy-MM-dd HH:mm:ss.SSS"]
inputTimezone : Asia/Shanghai
outputFormat : "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
outputTimezone : UTC
}
}
- 4号坑----将hive parquet表的数据批量导入到SOLR建立索引
Morphlines文件配置展示:
SOLR_LOCATOR : {
# Name of solr collection
collection : hive_test
# ZooKeeper ensemble
zkHost : "*****:2181/solr"
}
morphlines : [
{
id : shunguang_order_v1
importCommands : ["org.kitesdk.**", "com.ngdata.**"]
commands : [
{
readAvroParquetFile {
# Optionally, use this Avro schema in JSON format inline for projection:
# projectionSchemaString : """"""
# Optionally, use this Avro schema file in JSON format for projection:
# projectionSchemaFile : /path/to/syslog.avsc
}
}
{
extractAvroPaths {
flatten : false
paths : { ##解释一下:"id:/row_id",左边的id是指solr中索引的主键,
右边的row_id是指我选择hive表中row_id字段来作为solr索引的主键。下面的字段名则需要与schema.xml中
对应起来,与hive的字段名一样就行了,不容易出错。
id : /row_id
siteid : /siteid
istest : /istest
paytime : /paytime
isbackend : /isbackend
}
}
}
{
convertTimestamp { ##针对时间字段做一下转换。
field : paytime
inputFormats : ["yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd","","yyyy-MM-dd HH:mm:ss.SSS"]
inputTimezone : Asia/Shanghai
outputFormat : "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
outputTimezone : UTC
}
}
}
{
sanitizeUnknownSolrFields {
# Location from which to fetch Solr schema
solrLocator : ${SOLR_LOCATOR}
}
}
# log the record at DEBUG level to SLF4J
{ logInfo { format : "output record: {}", args : ["@{}"] } }
{loadSolr {solrLocator : ${SOLR_LOCATOR}}}
]
}
]
- 大坑来了----读取HDFS文件往SOLR批量建索引的两个工具
1、/opt/cloudera/parcels/CDH/jars/search-mr-*-job.jar,这个jar包是跑MapReduce批量建索引。
需要注意的是它只能用于初始化建历史索引,并不会对原有的索引数据做更新。比如你将pt=20190709分区共1万条
不重复的数用这个工具往solr建上了索引,然后你又运行了一遍脚本建了一遍,那solr里就成了2万条,
每一条都有俩索引记录,只是solr中的version版本号不同。
2、/opt/cloudera/parcels/CDH/lib/solr/contrib/crunch/search-crunch-1.0.0-cdh5.15.0.jar,
这个jar包是跑spark 1.6任务批量建索引。可以对原有索引数据做更新。假如你有一些增量表是按天更新的,那么用这个做增量脚本是个合适的选择。
3、/opt/cloudera/parcels/CDH/lib/solr/contrib/crunch/search-crunch-1.0.0-cdh5.15.0.jar,
哈哈,你没看错,跟第二个一样,只不过这个工具有两种运行方式,也可以用MapReduce的方式跑。具体怎么选择看你的环境。
1、/opt/cloudera/parcels/CDH/jars/search-mr-*-job.jar调用脚本
kinit -kt /home/***/***.keytab ****
hadoop --config /etc/hadoop/conf \
jar /opt/cloudera/parcels/CDH/jars/search-mr-*-job.jar org.apache.solr.hadoop.MapReduceIndexerTool \
-Dmapreduce.job.queuename=root.high \
--conf /etc/hadoop/conf.cloudera.hdfs/hdfs-site.xml \
--morphline-file morphlines.conf \
--morphline-id shunguang_order_v1 \
--solr-home-dir $HOME/hive-indexer/shunguang_order_v1 \
--output-dir hdfs://nameservice1/tmp/shunguang_order_v1 \
--zk-host ****:2181/solr \
--collection shunguang_order_v1 \
--go-live \
hdfs://nameservice1/hive/***/****/pt=1001/000001_0 hdfs://nameservice1/hive/***/****/pt=1001/000002_0
##可以一次性处理多个hdfs文件,只不过需要将每个文件都罗列上,用空格分隔。
##如果你的集群开了kerberos,那么最好像我一样先kinit一下有对应权限的principal。
2、/opt/cloudera/parcels/CDH/lib/solr/contrib/crunch/search-crunch-1.0.0-cdh5.15.0.jar调用脚本
这个脚本是直接拿的Cloudera document中,直接就能用。
直接甩链接
kinit -kt ~/****.keytab ***
export HADOOP_CLASSPATH="/opt/cloudera/parcels/CDH/lib/search/lib/search-crunch/*"
export myMorphline=/home/dsj_solr/hive-indexer/shunguang_order_v1/morphlines_2.conf
export myResourcesDir=/opt/cloudera/parcels/CDH/share/doc/search-*/search-crunch
export myDriverJarDir=/opt/cloudera/parcels/CDH/lib/solr/contrib/crunch
export myDependencyJarDir=/opt/cloudera/parcels/CDH/lib/search/lib/search-crunch
export myDriverJar=$(find $myDriverJarDir -maxdepth 1 -name 'search-crunch-*.jar' ! -name '*-job.jar' ! -name '*-sources.jar')
export myDependencyJarFiles=$(find $myDependencyJarDir -name '*.jar' | sort | tr '\n' ',' | head -c -1)
export myDependencyJarPaths=$(find $myDependencyJarDir -name '*.jar' | sort | tr '\n' ':' | head -c -1)
#export myDependencyJarFiles=$(find $myDependencyJarDir -name '*.jar' | grep -v poi| sort | tr '\n' ',' | head -c -1)
#export myJVMOptions="-DmaxConnectionsPerHost=10000 -DmaxConnections=10000 -Djava.io.tmpdir=/tmp/dir/ -verbose:class -Djute.maxbuffer=102400000"
export myJVMOptions="-DmaxConnectionsPerHost=10000 -DmaxConnections=10000 -Djava.io.tmpdir=/tmp/dir/ -verbose:class -Djute.maxbuffer=102400000 -Dlog4j.configuration=./log4j_debug.conf"
echo "DriverJar":$myDriverJar
#echo "myDependencyJarFiles":$myDependencyJarFiles
spark-submit \
--master yarn \
--deploy-mode cluster \
--jars $myDependencyJarFiles \
--executor-memory 4G \
--driver-memory 4G \
--conf spark.executor.extraJavaOptions="$myJVMOptions" \
--conf spark.executor.extraClassPath=/opt/cloudera/parcels/CDH/lib/search/lib/lucene-analyzers-common.jar \
--conf spark.driver.extraJavaOptions="$myJVMOptions" \
--conf spark.driver.extraClassPath=/opt/cloudera/parcels/CDH/lib/search/lib/lucene-analyzers-common.jar \
--driver-java-options "$myJVMOptions" \
--class org.apache.solr.crunch.CrunchIndexerTool \
--files $myMorphline,log4j_debug.conf,/opt/cloudera/parcels/CDH/lib/search/lib/lucene-analyzers-common.jar \
$myDriverJar \
-D hadoop.tmp.dir=/tmp \
-D morphlineVariable.ZK_HOST=***:2181/solr \
--morphline-file morphlines_2.conf \
--morphline-id shunguang_order_v1 \
--pipeline-type spark \
--chatty \
--log4j log4j_debug.conf \
hdfs://nameservice1/hive/***/***/pt=1001/000013_0
#####MapReduce调用方法
hadoop \
--config /etc/hadoop/conf.cloudera.yarn/ \
jar $myDriverJar org.apache.solr.crunch.CrunchIndexerTool \
--libjars $myDependencyJarFiles \
-D mapreduce.map.memory.mb=12000 \
-D mapreduce.reduce.memory.mb=12000 \
-D morphlineVariable.ZK_HOST=***:2181/solr \
-D mapreduce.input.fileinputformat.split.maxsize=64000000 \
-D mapreduce.job.queuename=root.***\
--files $myMorphline \
--morphline-file morphlines_2.conf \
--pipeline-type mapreduce \
--chatty \
--log4j $myResourcesDir/log4j.properties \
hdfs://nameservice1/hive/***/****/pt=1001/000013_0