阿里云 E-MapReduce(简称EMR)是运行在阿里云平台上的一种大数据处理的系统解决方案。
ClickHouse 作为开源的列式存储数据库,主要用于在线分析处理查询(OLAP),能够使用 SQL 查询实时生成分析数据报告。而阿里云 EMR ClickHouse 则提供了开源 OLAP 分析引擎 ClickHouse 的云上托管服务。
本系列文章将从以下几个方面详细介绍 EMR ClickHouse 的操作指南:
数据导入 (本文)
常见问题
EMR ClickHouse 操作指南 — 数据导入
一、从 Spark 导入数据至 ClickHouse
介绍如何将 Spark 中的数据导入至 ClickHouse 集群
前提条件
开发工具
-
本地安装了 Java JDK 8。
本地安装了 Maven 3.x。
本地安装了用于 Java 或 Scala 开发的 IDE,推荐 IntelliJ IDEA,且已配置完成 JDK 和 Maven 环境。
已创建 Spark 集群,详情请参见创建集群:
https://help.aliyun.com/document_detail/28088.htm已创建 ClickHouse 集群,详情请参见创建集群:
https://help.aliyun.com/document_detail/213395.htm
背景信息
关于 Spark 的更多介绍,请参见简介:
https://help.aliyun.com/document_detail/206291.htm
代码示例
代码示例如下:
package com.company.packageName
import java.util.Properties
import java.util.concurrent.ThreadLocalRandom
import scala.annotation.tailrec
import com.google.common.collect.ImmutableMap
import org.apache.spark.internal.Logging
import org.apache.spark.sql.{SaveMode, SparkSession}
case class Test(id: Int, key1: String, value1: Boolean, key2: Long, value2: Double)
object CKDataImporter extends Logging {
private var dbName: String = "default"
private var tableName: String = ""
private var ckHost: String = ""
private var ckPort: String = "8123"
private var user: String = "default"
private var password: String = ""
private var local: Boolean = false
def main(args: Array[String]): Unit = {
parse(args.toList)
checkArguments()
val jdbcUrl = s"jdbc:clickhouse://$ckHost:$ckPort/$dbName"
logInfo(s"Use jdbc: $jdbcUrl")
logInfo(s"Use table: $tableName")
val spark = getSparkSession
// generate test data
val rdd = spark.sparkContext.parallelize(1 to 1000).map(i => {
val rand = ThreadLocalRandom.current()
val randString = (0 until rand.nextInt(10, 20))
.map(_ => rand.nextLong())
.mkString("")
Test(i, randString, rand.nextBoolean(), rand.nextLong(), rand.nextGaussian())
})
val df = spark.createDataFrame(rdd)
df.write
.mode(SaveMode.Append)
.jdbc(jdbcUrl, tableName, getCKJdbcProperties(user, password))
}
private def printUsageAndExit(exitCode: Int = 0): Unit = {
logError("Usage: java -jar /path/to/CKDataImporter.jar [options]")
logError(" --dbName 设置ClickHouse数据库的名称,默认为default")
logError(" --tableName 设置ClickHouse库中表的名称")
logError(" --ckHost 设置ClickHouse地址")
logError(" --ckPort 设置ClickHouse端口,默认为8123")
logError(" --user 设置ClickHouse所使用的用户名")
logError(" --password 设置ClickHouse用户的密码,默认为空")
logError(" --local 设置此程序使用Spark Local模式运行")
System.exit(exitCode)
}
@tailrec
private def parse(args: List[String]): Unit = args match {
case ("--help" | "-h") :: _ =>
printUsageAndExit()
case "--dbName" :: value :: tail =>
dbName = value
parse(tail)
case "--tableName" :: value :: tail =>
tableName = value
parse(tail)
case "--ckHost" :: value :: tail =>
ckHost = value
parse(tail)
case "--ckPort" :: value :: tail =>
ckPort = value
parse(tail)
case "--user" :: value :: tail =>
user = value
parse(tail)
case "--password" :: value :: tail =>
password = value
parse(tail)
case "--local" :: tail =>
local = true
parse(tail)
case Nil =>
case _ =>
printUsageAndExit(1)
}
private def checkArguments(): Unit = {
if ("".equals(tableName) || "".equals(ckHost)) {
printUsageAndExit(2)
}
}
private def getCKJdbcProperties(
user: String,
password: String,
batchSize: String = "1000",
socketTimeout: String = "300000",
numPartitions: String = "8",
rewriteBatchedStatements: String = "true"): Properties = {
val kvMap = ImmutableMap.builder()
.put("driver", "ru.yandex.clickhouse.ClickHouseDriver")
.put("user", user)
.put("password", password)
.put("batchsize", batchSize)
.put("socket_timeout", socketTimeout)
.put("numPartitions", numPartitions)
.put("rewriteBatchedStatements", rewriteBatchedStatements)
.build()
val properties = new Properties
properties.putAll(kvMap)
properties
}
private def getSparkSession: SparkSession = {
val builder = SparkSession.builder()
if (local) {
builder.master("local[*]")
}
builder.appName("ClickHouse-Data-Importer")
builder.getOrCreate()
}
}
操作流程
步骤一:创建 ClickHouse 表
使用 SSH 方式登录 ClickHouse 集群,详情请参见使用SSH连接主节点:
https://help.aliyun.com/document_detail/169150.htm执行如下命令,进入 ClickHouse 客户端。
clickhouse-client
创建ClickHouse信息。
i. 执行如下命令,创建数据库clickhouse_database_name
CREATE DATABASE clickhouse_database_name ON CLUSTER cluster_emr;
阿里云EMR会为 ClickHouse 集群自动生成一个名为 cluster_emr 的集群。数据库名您可以自定义。
ii. 执行如下命令,创建表clickhouse_table_name_local。
CREATE TABLE clickhouse_database_name.clickhouse_table_name_local ON CLUSTER cluster_emr (
id UInt32,
key1 String,
value1 UInt8,
key2 Int64,
value2 Float64
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/clickhouse_database_name/clickhouse_table_name_local', '{replica}')
ORDER BY id
说明:表名您可以自定义,但请确保表名是以 _local 结尾。layer、shard 和 replica 是阿里云 EMR 为 ClickHouse 集群自动生成的宏定义,可以直接使用。
iii. 执行如下命令,创建与表 clickhouse_table_name_local 字段定义一致的表 clickhouse_table_name_all。
说明: 表名您可以自定义,但请确保表名是以 _all 结尾。
CREATE TABLE clickhouse_database_name.clickhouse_table_name_all ON CLUSTER cluster_emr (
id UInt32,
key1 String,
value1 UInt8,
key2 Int64,
value2 Float64
) ENGINE = Distributed(cluster_emr, clickhouse_database_name, clickhouse_table_name_local, rand());
步骤二:编译并打包
下载并解压 CKDataImporter 示例到本地:
https://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/206129/cn_zh/1618215359208/CKDataImporter.tgz在 CMD 命令行中,进入到下载文件中 pom.xml 所在的目录下,执行如下命令打包文件。
mvn clean package
根据您 pom.xml 文件中artifactId的信息,下载文件中的target目录下会出现 CKDataImporter-1.0.0.jar 的 JAR 包。
步骤三:提交作业
使用 SSH 方式登录 Spark 集群,详情请参见使用SSH连接主节点:
https://help.aliyun.com/document_detail/169150.htm执行如下命令提交作业。
spark-submit --master yarn \
--class com.aliyun.emr.CKDataImporter \
/CKDataImporter-1.0.0.jar \
--dbName clickhouse_database_name \
--tableName clickhouse_table_name_all \
--ckHost ${clickhouse_host}
参数 | 说明 |
dbName | ClickHouse集群数据库的名称,默认为default。本文示例为clickhouse_database_name。 |
tableName | ClickHouse集群数据库中表的名称。本文示例为clickhouse_table_name_all。 |
ckHost | ClickHouse集群的Master节点的内网IP地址或公网IP地址。ip地址获取方式,请参见下文获取主节点的IP地址。 |
获取主节点的 IP 地址
进入详情页面。
i. 登录