在中小業務數據規模上通過clickhouse進行數據分析很適合,維護簡單操作方便,更主要的是快;接下來給大家分享下易企秀在from hive to clickhouse過程中的經驗
概述
clickhouse對hadoop生態并不友好,官方也沒有提供spark connector直接用于讀寫操作,好在雙方都支持jdbc; clickhouse支持兩種jdbc驅動實現,一種是官方自帶的8123端口的,另一種是來自第三方實現的驅動,9000端口基于tcp協議
jdbc:8123端口
這種方式是http協議實現的,整體性能差了很多 經常會出現超時的問題,且對數據壓縮支持不好,因壓縮速度跟不上寫入速度,數據寫入的過程中數據目錄會快速膨脹 導致磁盤空間打滿。
jdbc:9000端口
這種方式支持高性能寫入,數據按列組織并壓縮。
因spark jdbc的方式不支持在clickhouse中自動創建表結構,這里在插入前需要提前創建表
考慮到clickhouse中的數據維度會經常新增和縮減,表結構維護仍需自動化,我們用了一種取巧的方式,借助mysql進行橋接,因為spark jdbc方式支持在mysql自動創建表,同時clickhouse也支持create table from mysql 。
# clickhouse jdbc驅動使用1.7的版本
/data/work/spark-2/bin/spark-shell --name "to_ck_scene_model" --master yarn --packages com.github.housepower:clickhouse-native-jdbc:1.7-stable --jars /data/work/spark-2/mysql-connector-java-5.1.48/mysql-connector-java-5.1.48-bin.jar
#讀取hive中數據并轉化為 dataframe
var df=spark.sql("select * from "+tableName )
//在mysql中創建表
val prop = new java.util.Properties
prop.setProperty("user", mysqlUser)
prop.setProperty("password", mysqlPwd)
prop.setProperty("driver",mysqlDriver)
df.where("1=0").write.mode("Overwrite").jdbc(mysqlUrl,tableName, prop)
//通過mysql橋接 在clickhouse中創建表 操作后兩邊數據結構會一致
val connection = DriverManager.getConnection(ckUrl,"default","")
var pst=connection.createStatement()
pst.execute("drop table if exists "+ckTableN)
pst.execute("create table "+ckTableN+" ENGINE = MergeTree partition by ifNull(toYYYYMM("+partitionField+"),1970-01) order by "+orderFieldAndDefauV+" as SELECT * FROM mysql('#:3306', 'bigdata', "+tableName+", '"+mysqlUser+"', '"+mysqlPwd+"')")
ckDriver = "com.github.housepower.jdbc.ClickHouseDriver"
var pro = new java.util.Properties
pro.put("driver",ckDriver)
#默認寫入批次是2w,可以調大至5w
df.write.mode("append").option("batchsize", "50000").option("isolationLevel", "NONE").option("numPartitions", "1").jdbc(ckUrl,ckTableN,pro)
總結
1、在mysql 中創建表時需注意,如果hive中存在一個以上的timestamp類型的字段時會創建失敗,并報 Invalid default value for ‘update_time’ ,需要將字段先轉成string類型寫入mysql ,然后通過 alter table modify column 將string類型轉成datetime就ok了
2、同時加載clickhouse與mysql的jdbc驅動可能會出現jar沖突的問題,出現 “Accept the id of response that is not recongnized by Server”的錯誤時,需先將clickhouse的驅動移除
val dv = DriverManager.getDriver(ckUrl)
DriverManager.deregisterDriver(dv)
當MySQL相關操作執行完畢后 ,再將clickhouse驅動重新注冊一下
DriverManager.registerDriver(dv)
3、clickhouse不支持事務操作,需關閉事務 option("isolationLevel", "NONE") ,否則個別clickhouse的jdbc版本可能會報錯
4、插入記錄數偏大問題: 使用com.github.housepower:clickhouse-native-jdbc:1.6-stable版本的同學需要注意,這個版本對spark支持有問題,當單次插入數據小于默認batchsize時數據正常插入,當插入數據量超過一個batch時會出現數據不一致的問題,看了源碼發現1.6版本執行完當前batch操作后未清除batch對象,導致后面數據一直在此基礎上累加
5、如果覺得寫入速度不夠快,那么還可以通過調大num-executors或者增加batchsize;我們目前1億數據寫入用時不到20分鐘