寫在前面: 博主是一名軟件工程系大數據應用開發專業大二的學生,暱稱來源於《愛麗絲夢遊仙境》中的Alice和自己的暱稱。作爲一名互聯網小白,
寫博客一方面是爲了記錄自己的學習歷程,一方面是希望能夠幫助到很多和自己一樣處於起步階段的萌新
。由於水平有限,博客中難免會有一些錯誤,有紕漏之處懇請各位大佬不吝賜教!個人小站:http://alices.ibilibili.xyz/ , 博客主頁:https://alice.blog.csdn.net/
儘管當前水平可能不及各位大佬,但我還是希望自己能夠做得更好,因爲一天的生活就是一生的縮影
。我希望在最美的年華,做最好的自己
!
在前面的博客中,博主已經爲大家帶來了關於大數據【用戶畫像】項目匹配型標籤開發的一個步驟流程(👉大數據【企業級360°全方位用戶畫像】匹配型標籤開發)。本篇博客帶來的同樣是匹配型標籤的開發,不同於之前的是,本次標籤開發需要將最終的結果與之前的用戶標籤數據進行合併,而並非是覆寫!
想知道如何實現的朋友可以點個關注,我們繼續往下看。
文章目錄
匹配型標籤開發
本次我們開發的仍然是匹配型標籤,以Hbase中用戶表的job字段爲例。我們做一個用戶的job標籤匹配。
獲悉需求之後,我們在web頁面上通過手動添加的方式,添加了四級標籤 職業,五級標籤 不同的職業名稱。
添加完畢,我們可以在MySQL數據庫中找到對應的數據信息
再去查看Hbase表中是否存在job列的數據
scan "tbl_users",{COLUMNS => "detail:job",LIMIT => 5}
確認了MySQL和Hbase中都有job的數據後,我們就可以愉快地寫代碼了~
書寫代碼
<1>創建一個sparksession
爲了後面我們好通過觀察控制檯,分析數據的變化過程,我們還可以設置日誌級別,減少程序運行時不必要冗餘信息出現在控制檯。
// 1. 創建SparkSQL
// 用於讀取mysql , hbase等數據
val spark: SparkSession = SparkSession.builder().appName("JobTag").master("local[*]").getOrCreate()
// 設置日誌級別
spark.sparkContext.setLogLevel("WARN")
<2>連接MySQL
我們肯定是需要先讀取MySQL中的四級和五級的標籤數據的,這裏我們先進行MySQL數據庫的連接。
// 設置Spark連接MySQL所需要的字段
var url: String ="jdbc:mysql://bd001:3306/tags_new2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&user=root&password=123456"
var table: String ="tbl_basic_tag" //mysql數據表的表名
var properties:Properties = new Properties
// 連接MySQL
val mysqlConn: DataFrame = spark.read.jdbc(url,table,properties)
<3>讀取四級標籤數據
這一步,我們正式開始讀取MySQL中的四級標籤數據,爲了方便在其他地方調用,這裏我們還創建了一個方法,用於將MySQL中的數據存入Map後又用樣例類進行封裝。
需要注意的是,在進行DataSet轉換成Map,或者List的時候,需導入隱式轉換,不然程序會報錯
// 引入隱式轉換
import spark.implicits._
//引入java 和scala相互轉換
import scala.collection.JavaConverters._
//引入sparkSQL的內置函數
import org.apache.spark.sql.functions._
// 3. 讀取MySQL數據庫的四級標籤
val fourTagsDS: Dataset[Row] = mysqlConn.select("id","rule").where("id=65")
val KVMaps: Map[String, String] = fourTagsDS.map(row => {
// 獲取到rule值
val RuleValue: String = row.getAs("rule").toString
// 使用“##”對數據進行切分
val KVMaps: Array[(String, String)] = RuleValue.split("##").map(kv => {
val arr: Array[String] = kv.split("=")
(arr(0), arr(1))
})
KVMaps
}).collectAsList().get(0).toMap
println(KVMaps)
var hbaseMeta:HBaseMeta=toHBaseMeta(KVMaps)
其中樣例類代碼爲
//將mysql中的四級標籤的rule 封裝成HBaseMeta
//方便後續使用的時候方便調用
def toHBaseMeta(KVMap: Map[String, String]): HBaseMeta = {
//開始封裝
HBaseMeta(KVMap.getOrElse("inType",""),
KVMap.getOrElse(HBaseMeta.ZKHOSTS,""),
KVMap.getOrElse(HBaseMeta.ZKPORT,""),
KVMap.getOrElse(HBaseMeta.HBASETABLE,""),
KVMap.getOrElse(HBaseMeta.FAMILY,""),
KVMap.getOrElse(HBaseMeta.SELECTFIELDS,""),
KVMap.getOrElse(HBaseMeta.ROWKEY,"")
)
}
<4> 讀取五級標籤數據
這一步,我們通過手動添加的標籤值對應的pid,將該標籤下的5級標籤全部獲取。並將返回的每條數據封裝成樣例類,所有結果保存在了一個List中。
//4. 讀取mysql數據庫的五級標籤
// 匹配職業
val fiveTagsDS: Dataset[Row] = mysqlConn.select("id","rule").where("pid=65")
// 將FiveTagsDS 封裝成樣例類TagRule
val fiveTageList: List[TagRule] = fiveTagsDS.map(row => {
// row 是一條數據
// 獲取出id 和 rule
val id: Int = row.getAs("id").toString.toInt
val rule: String = row.getAs("rule").toString
// 封裝樣例類
TagRule(id,rule)
}).collectAsList() // 將DataSet轉換成util.List[TagRule] 這個類型遍歷時無法獲取id,rule數據
.asScala.toList // 將util.List轉換成list 需要隱式轉換 import scala.collection.JavaConverters._
for(a<- fiveTageList){
println(a.id+" "+a.rule)
}
<5> 讀取Hbase中的數據
基於第三步我們讀取的四級標籤的數據,我們可以通過配置信息從Hbase中讀取數據,只不過跟之前一樣,爲了加快讀取Hbase的時間,我們將其作爲一個數據源來讀取,而並非傳統的客戶端進行讀取。
// 讀取hbase中的數據,這裏將hbase作爲數據源進行讀取
val hbaseDatas: DataFrame = spark.read.format("com.czxy.tools.HBaseDataSource")
// hbaseMeta.zkHosts 就是 192.168.10.20 和 下面是兩種不同的寫法
.option("zkHosts",hbaseMeta.zkHosts)
.option(HBaseMeta.ZKPORT, hbaseMeta.zkPort)
.option(HBaseMeta.HBASETABLE, hbaseMeta.hbaseTable)
.option(HBaseMeta.FAMILY, hbaseMeta.family)
.option(HBaseMeta.SELECTFIELDS, hbaseMeta.selectFields)
.load()
// 展示一些數據
hbaseDatas.show(5)
//| id|job|
//+---+---+
//| 1| 3|
//| 10| 5|
//|100| 3|
//|101| 1|
//|102| 1|
//+---+---+
<6> 標籤匹配
這一步我們需要根據hbase數據和五級標籤的數據進行標籤匹配。
需要注意的是,匹配的時候需要使用到udf函數。
// 需要自定義UDF函數
val getUserTags: UserDefinedFunction = udf((rule: String) => {
// 設置標籤的默認值
var tagId: Int = 0
// 遍歷每一個五級標籤的rule
for (tagRule <- fiveTageList) {
if (tagRule.rule == rule) {
tagId = tagRule.id
}
}
tagId
})
// 6、使用五級標籤與Hbase的數據進行匹配獲取標籤
val jobNewTags : DataFrame = hbaseDatas.select('id.as ("userId"),getUserTags('job).as("tagsId"))
jobNewTags.show(5)
//+------+------+
//|userId|tagsId|
//+------+------+
//| 1| 68|
//| 10| 70|
//| 100| 68|
//| 101| 66|
//| 102| 66|
//+------+------+
<7>讀取hbase中歷史數據,與新數據合併
從這一步開始,真正與之前匹配完就完事的程序不同。我們需要將Hbase中的歷史數據讀取出來,與新計算的指標進行一個join合併。
其中也需要編寫udf對標籤進行拼接,並對拼接後的數據進行去重處理。
/* 定義一個udf,用於處理舊數據和新數據中的數據 */
val getAllTages: UserDefinedFunction = udf((genderOldDatas: String, jobNewTags: String) => {
if (genderOldDatas == "") {
jobNewTags
} else if (jobNewTags == "") {
genderOldDatas
} else if (genderOldDatas == "" && jobNewTags == "") {
""
} else {
val alltages: String = genderOldDatas + "," + jobNewTags //可能會出現 83,94,94
// 對重複數據去重
alltages.split(",").distinct // 83 94
// 使用逗號分隔,返回字符串類型
.mkString(",") // 83,84
}
})
// 7、解決數據覆蓋的問題
// 讀取test,追加標籤後覆蓋寫入
// 標籤去重
val genderOldDatas: DataFrame = spark.read.format("com.czxy.tools.HBaseDataSource")
// hbaseMeta.zkHosts 就是 192.168.10.20 和 下面是兩種不同的寫法
.option("zkHosts","192.168.10.20")
.option(HBaseMeta.ZKPORT, "2181")
.option(HBaseMeta.HBASETABLE, "test")
.option(HBaseMeta.FAMILY, "detail")
.option(HBaseMeta.SELECTFIELDS, "userId,tagsId")
.load()
genderOldDatas.show(5)
//+------+------+
//|userId|tagsId|
//+------+------+
//| 1| 6,68|
//| 10| 6,70|
//| 100| 6,68|
//| 101| 5,66|
//| 102| 6,66|
//+------+------+
// 新表和舊錶進行join
val joinTags: DataFrame = genderOldDatas.join(jobNewTags, genderOldDatas("userId") === jobNewTags("userId"))
val allTags: DataFrame = joinTags.select(
// 處理第一個字段
when((genderOldDatas.col("userId").isNotNull), (genderOldDatas.col("userId")))
.when((jobNewTags.col("userId").isNotNull), (jobNewTags.col("userId")))
.as("userId"),
getAllTages(genderOldDatas.col("tagsId"), jobNewTags.col("tagsId")).as("tagsId")
)
allTags.show()
//+------+------+
//|userId|tagsId|
//+------+------+
//| 296| 5,71|
//| 467| 6,71|
//| 675| 6,68|
//| 691| 5,66|
//| 829| 5,70|
<8>將最終結果寫入到Hbase(數據覆蓋)
經過第七步數據的合併之後,我們只需將最終的結果寫入到Hbase中即可。
// 將最終結果進行覆蓋
allTags.write.format("com.czxy.tools.HBaseDataSource")
.option("zkHosts", hbaseMeta.zkHosts)
.option(HBaseMeta.ZKPORT, hbaseMeta.zkPort)
.option(HBaseMeta.HBASETABLE,"test")
.option(HBaseMeta.FAMILY, "detail")
.option(HBaseMeta.SELECTFIELDS, "userId,tagsId")
.save()
這個時候我們再去查詢Hbase中test表的數據。
scan "test",{LIMIT => 5}
當發現每個用戶都有了兩個標籤值時(ps:一個是上一篇文章開發的性別標籤,另一個是我們本篇開發的工作標籤),就說明我們標籤的累計開發就成功了。
過程小結
1、爲讀取hbase,mysql數據,創建一個sparksession,設置appname,master
2、鏈接mysql數據庫,設置url,tablename, properties
3、讀取四級標籤數據
a)通過ID讀取四級數據的rule。(ID是固定死的)
b)創建四級標籤時不要直接指定jar文件名和參數等。創建完四級標籤後,開發代碼後,再在四級標籤中添加jar文件信息。
c)將讀取的字符串類型數據封裝成樣例類,以便於後續使用
i.將字符串先按照##切分數據,再按照=切分數據
ii.將切分後的數據封裝成Map
iii.最後將Map封裝成樣例類
4、讀取五級標籤數據
a)讀物數據中pid=XXX的數據,查詢出ID和rule
b)將id 和rule封裝成樣例類
c)最終返回List內部爲樣例類
5、基於第三步讀取的hbase表、列族、字段。到相應的表中讀取字段
6、根據hbase數據和五級標籤的數據進行標籤匹配
a)匹配時使用udf函數進行匹配
7、讀取hbase中歷史數據到程序中
a)將歷史數據和新計算出來的指標進行join.
b)獲取join後的用戶ID和用戶標籤,編寫UDF將標籤進行拼接
c)拼接後的數據需要進行去重
8、將最終拼接後的數據寫入hbase(數據的覆蓋)
小結
本篇博客主要在前一篇的基礎上,爲大家帶來了如何在已有標籤的情況下進行累計開發。即將原有數據和新數據進行合併,並重寫的技巧。
如果以上過程中出現了任何的紕漏錯誤,煩請大佬們指正😅
受益的朋友或對大數據技術感興趣的夥伴記得點贊關注支持一波🙏
希望我們都能在學習的道路上越走越遠😉