欢迎光临
我们一直在努力

Kafka 分区策略与消息路由:自定义 Partitioner 实现业务级数据分发

一、Kafka 分区基础认知:理解消息存储与分发的基本单元

1.1 Kafka 分区基本概念

Kafka 是一个分布式流处理平台,其核心组件之一是分区(Partition)。分区是 Kafka 中消息存储和传输的基本单元,每个主题(Topic)可以被分为一个或多个分区。分区机制不仅提高了 Kafka 的并行处理能力,还支持数据的水平扩展和高可用性。

每个分区在物理上对应一个日志文件(Log),消息被追加到日志文件的末尾。每个分区中的消息都有一个唯一的、单调递增的序列号,称为偏移量(Offset)。偏移量是分区级别而非主题级别的,这意味着不同分区的偏移量是独立的。

1.2 分区在 Kafka 架构中的作用

分区在 Kafka 架构中扮演着至关重要的角色:

  • 并行处理:分区允许消费者组并行处理消息,每个消费者可以处理不同的分区,从而提高整体吞吐量。
  • 数据分布:消息被分发到不同的分区,实现了数据的分布式存储和负载均衡。
  • 容错性:当某个分区所在的 Broker 出现故障时,其他副本分区可以接管其工作,保证系统的可用性。
  • 扩展性:通过增加分区数量,可以水平扩展 Kafka 的处理能力,而不需要增加 Broker 节点。
  • 1.3 分区与消息路由的关系

    消息路由是指生产者将消息发送到特定分区的过程。Kafka 的消息路由机制基于分区策略,决定了消息将被写入到主题的哪个分区。消息路由的准确性直接影响:

    • 消息的顺序性保证
    • 消费者的负载均衡
    • 数据的局部性
    • 系统的整体吞吐量

    合理的分区策略可以确保相关消息被路由到同一分区,从而维护消息的顺序性;同时也能确保消费者负载均衡,避免某些消费者过载而其他消费者空闲的情况。

    二、Kafka 分区策略深度解析:从默认实现到业务级控制

    2.1 Kafka 内置分区策略

    Kafka 提供了多种内置的分区策略,每种策略适用于不同的场景:

  • 随机分区策略(RandomPartitioner):消息被随机分配到各个分区,适用于无顺序要求的场景。
  • 轮询分区策略(RoundRobinPartitioner):消息按照顺序轮流分配到各个分区,可以实现较好的负载均衡。
  • 基于键的分区策略(DefaultPartitioner):通过消息的键(Key)进行哈希计算,确定分区位置,确保相同 Key 的消息总是被路由到同一分区。
  • 基于时间的分区策略:根据消息的时间戳分配到不同的分区,适用于时间序列数据处理。
  • 每种内置策略都有其优缺点,适用于不同的业务场景。

    2.2 Partitioner 接口详解

    Kafka 的 Partitioner 接口是自定义分区策略的核心。生产者通过实现该接口来定义自己的分区逻辑:

    public interface Partitioner {
    /**
    * 计算消息的分区号
    * @param topic 主题名称
    * @param key 消息的键
    * @param keyBytes 消息键的字节数组
    * @param value 消息的值
    * @param valueBytes 消息值的字节数组
    * @param numPartitions 分区总数
    * @return 分区号
    */
    int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, int numPartitions);

    /**
    * 关闭分区器,释放资源
    */
    void close();

    /**
    * 配置分区器
    */
    default void configure(Map<String, ?> configs) {}
    }

    通过实现这个接口,开发者可以完全控制消息的路由逻辑,将消息按照业务规则分发到不同的分区。

    2.3 业务级分区的必要性与价值

    在实际业务场景中,内置的分区策略往往无法满足复杂的需求,业务级分区具有以下价值:

  • 数据关联性:将关联的业务数据(如同一用户的所有操作)分配到同一分区,便于后续处理和分析。
  • 性能优化:根据业务特征(如访问频率)进行分区,平衡不同分区负载。
  • 存储优化:将热数据和冷数据分开存储,优化存储成本和查询性能。
  • 安全隔离:不同安全级别的数据分区存储,满足合规性要求。
  • 灵活扩容:根据业务发展,对特定数据的分区进行独立扩容。
  • 下面是一个业务级分区决策的流程图:

    #publish-mermaid-1788279062124-0{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1788279062124-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1788279062124-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1788279062124-0 .error-icon{fill:#552222;}#publish-mermaid-1788279062124-0 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1788279062124-0 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1788279062124-0 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1788279062124-0 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1788279062124-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1788279062124-0 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1788279062124-0 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1788279062124-0 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1788279062124-0 .marker.cross{stroke:#333333;}#publish-mermaid-1788279062124-0 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-1788279062124-0 p{margin:0;}#publish-mermaid-1788279062124-0 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#publish-mermaid-1788279062124-0 .cluster-label text{fill:#333;}#publish-mermaid-1788279062124-0 .cluster-label span{color:#333;}#publish-mermaid-1788279062124-0 .cluster-label span p{background-color:transparent;}#publish-mermaid-1788279062124-0 .label text,#publish-mermaid-1788279062124-0 span{fill:#333;color:#333;}#publish-mermaid-1788279062124-0 .node rect,#publish-mermaid-1788279062124-0 .node circle,#publish-mermaid-1788279062124-0 .node ellipse,#publish-mermaid-1788279062124-0 .node polygon,#publish-mermaid-1788279062124-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1788279062124-0 .rough-node .label text,#publish-mermaid-1788279062124-0 .node .label text,#publish-mermaid-1788279062124-0 .image-shape .label,#publish-mermaid-1788279062124-0 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1788279062124-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1788279062124-0 .rough-node .label,#publish-mermaid-1788279062124-0 .node .label,#publish-mermaid-1788279062124-0 .image-shape .label,#publish-mermaid-1788279062124-0 .icon-shape .label{text-align:center;}#publish-mermaid-1788279062124-0 .node.clickable{cursor:pointer;}#publish-mermaid-1788279062124-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1788279062124-0 .arrowheadPath{fill:#333333;}#publish-mermaid-1788279062124-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1788279062124-0 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1788279062124-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1788279062124-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1788279062124-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1788279062124-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1788279062124-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1788279062124-0 .cluster text{fill:#333;}#publish-mermaid-1788279062124-0 .cluster span{color:#333;}#publish-mermaid-1788279062124-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1788279062124-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1788279062124-0 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1788279062124-0 .icon-shape,#publish-mermaid-1788279062124-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1788279062124-0 .icon-shape p,#publish-mermaid-1788279062124-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1788279062124-0 .icon-shape .label rect,#publish-mermaid-1788279062124-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1788279062124-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1788279062124-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1788279062124-0 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1788279062124-0 [data-look=\”neo\”].node rect,#publish-mermaid-1788279062124-0 [data-look=\”neo\”].cluster rect,#publish-mermaid-1788279062124-0 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062124-0 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1788279062124-0 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1788279062124-0 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062124-0 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1788279062124-0 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062124-0 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1788279062124-0 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062124-0 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062124-0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}noyes

    开始业务数据分发

    分析业务特征与需求

    确定分区键选择策略

    评估分区数量

    设计分区逻辑

    实现自定义Partitioner

    测试分区均匀性

    是否满足需求

    调整分区策略

    部署到生产环境

    监控与优化

    定期评估分区策略

    三、自定义 Partitioner 实践:从理论到业务级分发实现

    3.1 自定义 Partitioner 实现步骤

    实现自定义 Partitioner 需要遵循以下步骤:

  • 创建 Partitioner 实现类:实现 org.apache.kafka.clients.producer.Partitioner 接口。
  • 实现 partition 方法:编写业务逻辑,确定消息路由到哪个分区。
  • 实现 configure 方法:加载必要的配置参数。
  • 实现 close 方法:释放资源。
  • 配置生产者:在生产者配置中指定自定义 Partitioner 的全限定类名。
  • 测试验证:确保分区策略符合预期。
  • 下面是一个基本的自定义 Partitioner 实现模板:

    public class CustomBusinessPartitioner implements Partitioner {

    // 自定义配置参数
    private String configParam;

    @Override
    public void configure(Map<String, ?> configs) {
    // 加载配置参数
    configParam = configs.get("custom.param").toString();
    }

    @Override
    public int partition(String topic, Object key, byte[] keyBytes,
    Object value, byte[] valueBytes, int numPartitions) {
    // 业务逻辑:基于业务特征确定分区号
    if (key != null) {
    // 使用业务键的哈希值确定分区
    return (Math.abs(key.hashCode()) % numPartitions);
    } else {
    // 无键消息的轮询分配
    return ThreadLocalRandom.current().nextInt(numPartitions);
    }
    }

    @Override
    public void close() {
    // 释放资源
    }
    }

    3.2 业务级分发逻辑设计

    设计业务级分发逻辑需要考虑以下关键因素:

  • 分区键选择:选择能够代表业务特征的字段作为分区键。
  • 分区数量确定:基于业务量和处理能力确定合适的分区数量。
  • 数据倾斜处理:避免热点数据集中在少数分区。
  • 顺序性保证:有顺序要求的数据必须分配到同一分区。
  • 未来扩展性:考虑业务发展,预留分区扩展空间。
  • 下面是一个业务级分区逻辑设计的决策表:

    | 业务场景 | 分区键选择 | 分区策略 | 数据顺序保证 | 扩展性考虑 |

    |———|———–|———|————|———–|

    | 电商订单 | 用户ID | 基于用户ID哈希 | 同一用户订单顺序 | 用户增长时可能需增加分区 |

    | 日志收集 | 时间戳+来源 | 按时间范围分区 | 时间顺序 | 按时间维度扩展分区 |

    | 交易流水 | 交易类型 | 按交易类型分区 | 类型内顺序 | 新交易类型时增加分区 |

    | 用户行为 | 用户标签 | 基于标签哈希 | 无严格顺序 | 标签体系变化时调整 |

    3.3 自定义 Partitioner 最佳实践

    实现自定义 Partitioner 时,应遵循以下最佳实践:

  • 线程安全:确保 Partitioner 实例是线程安全的,因为生产者可能会在多线程环境中使用。
  • 性能优化:避免在 partition 方法中执行耗时操作,保持快速决策。
  • 异常处理:处理可能的异常情况,如无效的业务键或分区数。
  • 配置灵活性:通过配置参数增强灵活性,避免硬编码。
  • 监控与日志:记录分区决策信息,便于后续分析和调试。
  • 下面是一个包含异常处理和优化的高级实现示例:

    public class AdvancedBusinessPartitioner implements Partitioner {
    private static final Logger logger = LoggerFactory.getLogger(AdvancedBusinessPartitioner.class);
    private String businessDomain;

    @Override
    public void configure(Map<String, ?> configs) {
    businessDomain = Objects.toString(configs.get("business.domain"), "default");
    }

    @Override
    public int partition(String topic, Object key, byte[] keyBytes,
    Object value, byte[] valueBytes, int numPartitions) {
    if (numPartitions <= 0) {
    logger.error("Invalid number of partitions: {}", numPartitions);
    throw new IllegalArgumentException("Number of partitions must be positive");
    }

    try {
    if (key != null) {
    // 基于业务域和键的复合哈希
    String compositeKey = businessDomain + ":" + key.toString();
    return (Math.abs(compositeKey.hashCode()) % numPartitions);
    } else if (value != null) {
    // 无键消息,基于业务域和内容的哈希
    String compositeValue = businessDomain + ":" + value.toString();
    return (Math.abs(compositeValue.hashCode()) % numPartitions);
    } else {
    // 既无键也无值,使用轮询策略
    return ThreadLocalRandom.current().nextInt(numPartitions);
    }
    } catch (Exception e) {
    logger.error("Error during partition calculation", e);
    // 出错时回退到随机分配
    return ThreadLocalRandom.current().nextInt(numPartitions);
    }
    }

    @Override
    public void close() {
    // 清理资源
    }
    }

    四、实战案例与性能优化:订单系统中的 Kafka 分区策略

    4.1 实战案例:电商平台订单系统分区策略

    电商平台通常需要处理大量订单数据,同时保证同一用户的订单能够被顺序处理。以下是一个电商平台订单系统的自定义 Partitioner 实现方案:

  • 业务需求分析:
    • 同一用户的订单需要保持顺序
    • 订单处理要分布均衡,避免某些用户订单过多导致处理延迟
    • 支持订单查询时的快速定位
    • 未来用户量增长时能平滑扩展
  • 分区策略设计:
    • 使用用户ID作为主要分区键
    • 将用户ID进行哈希处理,分散到不同分区
    • 为大客户预留额外分区,处理其高并发订单
  • 实现代码:
  • public class EcommerceOrderPartitioner implements Partitioner {
    private static final Logger logger = LoggerFactory.getLogger(EcommerceOrderPartitioner.class);
    private Map<String, Integer> vipCustomers;
    private String vipCustomerConfig;

    @Override
    public void configure(Map<String, ?> configs) {
    // 加载VIP客户配置
    vipCustomerConfig = Objects.toString(configs.get("ecommerce.vip.customers"), "");
    vipCustomers = Arrays.stream(vipCustomerConfig.split(","))
    .collect(Collectors.toMap(
    Function.identity(),
    customer -> 0 // 初始化,实际应用中可以从配置获取预留分区数量
    ));
    }

    @Override
    public int partition(String topic, Object key, byte[] keyBytes,
    Object value, byte[] valueBytes, int numPartitions) {
    if (numPartitions <= 0) {
    throw new IllegalArgumentException("Invalid number of partitions: " + numPartitions);
    }

    try {
    // 解析订单对象获取用户ID
    String orderId = key != null ? key.toString() : "";
    Order order = parseOrder(value);
    String userId = order.getUserId();

    // 检查是否为VIP客户
    if (vipCustomers.containsKey(userId)) {
    // VIP客户订单分配到预留分区
    int vipPartition = numPartitions – vipCustomers.size() + vipCustomers.get(userId);
    logger.debug("VIP customer {} assigned to partition {}", userId, vipPartition);
    return vipPartition;
    }

    // 普通客户订单基于用户ID哈希
    int partition = Math.abs(userId.hashCode()) % (numPartitions – vipCustomers.size());
    logger.debug("Customer {} assigned to partition {}", userId, partition);
    return partition;

    } catch (Exception e) {
    logger.error("Error during order partition", e);
    // 出错时回退到随机分配
    return ThreadLocalRandom.current().nextInt(numPartitions);
    }
    }

    private Order parseOrder(Object value) {
    // 实际应用中实现订单对象解析
    return (Order) value;
    }

    @Override
    public void close() {
    // 清理资源
    }
    }

  • 生产者配置:
  • Properties props = new Properties();
    props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "broker1:9092,broker2:9092");
    props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
    props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
    props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, "com.example.EcommerceOrderPartitioner");
    props.put("ecommerce.vip.customers", "user123,user456,user789"); // VIP客户配置
    KafkaProducer<String, String> producer = new KafkaProducer<>(props);

    4.2 性能调优与监控

    自定义 Partitioner 的性能调优和监控是确保系统稳定运行的关键:

  • 性能调优:
    • 减少分区计算复杂度,避免耗时操作
    • 考虑使用缓存减少重复计算
    • 预先计算并存储某些业务键的分区信息
    • 对高频访问的键使用特殊处理逻辑
  • 监控指标:
    • 分区分配均匀性
    • 特定键的热度分析
    • 分区处理延迟
    • 错误率和异常情况
  • 调优工具与方法:
  • | 调优方法 | 适用场景 | 实施步骤 | 预期效果 |

    |———|———|———|———|

    | 分区数量调整 | 数据倾斜 | 分析各分区负载,增加热点分区数量 | 分散热点数据 |

    | 分区键优化 | 顺序性要求 | 选择更具区分度的键 | 提高分布均匀性 |

    | 缓存策略 | 重复键场景 | 缓存常见键的分区计算结果 | 减少计算开销 |

    | 异步分区计算 | 高吞吐场景 | 将分区决策异步化 | 提高生产吞吐量 |

    4.3 常见问题与解决方案

    在使用自定义 Partitioner 时,可能会遇到以下常见问题及解决方案:

  • 数据倾斜问题
    • 现象:某些分区消息量远高于其他分区
    • 原因:业务键分布不均匀或热点数据集中
    • 解决方案:
    • 优化分区键选择,增加随机性
    • 对热点数据进行特殊处理,如二次哈希
    • 考虑使用多个键的复合分区策略
  • 顺序性保证问题
    • 现象:相关消息未路由到同一分区,顺序被打乱
    • 原因:分区策略未能充分考虑业务关联性
    • 解决方案:
    • 确保相关业务数据使用相同的分区键
    • 考虑在消息内容中添加序列号,在消费者端排序
    • 对必须顺序处理的数据单独分配分区
  • 性能瓶颈
    • 现象:消息处理延迟增加
    • 原因:分区计算复杂度过高或资源竞争
    • 解决方案:
    • 简化分区逻辑,减少计算复杂度
    • 使用缓存减少重复计算
    • 考虑使用专门的线程池进行分区计算
  • 配置与部署问题
    • 现象:Partitioner 未正确加载或配置不生效
    • 原因:配置错误或类路径问题
    • 解决方案:
    • 验证 Partitioner 类名和配置是否正确
    • 确保相关依赖在类路径中
    • 检查生产者配置中的关键参数

    下面是一个故障排查的流程图:

    #publish-mermaid-1788279062182-1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1788279062182-1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1788279062182-1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1788279062182-1 .error-icon{fill:#552222;}#publish-mermaid-1788279062182-1 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1788279062182-1 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1788279062182-1 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1788279062182-1 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1788279062182-1 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1788279062182-1 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1788279062182-1 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1788279062182-1 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1788279062182-1 .marker.cross{stroke:#333333;}#publish-mermaid-1788279062182-1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-1788279062182-1 p{margin:0;}#publish-mermaid-1788279062182-1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#publish-mermaid-1788279062182-1 .cluster-label text{fill:#333;}#publish-mermaid-1788279062182-1 .cluster-label span{color:#333;}#publish-mermaid-1788279062182-1 .cluster-label span p{background-color:transparent;}#publish-mermaid-1788279062182-1 .label text,#publish-mermaid-1788279062182-1 span{fill:#333;color:#333;}#publish-mermaid-1788279062182-1 .node rect,#publish-mermaid-1788279062182-1 .node circle,#publish-mermaid-1788279062182-1 .node ellipse,#publish-mermaid-1788279062182-1 .node polygon,#publish-mermaid-1788279062182-1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1788279062182-1 .rough-node .label text,#publish-mermaid-1788279062182-1 .node .label text,#publish-mermaid-1788279062182-1 .image-shape .label,#publish-mermaid-1788279062182-1 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1788279062182-1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1788279062182-1 .rough-node .label,#publish-mermaid-1788279062182-1 .node .label,#publish-mermaid-1788279062182-1 .image-shape .label,#publish-mermaid-1788279062182-1 .icon-shape .label{text-align:center;}#publish-mermaid-1788279062182-1 .node.clickable{cursor:pointer;}#publish-mermaid-1788279062182-1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1788279062182-1 .arrowheadPath{fill:#333333;}#publish-mermaid-1788279062182-1 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1788279062182-1 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1788279062182-1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1788279062182-1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1788279062182-1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1788279062182-1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1788279062182-1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1788279062182-1 .cluster text{fill:#333;}#publish-mermaid-1788279062182-1 .cluster span{color:#333;}#publish-mermaid-1788279062182-1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1788279062182-1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1788279062182-1 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1788279062182-1 .icon-shape,#publish-mermaid-1788279062182-1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1788279062182-1 .icon-shape p,#publish-mermaid-1788279062182-1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1788279062182-1 .icon-shape .label rect,#publish-mermaid-1788279062182-1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1788279062182-1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1788279062182-1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1788279062182-1 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1788279062182-1 [data-look=\”neo\”].node rect,#publish-mermaid-1788279062182-1 [data-look=\”neo\”].cluster rect,#publish-mermaid-1788279062182-1 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062182-1 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1788279062182-1 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1788279062182-1 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062182-1 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1788279062182-1 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062182-1 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1788279062182-1 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062182-1 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1788279062182-1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}是是否否是否是nonoyes

    分区问题发生

    是否是性能问题

    分析计算复杂度

    是否逻辑复杂

    简化分区逻辑

    检查资源竞争

    优化资源使用

    是否是数据倾斜

    分析分区键分布

    优化分区策略

    是否是顺序问题

    验证分区键选择

    调整相关业务数据的分区策略

    检查配置与部署

    监控效果

    问题解决

    进一步分析

    结束

    赞(0)
    未经允许不得转载:171主机测评 » Kafka 分区策略与消息路由:自定义 Partitioner 实现业务级数据分发
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址