TPC-DS 数据集是怎么回事大家可以在网上搜索一下就知道了,我在这里不做介绍,我只介绍一下如果使用spark做TPC-DS测试。
TPC-DS数据集的生成
TPC-DS数据生成需要两个项目, 一个是tpcds-kit ,另一个是 spark-sql-perf
辅助工具生成
tpcds-kit
源码地址: tpcds-kit
按照github上的文档按照步骤进行编译就可以了,编译后需要tools的两个文件: dsdgen,tpcds.idx
把文件放在所有计算节点的/tmp/tpcds 目录下,这里待后面使用,如果是不方便的话,可以使用spark local的方式进行生成数据,可以只放在本地目录。
spark-sql-perf
源码地址: GitHub - databricks/spark-sql-perf
按照github上的文档按照步骤进行编译就可以了,编译后需要的是target/scala-2.12目录下的jar包:spark-sql-perf-assembly-0.5.1-SNAPSHOT.jar
注意事项:spark-sql-perf 项目是sbt进行项目管理的可以使用阿里的源进行编译,就很容编译成功
生成数据
执行spark-submit进行生成数据,注意修改参数--dsdgenDir目录和 jar包位置。
文件格式一定使用ORC ,因为文本和Parquet 在hive表生成的时候有问题,只能使用ORC, 如果要想测试文本和qarquet的话可以不用hive进行创建外部表。
spark3-submit \
--conf spark.dynamicAllocation.enabled=false \
--conf spark.shuffle.service.enabled=false \
--class com.databricks.spark.sql.perf.tpcds.GenTPCDSData \
--deploy-mode client \
--name generate_dataset \
--master yarn \
--conf spark.yarn.submit.waitAppCompletion=true \
--conf spark.driver.cores=2 \
--conf spark.driver.memory=4G \
--conf spark.executor.cores=2 \
--conf spark.executor.memory=8G \
--conf spark.executor.instances=25 \
--conf spark.executor.memoryOverhead=2048 \
--conf spark.default.parallelism=600 \
/home/hadoop_test/tpcdb/spark-sql-perf-assembly-0.5.1-SNAPSHOT.jar \
--dsdgenDir /tmp/tpcds \
--master yarn \
--scaleFactor 100 \
--location /user/hadoop_test/tpcdb_4 \
--format orc \
--overwrite true \
--partitionTables true \
--numPartitions 100
创建Hive 外部表
创建外部表,使用的是spark-shell ,在spark-shell中执行代码。亲测创建外部表只能使用sparksql进行查询,使用hive进行查询会有问题,猜测和本人使用的hive版本和spark版本有关系,不过对于spark跑tpc-ds 测试已经足够了
spark3-shell \
--conf spark.dynamicAllocation.enabled=false \
--name generate_dataset \
--master yarn \
--conf spark.yarn.submit.waitAppCompletion=true \
--conf spark.driver.cores=2 \
--conf spark.driver.memory=4G \
--conf spark.executor.cores=2 \
--conf spark.executor.memory=8G \
--conf spark.executor.instances=25 \
--conf spark.executor.memoryOverhead=2048 \
--conf spark.default.parallelism=600 \
--jars /home/hadoop_test/tpcdb/spark-sql-perf-assembly-0.5.1-SNAPSHOT.jar
val databaseName = "tpcds"
sql(s"drop database if exists $databaseName cascade")
sql(s"create database $databaseName")
val scaleFactor = "10"
val format = "orc"
val useDecimal = true
val useDate = true
val filterNull = false
val shuffle = true
import com.databricks.spark.sql.perf.tpcds.TPCDSTables
val sqlContext = spark.sqlContext
val tables = new TPCDSTables(sqlContext, dsdgenDir = "/tmp/tpcds", scaleFactor = scaleFactor, useDoubleForDecimal = !useDecimal, useStringForDate = !useDate)
val rootDir="/user/hadoop_test/tpcdb_4"
tables.createExternalTables(rootDir, format, databaseName, overwrite = true, discoverPartitions = true)
// 如果是其他格式可以试试临时,本人因为要对比k8s和cdh的性能,在k8s使用的就是这种格式,亲测可以使用
// tables.createTemporaryTables(rootDir, format)
TPC-DS数据集性能SQL运行
性能测试运行也是使用的spark-shell ,启动命令值前面一直 , 如果是使用临时表的话可以使用上面那个spark 应用继续往下执行。
注意事项, 本段scala代码有两段多行的语句,需要使用:paste 进行执行,直接粘贴会有报错
val databaseName = "tpcds"
val scaleFactor = "100"
val useDecimal = true
val useDate = true
val iterations = 2
val timeout = 60
val query_filter = Seq()
val randomizeQueries = false
val resultLocation = "/user/hadoop_test/tpcdb_4_results"
sql(s"use $databaseName")
import com.databricks.spark.sql.perf.tpcds.TPCDS
val sqlContext = spark.sqlContext
val tpcds = new TPCDS (sqlContext = sqlContext)
def queries = {
val filtered_queries = query_filter match {
case Seq() => tpcds.tpcds2_4Queries
case _ => tpcds.tpcds2_4Queries.filter(q => query_filter.contains(q.name))
}
if (randomizeQueries) scala.util.Random.shuffle(filtered_queries) else filtered_queries
}
val experiment = tpcds.runExperiment(
queries,
iterations = iterations,
resultLocation = resultLocation,
tags = Map("runtype" -> "benchmark", "database" -> databaseName, "scale_factor" -> scaleFactor))
println(experiment.toString)
experiment.waitForFinish(timeout*60*60)
查看执行结果
对应目录:resultLocation = "/user/hadoop_test/tpcdb_4_results" 下有一个时间目录下是一个json文件。
json文件会存放结果,这个结果会有运行迭代次数据的行数,建议使用json格式化每行数,进行查看比较方便,也可以自己写一个小程序生成CSV,用EXCEL 进行查看。
本人写了一个小程序,可以进行结果,如果大家有好的方式欢迎大家写在评论区中。
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import lombok.Data;
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class Result {
private String name;
private Integer executionTime;
}
// //
//
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.opencsv.CSVWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
public class TpcdsResultAnalytis {
public static void main(String[] args) throws Exception {
String rootDir = "D:\\test\\tpcds\\result";
String resultDir = "D:\\test\\tpcds\\result-result";
File[] files = new File(rootDir).listFiles();
List<String> headers = new ArrayList<>();
headers.add("name");
ObjectMapper mapper = new ObjectMapper();
Map<String, List<Result>> resultMap = new HashMap<>();
for (File file : files) {
String fileName = file.getName();
if (!fileName.endsWith(".json")) {
System.out.println(fileName + "is not a json");
continue;
}
String nameKey = fileName.substring(0, fileName.length() - 5);
List<String> lines = IOUtils.readLines(new FileReader(file));
for (String line : lines) {
JsonNode node = mapper.readTree(line);
int iteration = node.get("iteration").asInt();
JsonNode results = node.get("results");
List<Result> resultsObject = mapper
.convertValue(results, new TypeReference<List<Result>>() {
});
String header = nameKey + "-" + iteration;
headers.add(header);
resultMap.put(header, resultsObject);
}
}
File resultDirFile = new File(resultDir);
if (!resultDirFile.exists()) {
resultDirFile.mkdirs();
}
CSVWriter csvWriter = new CSVWriter(new FileWriter(new File(resultDir, "tmp.csv")));
csvWriter.writeNext(headers.toArray(new String[0]));
List<Result> oneResults = resultMap.get(headers.get(1));
for (int i = 0; i < oneResults.size(); i++) {
String tmpResult [] = new String[headers.size()];
tmpResult[0] = oneResults.get(i).getName();
for (int j = 1; j < headers.size(); j++) {
Result tmp = resultMap.get(headers.get(j)).get(i);
if(!tmp.getName().equals(tmpResult[0])){
System.out.println("========================1========================");
System.out.println(tmp);
System.out.println(oneResults.get(i));
System.out.println("========================2========================");
}else{
tmpResult[j]=tmp.getExecutionTime()+"";
}
}
csvWriter.writeNext(tmpResult);
}
csvWriter.close();
}
}