Spark雙流join-延遲資料--double_happy

double_happy(雙喜)發表於2020-11-13
目的:
	Spark流式處理是微批次進行處理的
	那麼雙流join 的時候 如何保證各個批次 以及跨批次進行join呢???

Spark流處理進行雙流join:
	1.延遲資料會join不上
	2.該如何join

問題

Spark雙流join可能發生的情況:
	1.左右
	2.無右
	3.左無

注意:
	左右分佈代表 不同的流 有資料
	無表示 沒有資料 
	即:
		左無:就是左邊有資料,右邊沒有資料


解決思路:
	下圖

在這裡插入圖片描述

測試

Spark雙流join問題展現

這裡就不已Kafka資料進行測試 :
	使用sock資料即可
先測試資料是否打通:

code:

package code.com.hivemetabi.task

import java.io.Serializable

import code.com.common.util.ParserUtil
import code.com.hivemetabi.bean.TechBean.OrderInfo
import code.com.hivemetabi.bean.TechBean.Order
import com.common.util.ContextUtil
import org.apache.flink.api.java.utils.ParameterTool

/**
  * author : sxwang
  * date : 2020/11/13 9:36
  * version: 1.0
  */

class MultiStreamJoinTask(params: ParameterTool) extends Serializable  {
  private val className = "MultiStreamJoinTask"
  private val jobName = s"bi_${className}"
  private val batch: Int = params.getRequired("batch").toInt

  private val host: String = params.getRequired("host")
  private val port1 = params.getRequired("port1").toInt
  private val port2 = params.getRequired("port2").toInt



  def execute={
    val ssc = ContextUtil.getStreamingContext(jobName, batch)

    val s1 = ssc.socketTextStream(host,port1)
                .map(data=>{
                  val fileds = data.split(",")
                  (fileds(0),ParserUtil.arrConvert2CaseClass[Order](classOf[Order], fileds, 2))
                })
    val s2 = ssc.socketTextStream(host,port2)
                .map(data=>{
                  val fileds = data.split(",")
                  (fileds(0),ParserUtil.arrConvert2CaseClass[OrderInfo](classOf[OrderInfo], fileds, 4))
                })

    //join
    s1.fullOuterJoin(s2).print()



    ssc.start()
    ssc.awaitTermination()
  }
}
object MultiStreamJoinTask {
  def apply(params: ParameterTool): MultiStreamJoinTask = new MultiStreamJoinTask(params)

  def main(args: Array[String]): Unit = {
    val parameterTool = ParameterTool.fromArgs(args)
    MultiStreamJoinTask(parameterTool).execute
  }

}


package code.com.hivemetabi.bean

/**
  * author : sxwang
  * date : 2020/11/13 9:48
  * version: 1.0
  */
object TechBean {

  case class  Order(orderId:String,orderNum:String)

  case class OrderInfo(orderId:String,userName:String,skuName:String,skuNum:String)

  case class OrderDetail(var orderId:String,var orderNum:String,  var userName:String,var skuName:String,var skuNum:String){

    def builderOrder(order:Order): OrderDetail ={
      if(null!=order) {
        this.orderId = order.orderId
        this.orderNum = order.orderNum
      }
      this
    }

    def builderOrderInfo(orderInfo:OrderInfo):OrderDetail={
      if(null!=orderInfo) {
        this.userName=orderInfo.userName
        this.skuName=orderInfo.skuName
        this.skuNum=orderInfo.skuNum
      }
      this
    }
  }

}

結果1:

輸入資料:
port : 8888  8889

8888:
	1,200
	2,100
	3,300

8889 :
	1,sxwang,spark,2
	1,ygy,flink,3
	2,mm,miao,100
	3,xmm,miao2,200


結果:
	join 的三種情況:

	左右:
		(2,(Some(Order(2,100)),Some(OrderInfo(2,mm,miao,100))))
		(3,(Some(Order(3,300)),Some(OrderInfo(3,xmm,miao2,200))))
		(1,(Some(Order(1,200)),Some(OrderInfo(1,sxwang,spark,2))))
		(1,(Some(Order(1,200)),Some(OrderInfo(1,ygy,flink,3))))
	
	無右:
		(2,(None,Some(OrderInfo(2,mm,miao,100))))
		(3,(None,Some(OrderInfo(3,xmm,miao2,200))))
		(1,(None,Some(OrderInfo(1,sxwang,spark,2))))
		(1,(None,Some(OrderInfo(1,ygy,flink,3))))
	
	左無:
		(2,(Some(Order(2,100)),None))
		(3,(Some(Order(3,300)),None))
		(1,(Some(Order(1,200)),None))

結論:

因為上面程式碼裡面:
	沒有對 join 的情況進行做處理 
	所以 結果會有三種情況 

這就是 Spark雙流join 的問題!!

改進:

因為上面的join  你自己是不知道 到底 哪一邊沒有join上 

稍微改進一下
package code.com.hivemetabi.task

import java.io.Serializable

import code.com.common.util.ParserUtil
import code.com.hivemetabi.bean.TechBean.{Order, OrderDetail, OrderInfo}
import com.common.util.ContextUtil
import org.apache.flink.api.java.utils.ParameterTool

/**
  * author : sxwang
  * date : 2020/11/13 9:36
  * version: 1.0
  */

class MultiStreamJoinTask(params: ParameterTool) extends Serializable  {
  private val className = "MultiStreamJoinTask"
  private val jobName = s"bi_${className}"
  private val batch: Int = params.getRequired("batch").toInt

  private val host: String = params.getRequired("host")
  private val port1 = params.getRequired("port1").toInt
  private val port2 = params.getRequired("port2").toInt



  def execute={
    val ssc = ContextUtil.getStreamingContext(jobName, batch)

    val s1 = ssc.socketTextStream(host,port1)
                .map(data=>{
                  val fileds = data.split(",")
                  (fileds(0),ParserUtil.arrConvert2CaseClass[Order](classOf[Order], fileds, 2))
                })
    val s2 = ssc.socketTextStream(host,port2)
                .map(data=>{
                  val fileds = data.split(",")
                  (fileds(0),ParserUtil.arrConvert2CaseClass[OrderInfo](classOf[OrderInfo], fileds, 4))
                })

    //join
    s1.fullOuterJoin(s2).map({
      case (orderId,(Some(order),Some(orderInfo))) =>{
        OrderDetail().builderOrder(order).builderOrderInfo(orderInfo)
      }
      case (orderId,(None,Some(orderInfo))) =>{
        println("左邊沒有匹配上")
      }
      case (orderId,(Some(order),None)) =>{
        println("右邊沒有匹配上")
      }

      case _=> Nil
    }).print()


    ssc.start()
    ssc.awaitTermination()
  }
}
object MultiStreamJoinTask {
  def apply(params: ParameterTool): MultiStreamJoinTask = new MultiStreamJoinTask(params)

  def main(args: Array[String]): Unit = {
    val parameterTool = ParameterTool.fromArgs(args)
    MultiStreamJoinTask(parameterTool).execute
  }

}

結果:

左邊沒有匹配上
左邊沒有匹配上
左邊沒有匹配上
-------------------------------------------
Time: 1605236390000 ms
-------------------------------------------
()
()
()

右邊沒有匹配上
右邊沒有匹配上
-------------------------------------------
Time: 1605236400000 ms
-------------------------------------------
()
()

-------------------------------------------
Time: 1605236410000 ms
-------------------------------------------
OrderDetail(3,300,xmm,miao2,200)

-------------------------------------------
Time: 1605236420000 ms
-------------------------------------------

-------------------------------------------
Time: 1605236430000 ms
-------------------------------------------

-------------------------------------------
Time: 1605236440000 ms
-------------------------------------------

-------------------------------------------
Time: 1605236450000 ms
-------------------------------------------

-------------------------------------------
Time: 1605236460000 ms
-------------------------------------------
右邊沒有匹配上
右邊沒有匹配上
右邊沒有匹配上
-------------------------------------------
Time: 1605236470000 ms
-------------------------------------------
()
()
()

-------------------------------------------
Time: 1605236480000 ms
-------------------------------------------
左邊沒有匹配上
左邊沒有匹配上
左邊沒有匹配上
左邊沒有匹配上
-------------------------------------------
Time: 1605236490000 ms
-------------------------------------------
()
()
()
()

-------------------------------------------
Time: 1605236500000 ms
-------------------------------------------

-------------------------------------------
Time: 1605236510000 ms
-------------------------------------------

-------------------------------------------
Time: 1605236520000 ms
-------------------------------------------
OrderDetail(2,100,mm,miao,100)
OrderDetail(3,300,xmm,miao2,200)
OrderDetail(1,200,sxwang,spark,2)
OrderDetail(1,200,ygy,flink,3)

-------------------------------------------
Time: 1605236530000 ms
-------------------------------------------


解析結果:

左邊沒有匹配上:說明左邊沒有資料,右邊有資料

右邊沒有匹配上:說明右邊沒有資料,左邊有資料


注意:
我左邊輸入的資料: 3條
	1,200
	2,100
	3,300
右邊輸入的資料: 4條
	1,sxwang,spark,2
	1,ygy,flink,3
	2,mm,miao,100
	3,xmm,miao2,200


檢視有一個的結果:
	左邊沒有匹配上
	左邊沒有匹配上
	左邊沒有匹配上
	-------------------------------------------
	Time: 1605236390000 ms
	-------------------------------------------
	()
	()
	()
	
	右邊沒有匹配上
	右邊沒有匹配上
	-------------------------------------------
	Time: 1605236400000 ms
	-------------------------------------------
	()
	()
	
	-------------------------------------------
	Time: 1605236410000 ms
	-------------------------------------------
	OrderDetail(3,300,xmm,miao2,200)


說明 :
	右邊4條匹配上1條
	左邊3條匹配上1條

結果就是 :
	匹配上一條

那麼 如何都匹配上,就是我們這篇文章的重點!!!!

解決:

上面圖片開始提出了思路,基於次之上給與總結:


1.
與flink流的join原理不同的是:
	Spark雙流join是對倆個流做滿外連線 ,
	因為網路延遲等關係,不能保證每個視窗中的資料key都能匹配上,
	這樣勢必會出現三種情況:
	
(some,some),(None,some),(Some,None)

2.根據這三種情況,下面做一下詳細解析:

(some,some)—— 
	1號流和2號流中key能正常進行邏輯運算,
	但是考慮到2號流後續可能會有剩下的資料到來,
	所以需要將1號流中的key儲存到redis,以等待接下來的資料

(None,Some)—— 
	找不到1號流中對應key的資料,
	需要去redis中查詢1號流的快取,如果找不到,
	則快取起來,等待1號流

(Some,None)—— 
	找不到2號流中的資料,需要將key儲存到redis,以等待接下來的資料,
	並且去reids中找2號流的快取,如果有,則join,然後刪除2號流的快取


這裡的中間快取可以使用:
	redis、hbase、phoenix、clickhouse 很多 
	alluxio 不建議使用 為了做雙流join 使用它 維護成本太高 完全沒有必要

代續

相關文章