Kafka分區分配策略詳解

Kafka分區分配策略詳解

Kafka作為當前最流行的分布式消息隊列系統,其分區分配策略直接影響著系統的性能、可靠性和可擴展性。合理的分區分配不僅能夠提高數據處理的效率,還能確保系統負載的均衡。

Kafka提供了多種內置的分區分配策略,包括RoundRobin(輪詢)、Range(范圍)和Sticky(粘性)等,同時還支持自定義分配策略的實現。每種策略都有其特定的應用場景和優勢。

1 均衡性:確保分區盡可能均勻地分配給消費者,避免出現某些消費者負載過重而其他消費者閑置的情況。

2 穩定性:在消費者加入或離開組時,盡量減少分區的重新分配,以降低對消費過程的影響。

3 可用性:當消費者發生故障時,能夠快速進行分區重分配,確保數據能夠繼續被消費。

4 擴展性:支持動態增減消費者,能夠自動調整分區分配。

RoundRobin輪詢分配策略

RoundRobin(輪詢)是最簡單也是最常用的分區分配策略之一。該策略按照分區和消費者的字典序排序后,通過輪詢方式逐個將分區分配給消費者。這種策略的主要優點是實現簡單,分配均勻,但在某些場景下可能會導致分配不夠優化。
輪詢策略的工作流程如下:
1 收集所有可用的分區和消費者
2 對分區和消費者進行排序,確保分配的確定性
3 按照輪詢方式將分區依次分配給消費者
4 當出現消費者變化時,重新進行完整的分配

以下是RoundRobin策略的具體實現:

/*** 輪詢分區分配策略實現*/
public class RoundRobinAssignor implements PartitionAssignor {private static final Logger log = LoggerFactory.getLogger(RoundRobinAssignor.class);@Overridepublic Map<String, Assignment> assign(Cluster metadata,Map<String, Subscription> subscriptions) {// 構建消費者訂閱的主題集合Map<String, List<String>> consumerTopics = new HashMap<>();for (Map.Entry<String, Subscription> entry : subscriptions.entrySet()) {consumerTopics.put(entry.getKey(), entry.getValue().getTopics());}// 收集所有訂閱的主題分區List<TopicPartition> allPartitions = new ArrayList<>();for (String topic : getAllSubscribedTopics(consumerTopics)) {for (PartitionInfo partition : metadata.partitionsForTopic(topic)) {allPartitions.add(new TopicPartition(topic, partition.partition()));}}// 對分區進行排序Collections.sort(allPartitions, Comparator.comparing(TopicPartition::toString));// 對消費者進行排序List<String> consumers = new ArrayList<>(subscriptions.keySet());Collections.sort(consumers);// 執行分配Map<String, List<TopicPartition>> assignment = new HashMap<>();for (String consumer : consumers) {assignment.put(consumer, new ArrayList<>());}int currentPartitionIndex = 0;int currentConsumerIndex = 0;int remainingPartitions = allPartitions.size();// 輪詢分配分區while (remainingPartitions > 0) {String consumer = consumers.get(currentConsumerIndex);TopicPartition partition = allPartitions.get(currentPartitionIndex);// 檢查消費者是否訂閱了該主題if (consumerTopics.get(consumer).contains(partition.topic())) {assignment.get(consumer).add(partition);remainingPartitions--;}currentPartitionIndex = (currentPartitionIndex + 1) % allPartitions.size();currentConsumerIndex = (currentConsumerIndex + 1) % consumers.size();}// 構建返回結果Map<String, Assignment> result = new HashMap<>();for (Map.Entry<String, List<TopicPartition>> entry : assignment.entrySet()) {result.put(entry.getKey(), new Assignment(entry.getValue(), null));}// 打印分配結果logAssignment(result);return result;}/*** 獲取所有訂閱的主題*/private Set<String> getAllSubscribedTopics(Map<String, List<String>> consumerTopics) {Set<String> topics = new HashSet<>();for (List<String> subscribed : consumerTopics.values()) {topics.addAll(subscribed);}return topics;}/*** 記錄分配結果*/private void logAssignment(Map<String, Assignment> assignment) {StringBuilder builder = new StringBuilder();builder.append("Assignment results:\n");for (Map.Entry<String, Assignment> entry : assignment.entrySet()) {builder.append(String.format("\tConsumer %s -> Partitions %s\n",entry.getKey(),entry.getValue().getPartitions()));}log.info(builder.toString());}@Overridepublic String name() {return "roundrobin";}
}

這個實現包含了以下特點:

1 確定性:通過對分區和消費者進行排序,確保相同的輸入產生相同的分配結果

2 公平性:通過輪詢方式保證分區分配的均勻性

3 訂閱感知:只將分區分配給訂閱了相應主題的消費者

4 可追蹤性:通過日志記錄分配結果,便于問題排查

Range范圍分配策略

Range策略是Kafka的默認分區分配策略,它對每個主題單獨進行分區分配。該策略首先對同一個主題的分區按照分區ID進行排序,然后將消費者按照消費者ID排序,最后根據分區數量和消費者數量計算每個消費者應該分配的分區范圍。這種策略的優勢在于可以保證同一個主題的相鄰分區盡可能地分配給同一個消費者,這在某些場景下能夠提供更好的數據局部性。

Range策略的核心思想是:
按主題進行分組處理
確保分區的連續性分配
盡可能平均分配分區數量
保持分配的穩定性

以下是Range策略的實現代碼:

/*** 范圍分區分配策略實現*/
public class RangeAssignor implements PartitionAssignor {private static final Logger log = LoggerFactory.getLogger(RangeAssignor.class);@Overridepublic Map<String, Assignment> assign(Cluster metadata,Map<String, Subscription> subscriptions) {// 構建每個主題的消費者列表Map<String, List<String>> consumersPerTopic = new HashMap<>();for (Map.Entry<String, Subscription> entry : subscriptions.entrySet()) {String consumerId = entry.getKey();for (String topic : entry.getValue().getTopics()) {consumersPerTopic.computeIfAbsent(topic, t -> new ArrayList<>()).add(consumerId);}}// 對每個主題的消費者進行排序for (List<String> consumers : consumersPerTopic.values()) {Collections.sort(consumers);}// 為每個消費者初始化分配結果Map<String, List<TopicPartition>> assignment = new HashMap<>();for (String consumerId : subscriptions.keySet()) {assignment.put(consumerId, new ArrayList<>());}// 對每個主題進行分區分配for (Map.Entry<String, List<String>> topicEntry : consumersPerTopic.entrySet()) {String topic = topicEntry.getKey();List<String> consumers = topicEntry.getValue();// 獲取主題的分區數Integer numPartitionsForTopic = metadata.partitionCountForTopic(topic);if (numPartitionsForTopic == null) {continue;}// 計算每個消費者應該分配的分區數量int numPartitionsPerConsumer = numPartitionsForTopic / consumers.size();int consumersWithExtraPartition = numPartitionsForTopic % consumers.size();// 為每個消費者分配分區for (int i = 0; i < consumers.size(); i++) {String consumer = consumers.get(i);int start = numPartitionsPerConsumer * i + Math.min(i, consumersWithExtraPartition);int length = numPartitionsPerConsumer + (i < consumersWithExtraPartition ? 1 : 0);// 分配分區范圍for (int partition = start; partition < start + length; partition++) {assignment.get(consumer).add(new TopicPartition(topic, partition));}}}// 構建返回結果Map<String, Assignment> result = new HashMap<>();for (Map.Entry<String, List<TopicPartition>> entry : assignment.entrySet()) {result.put(entry.getKey(), new Assignment(entry.getValue(), null));}// 記錄分配結果logAssignment(result);return result;}/*** 記錄分配結果*/private void logAssignment(Map<String, Assignment> assignment) {StringBuilder builder = new StringBuilder();builder.append("Range assignment results:\n");for (Map.Entry<String, Assignment> entry : assignment.entrySet()) {builder.append(String.format("\tConsumer %s -> Partitions %s\n",entry.getKey(),entry.getValue().getPartitions().stream().sorted(Comparator.comparing(TopicPartition::toString)).collect(Collectors.toList())));}log.info(builder.toString());}@Overridepublic String name() {return "range";}/*** 計算分區分配的統計信息*/private void calculateAssignmentStats(Map<String, Assignment> assignment) {Map<String, Integer> partitionsPerConsumer = new HashMap<>();for (Map.Entry<String, Assignment> entry : assignment.entrySet()) {partitionsPerConsumer.put(entry.getKey(), entry.getValue().getPartitions().size());}int min = Collections.min(partitionsPerConsumer.values());int max = Collections.max(partitionsPerConsumer.values());double avg = partitionsPerConsumer.values().stream().mapToInt(Integer::intValue).average().orElse(0.0);log.info("Assignment stats - Min: {}, Max: {}, Avg: {:.2f}", min, max, avg);}
}

Sticky粘性分配策略

Sticky(粘性)分配策略是Kafka在0.11.0版本中引入的新策略,它的主要目標是在保證分區均勻分配的同時,盡可能地維持現有的分區分配,減少分區的移動。這種策略特別適合那些對分區遷移敏感的場景,例如維護了大量本地狀態的消費者。
粘性分配策略的核心原則是:

  1. 分區分配盡量均勻
  2. 每次重分配時,盡量保持已有的分配關系
  3. 必要時才進行分區移動
  4. 當消費者離開時,其分區盡量平均分配給其他消費者

以下是Sticky策略的實現代碼:

/*** 粘性分區分配策略實現*/
public class StickyAssignor implements PartitionAssignor {private static final Logger log = LoggerFactory.getLogger(StickyAssignor.class);// 記錄當前的分配方案private Map<String, List<TopicPartition>> currentAssignment;public StickyAssignor() {this.currentAssignment = new HashMap<>();}@Overridepublic Map<String, Assignment> assign(Cluster metadata,Map<String, Subscription> subscriptions) {// 獲取所有待分配的分區Set<TopicPartition> allPartitions = getAllPartitions(metadata, subscriptions);// 獲取當前活躍的消費者Set<String> consumers = subscriptions.keySet();// 構建新的分配方案Map<String, List<TopicPartition>> newAssignment = new HashMap<>();// 如果是首次分配,直接使用平均分配if (currentAssignment.isEmpty()) {newAssignment = assignPartitionsEvenly(allPartitions, consumers);} else {// 否則嘗試保持現有分配newAssignment = reassignPartitions(allPartitions, consumers);}// 更新當前分配方案currentAssignment = newAssignment;// 構建返回結果Map<String, Assignment> result = new HashMap<>();for (Map.Entry<String, List<TopicPartition>> entry : newAssignment.entrySet()) {result.put(entry.getKey(), new Assignment(entry.getValue(), null));}// 記錄分配結果logAssignment(result);return result;}/*** 重新分配分區,盡量保持現有分配*/private Map<String, List<TopicPartition>> reassignPartitions(Set<TopicPartition> allPartitions, Set<String> consumers) {Map<String, List<TopicPartition>> assignment = new HashMap<>();// 初始化消費者的分配列表for (String consumer : consumers) {assignment.put(consumer, new ArrayList<>());}// 找出需要重新分配的分區Set<TopicPartition> partitionsToReassign = new HashSet<>(allPartitions);// 保留現有的有效分配for (Map.Entry<String, List<TopicPartition>> entry : currentAssignment.entrySet()) {String consumer = entry.getKey();if (consumers.contains(consumer)) {List<TopicPartition> partitions = entry.getValue();for (TopicPartition partition : partitions) {if (allPartitions.contains(partition)) {assignment.get(consumer).add(partition);partitionsToReassign.remove(partition);}}}}// 計算目標分配數量int targetPartitionsPerConsumer = allPartitions.size() / consumers.size();int consumersWithExtraPartition = allPartitions.size() % consumers.size();// 重新分配剩余分區List<String> sortedConsumers = new ArrayList<>(consumers);Collections.sort(sortedConsumers);for (TopicPartition partition : partitionsToReassign) {// 找到分配數量最少的消費者String selectedConsumer = findConsumerWithLeastPartitions(assignment, targetPartitionsPerConsumer, consumersWithExtraPartition);assignment.get(selectedConsumer).add(partition);}return assignment;}/*** 查找分配數量最少的消費者*/private String findConsumerWithLeastPartitions(Map<String, List<TopicPartition>> assignment,int targetPartitionsPerConsumer,int consumersWithExtraPartition) {String selectedConsumer = null;int minPartitions = Integer.MAX_VALUE;for (Map.Entry<String, List<TopicPartition>> entry : assignment.entrySet()) {int currentPartitions = entry.getValue().size();if (currentPartitions < minPartitions) {minPartitions = currentPartitions;selectedConsumer = entry.getKey();}}return selectedConsumer;}/*** 平均分配分區(用于首次分配)*/private Map<String, List<TopicPartition>> assignPartitionsEvenly(Set<TopicPartition> allPartitions, Set<String> consumers) {Map<String, List<TopicPartition>> assignment = new HashMap<>();List<TopicPartition> partitionList = new ArrayList<>(allPartitions);Collections.sort(partitionList, Comparator.comparing(TopicPartition::toString));List<String> consumerList = new ArrayList<>(consumers);Collections.sort(consumerList);for (String consumer : consumerList) {assignment.put(consumer, new ArrayList<>());}int currentConsumerIndex = 0;for (TopicPartition partition : partitionList) {String consumer = consumerList.get(currentConsumerIndex);assignment.get(consumer).add(partition);currentConsumerIndex = (currentConsumerIndex + 1) % consumerList.size();}return assignment;}@Overridepublic String name() {return "sticky";}
}

自定義分配策略實現

在某些特定場景下,Kafka內置的分配策略可能無法滿足業務需求,這時我們需要實現自定義的分區分配策略。例如,我們可能需要考慮消費者的機器配置、網絡帶寬、地理位置等因素,或者需要實現特定的業務邏輯。

以下是一個考慮消費者權重的自定義分配策略實現:

/*** 基于權重的自定義分區分配策略*/
public class WeightedAssignor implements PartitionAssignor {private static final Logger log = LoggerFactory.getLogger(WeightedAssignor.class);// 消費者權重配置private final Map<String, Integer> consumerWeights;public WeightedAssignor(Map<String, Integer> weights) {this.consumerWeights = weights;}@Overridepublic Map<String, Assignment> assign(Cluster metadata,Map<String, Subscription> subscriptions) {// 收集所有分區Set<TopicPartition> allPartitions = getAllPartitions(metadata, subscriptions);// 計算總權重int totalWeight = calculateTotalWeight(subscriptions.keySet());// 按權重分配分區Map<String, List<TopicPartition>> assignment = assignByWeight(allPartitions, subscriptions.keySet(), totalWeight);// 構建返回結果Map<String, Assignment> result = new HashMap<>();for (Map.Entry<String, List<TopicPartition>> entry : assignment.entrySet()) {result.put(entry.getKey(), new Assignment(entry.getValue(), null));}// 記錄分配結果logAssignment(result);return result;}/*** 按權重分配分區*/private Map<String, List<TopicPartition>> assignByWeight(Set<TopicPartition> partitions,Set<String> consumers,int totalWeight) {Map<String, List<TopicPartition>> assignment = new HashMap<>();List<TopicPartition> sortedPartitions = new ArrayList<>(partitions);Collections.sort(sortedPartitions, Comparator.comparing(TopicPartition::toString));// 初始化分配結果for (String consumer : consumers) {assignment.put(consumer, new ArrayList<>());}// 計算每個消費者應該分配的分區數量Map<String, Integer> targetAssignments = calculateTargetAssignments(consumers, totalWeight, partitions.size());// 執行分配int currentIndex = 0;for (String consumer : consumers) {int targetCount = targetAssignments.get(consumer);for (int i = 0; i < targetCount && currentIndex < sortedPartitions.size(); i++) {assignment.get(consumer).add(sortedPartitions.get(currentIndex++));}}return assignment;}/*** 計算目標分配數量*/private Map<String, Integer> calculateTargetAssignments(Set<String> consumers,int totalWeight,int totalPartitions) {Map<String, Integer> targets = new HashMap<>();int remainingPartitions = totalPartitions;// 按權重比例計算基本分配數量for (String consumer : consumers) {int weight = consumerWeights.getOrDefault(consumer, 1);int target = (int) Math.floor((double) totalPartitions * weight / totalWeight);targets.put(consumer, target);remainingPartitions -= target;}// 分配剩余的分區List<String> sortedConsumers = new ArrayList<>(consumers);Collections.sort(sortedConsumers, (c1, c2) -> {int w1 = consumerWeights.getOrDefault(c1, 1);int w2 = consumerWeights.getOrDefault(c2, 1);return w2 - w1;  // 權重大的優先獲得剩余分區});int index = 0;while (remainingPartitions > 0) {String consumer = sortedConsumers.get(index);targets.put(consumer, targets.get(consumer) + 1);remainingPartitions--;index = (index + 1) % sortedConsumers.size();}return targets;}/*** 計算總權重*/private int calculateTotalWeight(Set<String> consumers) {return consumers.stream().mapToInt(c -> consumerWeights.getOrDefault(c, 1)).sum();}/*** 記錄分配結果和權重信息*/private void logAssignment(Map<String, Assignment> assignment) {StringBuilder builder = new StringBuilder();builder.append("Weighted assignment results:\n");for (Map.Entry<String, Assignment> entry : assignment.entrySet()) {String consumer = entry.getKey();int weight = consumerWeights.getOrDefault(consumer, 1);builder.append(String.format("\tConsumer %s (weight=%d) -> Partitions %s\n",consumer,weight,entry.getValue().getPartitions()));}log.info(builder.toString());}@Overridepublic String name() {return "weighted";}
}

通過對Kafka分區分配策略的深入分析,我們可以看到不同策略在不同場景下的優勢和局限。Range策略適合需要保持分區連續性的場景,RoundRobin策略在追求絕對均衡時表現出色,Sticky策略則在減少分區遷移方面具有明顯優勢。而自定義策略的靈活性,則為特定業務場景提供了更多可能性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/74442.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/74442.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/74442.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C#中 String類API(函數)

字符串屬性 string str "打工人";Console.WriteLine(str);char s str[0];Console.WriteLine(s); 字符串內置API(函數) 1. Concat 拼接字符串 string s1 "打";string s2 "工";string s3 "人";string sthstring.Concat(s1, s2, s…

JavaScript性能優化實戰手冊:從V8引擎到React的毫秒級性能革命

目錄 一、性能優化的本質挑戰1.1 瀏覽器渲染管線的性能瓶頸2.1 內存管理優化2.2 執行效率優化2.3 網絡傳輸優化 三、React框架深度調優3.1 渲染性能優化3.2 性能監控體系 四、企業級優化案例4.1 電商平臺首頁優化4.2 數據可視化大屏優化 五、新一代性能優化技術5.1 WASM性能突破…

【PostgreSQL】pg各版本選用取舍邏輯與docker安裝postgres:15

企業常用 PostgreSQL 版本推薦 1. PostgreSQL 14&#xff08;最常見&#xff0c;穩定&#xff09; 目前許多企業仍在使用 PostgreSQL 14&#xff0c;因為它在性能、并發處理、JSON 支持等方面做了較多優化&#xff0c;同時又非常穩定。官方支持時間&#xff1a;2026 年 11 月…

DeepSeek 助力 Vue3 開發:打造絲滑的表格(Table)之添加導出數據功能

前言:哈嘍,大家好,今天給大家分享一篇文章!并提供具體代碼幫助大家深入理解,徹底掌握!創作不易,如果能幫助到大家或者給大家一些靈感和啟發,歡迎收藏+關注哦 ?? 目錄 DeepSeek 助力 Vue3 開發:打造絲滑的表格(Table)之添加導出數據功能??頁面效果??指令輸入?…

《Python實戰進階》第31集:特征工程:特征選擇與降維技術

第31集&#xff1a;特征工程&#xff1a;特征選擇與降維技術 摘要 特征工程是機器學習和數據科學中不可或缺的一環&#xff0c;其核心目標是通過選擇重要特征和降低維度來提升模型性能并減少計算復雜度。本集聚焦于特征選擇與降維技術&#xff0c;涵蓋過濾法、包裹法、嵌入法等…

避雷 :C語言中 scanf() 函數的錯誤?使用!!!

1. 返回值說明 scanf函數會返回成功匹配并賦值的輸入項個數&#xff0c;而不是返回輸入的數據。 可以通過檢查返回值數量來確認輸入是否成功。若返回值與預期不符&#xff0c;就表明輸入存在問題。 #include <stdio.h>int main() {int num;if (scanf("%d", …

Excel第41套全國人口普查

2. 導入網頁中的表格&#xff1a;數據-現有鏈接-考生文件夾&#xff1a;網頁-找到表格-點擊→變為√-導入刪除外部鏈接關系&#xff1a;數據-點擊鏈接-選中連接-刪除-確定&#xff08;套用表格格式-也會是刪除外部鏈接&#xff09;數值縮小10000倍&#xff08;除以10000即可&am…

WPS宏開發手冊——使用、工程、模塊介紹

目錄 系列文章前言1、開始1.1、宏編輯器使用步驟1.2、工程1.3、工程 系列文章 使用、工程、模塊介紹 JSA語法 第三篇練習練習題&#xff0c;持續更新中… 前言 如果你是開發人員&#xff0c;那么wps宏開發對你來說手拿把切。反之還挺吃力&#xff0c;需要嘻嘻&#xf…

EtherCAT轉CANopen配置CANopen側的PDO映射

EtherCAT轉CANopen配置CANopen側的PDO映射 在工業自動化領域&#xff0c;EtherCAT和CANopen是兩種廣泛應用的通信協議。它們各自具有獨特的優勢&#xff0c;但在某些應用場景下&#xff0c;需要將這兩種協議進行轉換以實現設備間的高效數據交換。本文將詳細介紹如何在使用Ethe…

【QT】Qt creator快捷鍵

Qt creator可以通過以下步驟快捷鍵査看調用關系&#xff1a; 1.打開代碼文件。 2.將光標放在你想要查看調用關系的函數名上。 3.按下鍵盤快捷鍵 CtrlshiftU。 4.彈出菜單中選擇“調用路徑”或“被調用路徑” 5.在彈出的窗口中可以查看函數的調用關系 折疊或展開代碼快捷鍵&…

【RHCE】LVS-NAT模式負載均衡實驗

目錄 題目 IP規劃 配置IP RS1 RS2 RS3 LVS client 配置RS 配置LVS 安裝lvs軟件 啟動ipvsadm服務 lvs規則匹配 ipvsadm部分選項 客戶端測試 總結 題目 使用LVS的 NAT 模式實現 3 臺RS的輪詢訪問&#xff0c;IP地址和主機自己規劃。 IP規劃 主機IP地址RS1-nat模…

排序算法(插入,希爾,選擇,冒泡,堆,快排,歸并)

1.插入排序 插入排序的主要思想是額外申請一個空間cur&#xff0c;讓cur一開始等于數組的第1號位置,設置i1&#xff0c;讓i-1的元素與其比較&#xff0c;如果arr[i-1]>arr[i]&#xff0c;就讓arr[i1] arr[i]&#xff0c;當進行到最后一次對比結束&#xff0c;i-1,再讓arr[…

Java——Random庫

一、作用 Random庫——生成隨機數 二、實現步驟 1.導包&#xff1a;import java.util.Random; #快捷鍵&#xff1a;“Random”回車鍵 2.取得隨機數&#xff1a;Random 變量1 new Random(); 3.調用隨機數&#xff1a;類型 變量2 變量1.nextInt(n); &#xff08;代表變量…

解線性方程組的直接方法:高斯消元法與其程序實現

解線性方程組的直接方法&#xff1a;高斯消元法與其程序實現 1.順序高斯消元法 設線性方程組 A x b \boldsymbol{Ax}\boldsymbol{b} Axb 如果 a k k ( k ) ≠ 0 a_{kk}^{\left( k \right)}\ne 0 akk(k)??0 可以通過高斯消元法轉化為等價的三角形線性方程組&#xff1a; …

LiteIDE中配置golang編譯生成無CMD窗口EXE的步驟

LiteIDE中配置golang編譯生成無CMD窗口EXE的步驟 一、環境配置1、設置GOROOT?2、配置GOPATH? 二、項目編譯參數設置1、新建/打開項目?2、修改編譯配置?3、其他優化選項&#xff08;可選&#xff09;? 三、構建與驗證1、編譯生成EXE?2、驗證無窗口效果? 四、注意事項 一、…

Maya基本操作

基本操作 按住ALT鍵&#xff0c;左鍵旋轉視角&#xff0c;中鍵平移視角&#xff0c;右鍵放大縮小視角。 按空格鍵切換4格視圖。 導入FBX格式文件后&#xff0c;無貼圖顯示。 按6鍵開啟。著色紋理顯示 坐標軸相關 修改菜單-左鍵最上面的虛線。固定修改選項窗口。 選中物體…

Windows打開ftp局域網共享

前提是windows已經設置好開機賬號密碼了&#xff0c;否則教程不適用 第一先打開電腦ftp共享配置 點擊保存即可 2.設置要共享到其他電腦的文件路徑&#xff08;如果你要共享整個盤你就設置整個盤&#xff0c;如果是共享盤中某文件就設置某文件&#xff0c;這里是某文件&#x…

overleaf中會議參考文獻使用什么標簽:inproceedings

overleaf中會議參考文獻使用什么標簽 會議論文在LaTeX文獻條目中應使用 @inproceedings 標簽,而非 @article。根據你提供的內容,修正后的格式如下: @inproceedings{asai2023self, author = {Asai, Akari and Wu, Zeqiu and Wang, Yizhong and Sil, Avirup and Hajishirzi,…

一文詳解redis

redis 5種數據類型 string 字符串是 Redis 里最基礎的數據類型&#xff0c;一個鍵對應一個值。 設置值 SET key value例如&#xff1a; SET name "John"獲取值 GET key例如&#xff1a; GET namelist 列表是簡單的字符串列表&#xff0c;按插入順序排序。 在列…

第16章:基于CNN和Transformer對心臟左心室的實驗分析及改進策略

目錄 1. 項目需求 2. 網絡選擇 2.1 UNet模塊 2.2 TransUnet 2.2.1 SE模塊 2.2.2 CBAM 2.3 關鍵代碼 3 對比試驗 3.1 unet 3.2 transformerSE 3.3 transformerCBAM 4. 結果分析 5. 推理 6. 下載 1. 項目需求 本文需要做的工作是基于CNN和Transformer的心臟左心室…