本文旨在介紹 Spark 通過JDBC讀取數(shù)據(jù)時常用的一些優(yōu)化手段
關(guān)于數(shù)據(jù)庫索引
無論使用哪種JDBC API,spark拉取數(shù)據(jù)最終都是以select語句
來執(zhí)行的府喳,所以在自定義分區(qū)條件或者指定的long型column時岖寞,都需要結(jié)合表的索引來綜合考慮稠曼,才能以更高性能并發(fā)讀取數(shù)據(jù)庫數(shù)據(jù)逊移。
API的使用可以參考文檔:Spark JDBC系列--取數(shù)的四種方式
離散型的分區(qū)字段
當使用spark拉取table_example表的數(shù)據(jù)時呼盆,使用的分區(qū)字段吧彪,并不是連續(xù)或均勻分布的土铺。這時如果簡單的按預(yù)期的numPartitions
做均分萤悴,則會造成數(shù)據(jù)傾斜瘾腰,讀取性能也會受到影響。
ID離散型例舉
背景
一般情況下稚疹,表的ID字段居灯,都會設(shè)置成自增,即使 step!=1
内狗,也是均勻分布的的怪嫌。但是當數(shù)據(jù)積累到一定程度,需要進行分庫分表時柳沙,多個實例中ID的唯一性就需要借助分庫分表中間件岩灭,使用如snowflake之類的全局唯一編號,來生成全局唯一ID了赂鲤,此時必定會出現(xiàn)一定程度的ID離散噪径。
入?yún)?/h4>
min_id:1,max_id:1000000,數(shù)據(jù)集中在:1~500柱恤,10000~20000,100000~400000 找爱。梗顺。。即存在多段不均勻分布
普通處理方式
sqlContext.read.jdbc(url,tableName, "id", 1, 1000000,400,prop)
min_id:1,max_id:1000000,數(shù)據(jù)集中在:1~500柱恤,10000~20000,100000~400000 找爱。梗顺。。即存在多段不均勻分布
sqlContext.read.jdbc(url,tableName, "id", 1, 1000000,400,prop)
此方式的分區(qū)where查詢條件车摄,會存在很多的無用查詢(返回了空結(jié)果)寺谤,劃分的task為400,但實際有效的可能只有200個,且數(shù)據(jù)還可能存在一定程度的傾斜吮播,對后續(xù)的計算產(chǎn)生影響变屁。
自定義處理方式
def getPredicates = {
//1.獲取表total數(shù)據(jù)。
//2.按numPartitions均分意狠,獲得offset粟关,可以確保每個分片的數(shù)據(jù)一致
//3.獲取每個分片內(nèi)的最大最小ID,組裝成條件數(shù)組
环戈。闷板。。實現(xiàn)細節(jié)省略
}
sqlContext.read.jdbc(url,table, getPredicates,connectionProperties)
通過自由組裝方式谷市,可以達到精確控制蛔垢,但是實現(xiàn)成本較高击孩。
ID取模方式
sqlContext.read.jdbc(url,tableName, "id%200", 1, 1000000,400,prop)
根據(jù)numPartitions
確定合理的模值迫悠,可以盡量做到數(shù)據(jù)的連續(xù),且寫法簡單巩梢,但是由于在ID字段上使用了函數(shù)計算创泄,所以索引將失效,此時需要配合其他包含索引的where條件加以輔助括蝠,才能使查詢性能最大化鞠抑。
原理:
API中的columnName
其實只會作為where條件進行簡單的拼接,所以數(shù)據(jù)庫中支持的語法忌警,都可以使用搁拙。tableName
的原理也一樣,僅會作為from 后的內(nèi)容進行拼接法绵,所以也可以寫一個子句傳入tableName
中箕速,但依然要在保證性能的前提下。
結(jié)語
不僅僅是取模操作
朋譬,數(shù)據(jù)庫語法支持的任何函數(shù)盐茎,都可以在API中傳入使用,關(guān)鍵在于性能是否達到預(yù)期徙赢。