Kafka 儲存機制和副本

哥不是小蘿莉發表於2017-08-09

1.概述

  Kafka 快速穩定的發展,得到越來越多開發者和使用者的青睞。它的流行得益於它底層的設計和操作簡單,儲存系統高效,以及充分利用磁碟順序讀寫等特性,和其實時線上的業務場景。對於Kafka來說,它是一個分散式的,可分割槽的,多副本,多訂閱者的,基於Zookeeper統一協調的分散式日誌系統。常見的可以用於系統日誌,業務日誌,訊息資料等。那今天筆者給大家分析Kafka的儲存機制和副本的相關內容。

2.Replication

  Replication是Kafka的重要特性之一,針對其Kafka Brokers進行自動調優Replication數,是比較有難度的。原因之一在於要知道怎麼避免Follower進入和退出同步 ISR (In-Sync Replicas)。再訊息生產的過程當中,在有一大批海量資料寫入時,可能會引發Broker告警。如果某些Topic的部分Partition長期處於 “under replicated”,這樣是會增加丟失資料的機率的。Kafka 通過多副本機制實現高可用,確保當Kafka叢集中某一個Broker當機的情況下,仍然可用。而 Kafka 的複製演算法保證,如果Leader發生故障或者當機,一個新的Leader會被重新選舉出來,並對外提供服務,供客戶端寫入訊息。Kafka 在同步的副本列表中選舉一個副本為Leader。

  在Topic中,每個分割槽有一個預寫式日誌檔案,每個分割槽都由一系列有序,不可變的訊息組成,這些訊息被連續的追加到分割槽中,分割槽中的每個訊息都包含一個連續的序列號,即:offset。它用於確定在分割槽中的唯一位置。如下圖所示:

  在Kafka中,假如每個Topic的分割槽有N個副本,由於Kafka通過多副本機制實現故障自動轉移,這裡需要說明的是,當KafkaController出現故障,進而不能繼續管理叢集,則那些KafkaController Follower開始競選新的Leader,而啟動的過程則是在KafkaController的startup方法中完成的,如下所示:

 def startup() = {
    inLock(controllerContext.controllerLock) {
      info("Controller starting up")
      registerSessionExpirationListener()
      isRunning = true
      controllerElector.startup
      info("Controller startup complete")
    }
  }

  然後啟動ZookeeperLeaderElector,在建立臨時節點,進行session檢查,更新leaderId等操作完成後,會呼叫故障轉移函式onBecomingLeader,也就是KafkaController中的onControllerFailover方法,如下所示:

def onControllerFailover() {
    if(isRunning) {
      info("Broker %d starting become controller state transition".format(config.brokerId))
      readControllerEpochFromZookeeper()
      incrementControllerEpoch(zkUtils.zkClient)

      // before reading source of truth from zookeeper, register the listeners to get broker/topic callbacks
      registerReassignedPartitionsListener()
      registerIsrChangeNotificationListener()
      registerPreferredReplicaElectionListener()
      partitionStateMachine.registerListeners()
      replicaStateMachine.registerListeners()

      initializeControllerContext()

      // We need to send UpdateMetadataRequest after the controller context is initialized and before the state machines
      // are started. The is because brokers need to receive the list of live brokers from UpdateMetadataRequest before
      // they can process the LeaderAndIsrRequests that are generated by replicaStateMachine.startup() and
      // partitionStateMachine.startup().
      sendUpdateMetadataRequest(controllerContext.liveOrShuttingDownBrokerIds.toSeq)

      replicaStateMachine.startup()
      partitionStateMachine.startup()

      // register the partition change listeners for all existing topics on failover
      controllerContext.allTopics.foreach(topic => partitionStateMachine.registerPartitionChangeListener(topic))
      info("Broker %d is ready to serve as the new controller with epoch %d".format(config.brokerId, epoch))
      maybeTriggerPartitionReassignment()
      maybeTriggerPreferredReplicaElection()
      if (config.autoLeaderRebalanceEnable) {
        info("starting the partition rebalance scheduler")
        autoRebalanceScheduler.startup()
        autoRebalanceScheduler.schedule("partition-rebalance-thread", checkAndTriggerPartitionRebalance,
          5, config.leaderImbalanceCheckIntervalSeconds.toLong, TimeUnit.SECONDS)
      }
      deleteTopicManager.start()
    }
    else
      info("Controller has been shut down, aborting startup/failover")
  }

  正因為有這樣的機制存在,所示當Kafka叢集中的某個Broker當機後,仍然保證服務是可用的。在Kafka中發生複製操作時,確保分割槽的預寫式日誌有序的寫到其他節點,在N個複製因子中,其中一個複製因子角色為Leader,那麼其他複製因子的角色則為Follower,Leader處理分割槽的所有讀寫請求,同時,Follower會被動的定期去複製Leader上的資料。以上分析可以總結為以下幾點,如下所示:

  • Leader負責處理分割槽的所有讀寫請求。
  • Follower會複製Leader上資料。
  • Kafka 的故障自動轉移確保服務的高可用。

3.儲存

  對於訊息對應的效能評估,其檔案儲存機制設計是衡量的關鍵指標之一,在分析Kafka的儲存機制之前,我們先了解Kafka的一些概念:

  • Broker:Kafka訊息中介軟體節點,一個節點代表一個Broker,多個Broker可以組建成Kafka Brokers,即:Kafka叢集。
  • Topic:訊息儲存主題,即可以理解為業務資料名,Kafka Brokers能夠同時負責多個Topic的處理。
  • Partition:針對於Topic來說的,一個Topic上可以有多個Partition,每個Partition上的資料是有序的。
  • Segment:對於Partition更小粒度,一個Partition由多個Segment組成。
  • Offset:每個Partition上都由一系列有序的,不可變的訊息組成,這些訊息被連續追加到Partition中。而在其中有一個連續的序列號offset,用於標識訊息的唯一性。

3.1 Topic儲存

  在Kafka檔案儲存中,同一個Topic下有多個不同的Partition,每個Partition為一個單獨的目錄,Partition的命名規則為:Topic名稱+有序序號,第一個Partition序號從0開始,序號最大值等於Partition的數量減1,如下圖所示:

3.2 分割槽檔案儲存

  每個分割槽相當於一個超大的檔案被均分到多個大小相等的Segment資料檔案中,但是每個Segment訊息數量不一定相等,正因為這種特性的存在,方便了Old Segment File快速被刪除。而對於每個分割槽只需要支援順序讀寫即可,Segment檔案生命週期由服務端配置的引數決定。這樣即可快速刪除無用資料檔案,有效提高磁碟利用率。

3.3 Segment檔案儲存

  這裡,Segment檔案由Index File和Data File組成,檔案是一一對應的,字尾為 .index 表示索引檔案, .log 表示資料檔案,如下圖所示:

  如上圖所示,Segment檔案命名規則由分割槽全域性第一個Segment從0開始,後續每一個Segment檔名為上一個Segment檔案最後一個訊息的Offset值。這裡Segment資料檔案由許多訊息組成,訊息物理結構如下所示:

Key Describer
offset 用於標識每個分割槽中每條訊息的唯一性,Offset的數值標識該分割槽的第幾條訊息
message Size 訊息大小
CRC32 用CRC32校驗訊息
“magic” 當前釋出Kafka服務程式的協議版本號
“attribute” 獨立版本,或標識壓縮型別,或者編碼型別
key length key的長度
key 可選
payload length 實際訊息資料

3.4 分割槽中查詢訊息

  在分割槽中,可以通過offset偏移量來查詢訊息,如上圖中,檔案00000000000046885905.index的訊息起始偏移量為46885906=46885905+1,其他檔案依此類推,以起始偏移量命名並排序這些檔案,這樣能夠快速的定位到具體的檔案。通過segment file,當offset為46885906時,我們可以定位到00000000000046885905.index後設資料物理位置和00000000000046885905.log物理偏移地址。

4.總結

  通過對副本和儲存機制的分析,我們可以清楚的知道,Kafka通過自動故障轉移來確保服務的高可用,Leader負責分割槽的所有讀寫操作,Follower會複製Leader上的資料。Kafka針對Topic,使某一個分割槽中的大檔案分成多個小檔案,通過多個小的segment file,使之便捷定期清理或刪除已經消費的檔案,減少磁碟佔用。另外,通過索引檔案稀疏儲存,可以大幅度降低索引檔案後設資料所佔用的空間。

5.結束語

  這篇部落格就和大家分享到這裡,如果大家在研究學習的過程當中有什麼問題,可以加群進行討論或傳送郵件給我,我會盡我所能為您解答,與君共勉。

相關文章