1.分區的一些基本概念規則
- 每個topic都可以被划分成一個或者多個分區(至少有一個分區),它是topic物理上的分組,在創建topic的時候指定
- 一個Partition只對應一個Broker,一個Broker可以管理多個Partition。
- 在一個分區內消息是順序的,在不同的分區之間,kafka並不保證消息的順序
- 同一個主題下,不同分區所包含的內容是不同的,每個消息被添加到分區當中時,會被分配一個偏移量(offset),它是消息在分區當中的唯一編號,kafka是通過offset來確保一個分區內的消息順序的,offset的順序並不跨越分區。
- 在需要嚴格保證消息的消費順序的場景下,需要將partition數目設為1
2.分區下的Leader和Follower
- 每個分區選一個server節點作Leader,(0個或多個)server節點做Follower。
- 每個分區有且僅有一個Leader,Leader是負責當前數據讀寫的Partition;有(0個或多個)Follower跟隨Leader,保持數據同步
- Leadre失效,會從Follower中選舉一個新的Leader
- Follower掛掉、卡住或者同步太慢,Leader會把Follower刪除,在新建個follower
- Leader和Follower跨節點同步,達到一種選舉方式,若是在一個broker上同步沒意義;每個服務器都能作為分區的一個Leader和其他分區的flowers,因此kafka是一個去中心化的集群,能被很好平衡(雖然也可以一個server上又多個leader,但是壓力會大)
3.分區如何分配到broker
- 網上查到的分配策略如下
- 將所有的broker和partition排序;
- 將第i個partition分配到第(i mode n)個broker上,這個Partition的第一個Replica存在於這個分配的Broker上,並且會作為partition的優先副本
- 將第i個partition的j個副本,放到第((i+j) mode n)個broker上
- 上述做法會有問題,每一個topic的分區0都會被分配在broker 0上,第1個分區都分配到broker 1上。直到partition的id超過broker的數量后開始重復在輪詢,這樣會導致分區更多的在前幾個broker上,這樣前面的機器的壓力比后面的機器壓力更大,反而會造成負載不均衡。同時我們也可以做實驗得到證明,真實的分配方法並不是上面這樣。
- 實際上在Kafka集群中,每一個Broker都有均等分配Partition的Leader機會,kafka是先隨機挑選一個broker放置分區0,然后再按順序放置其他分區,副本也是一樣的情況。第一個放置的分區副本一般都是 Leader,其余的都是 Follow 副本。
4.Segment
- 由於生產者生產的消息會不斷追加到log文件末尾,為防止log文件過大導致數據定位效率低下,kafka采取了分片和索引機制。將每個partition分為多個segment file,每個segment file存着message,segment由兩部分(.log數據文件 .index索引文件)組成,並且一一對應
- 目錄和file都是物理存儲於磁盤,但是kafka不支持隨機讀寫,只支持順序讀寫,有效提高磁盤利用率,而且順序讀寫速度超過內存讀寫速度,所以效率很高
- partion全局的第一個segment從0開始,后續每個segment文件名為上一個segment文件最后一條消息的offset值
- ".log"數據文件
- offset在parition(分區)內的每條消息都有一個有序的id號,這個id號被稱為偏移(offset),它可以唯一確定每條消息在parition(分區)內的位置。即offset表示partiion的第多少message
- MessageSize表示消息內容data的大小
- data為Message的具體內容
- ".index"索引文件
- 采取稀疏索引的方式,減少了索引文件的大小,相對於稠密索引節約了存儲空間,但是查找起來更費時間
- 索引包含兩個部分,分別為相對offset和position
- 消息的查找流程
- 通過offset定位數據信息在哪個文件(.log,.index)
- 找到文件后,在根據offset和文件名計算相對偏移量,可以找到index中查找到對應position????還需要試驗下
5.生產者分區策略
- 每一條下消息ProducerRecord由主題名稱、可選的分區號、可選的鍵和值組成
- 分區策略
- 如果消息ProducerRecord中指定了有效partition字段,發送記錄使用該partition
- 如果消息ProducerRecord中沒有指定partition字段
- 但指定key,則將使用key進行hash采用MurmurHash2算法,具備高運算性能及低碰撞率)選擇一個分區
- 且沒有key,則將以輪詢的方式分配一個分區,常說的“round-robin”算法
- 注意:如果key不為null,那么hash計算得到的分區號會是所有分區中的任意一個;如果key為null並且有可用分區,那么計算得到的分區號僅為可用分區中的任意一個
- 自己定義分區策略
- 隨機分區
- 創建一個類,並繼承partitioner,改寫partition
- 再修改配置文件partitioner.class=XXX._RandomPartitioner,啟動即可
- 或者prop.put("partition.class",XXX._RandomPartitioner.class)
public class _RandomPartitioner implements Partitioner{ public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster){ //獲取總的分區數 Integer partitionNum = cluster.partitionsForTopic(topic); // 隨機策略 int i = random.nextInt(partitionNum) return i } }
- hash分區(個人寫的話可以模擬下:hash算完取絕對值,在取模)
- 輪詢分區
- 分組分區
- 隨機分區