Kafka 全局数据有序

我们知道kafka,每个partition内数据是有序的,,然后数据发送到某个topic内 单个topic内全局数据肯定是乱序的。

先通过源码来分析某条消息是如何选择计算 partition 的

/**
 * The default partitioning strategy:
 * <li>If a partition is specified in the record, use it
 * <li>If no partition is specified but a key is present choose a partition based on a hash of the key
 * <li>If no partition or key is present choose a partition in a round-robin fashion
 */
public class DefaultPartitioner implements Partitioner {

    private final ConcurrentMap<String, AtomicInteger> topicCounterMap = new ConcurrentHashMap<>();

    public void configure(Map<String, ?> configs) {}

    /**
     * Compute the partition for the given record.
     *
     * @param topic The topic name
     * @param key The key to partition on (or null if no key)
     * @param keyBytes serialized key to partition on (or null if no key)
     * @param value The value to partition on or null
     * @param valueBytes serialized value to partition on or null
     * @param cluster The current cluster metadata
     */
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
        int numPartitions = partitions.size();
        if (keyBytes == null) {
            int nextValue = nextValue(topic);
            List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
            if (availablePartitions.size() > 0) {
                int part = Utils.toPositive(nextValue) % availablePartitions.size();
                return availablePartitions.get(part).partition();
            } else {
                // no partitions are available, give a non-available partition
                return Utils.toPositive(nextValue) % numPartitions;
            }
        } else {
            // hash the keyBytes to choose a partition
            return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
        }
    }

    private int nextValue(String topic) {
        AtomicInteger counter = topicCounterMap.get(topic);
        if (null == counter) {
            counter = new AtomicInteger(ThreadLocalRandom.current().nextInt());
            AtomicInteger currentCounter = topicCounterMap.putIfAbsent(topic, counter);
            if (currentCounter != null) {
                counter = currentCounter;
            }
        }
        return counter.getAndIncrement();
    }

    public void close() {}

}

生产上produer.send消息到kafka,指定key,根据key hash求出一个固定的分区 确保相同的key可以发送到同一个partition;
也可以根据某个topic,指定某一个partition。

topic 默认是 3 个partition
我们现在公司的架构是
MySQL 的binlog ===》 Canal ===》Kafka ===》Flink ===》Kudu
通过Canal抽取MySQL的binlog 日志 写入kafka

假设 现在 kafka有个topic为 ruozedata 3个分区,
ruozedata表现在执行一波操作 ,insert ,update1,update2,update3,update4,delete
到最后还是数据为0
如果不保证全局有序结果
p0: u1,u4
p1:i,u2
p2:u3,d

这样下游消费kafka 很明显是错误的顺序会是。

解决办法:

  1. topic 只设置一个partition,这样所有的数据都往这一个partition发,能保证有序
    但是对性能可就大打折扣了,一旦数据量提升,且有隐患。

  2. 下游消费者对topic做分组时间排序,性能也差。

  3. 采用特征数据处理
    producer.send(new ProducerRecord<>(topic,messageNo,messageStr))
    对这个 方法中的messageNo做文章。
    messageNo = database.table.key
    比如上面那条操作的key为 id = 100
    messageNo = bigdata.ruozedata.100

计算partition
假设 hash(bigdata.ruozedata.100) = 99
99 % 3 = 0
数据分发到 p0 分区上
p0:insert u1 u2 u3 u4 delete
p1:
p2:

可能 bigdata.ruozedata.666 发送到了p1 分区上
p0:
p1: insert u1 u2 u3 u4 delete
p2:

这样就能保证对某一条数据的操作分发到单个partition中去,从而保证全局的有序性。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冬瓜螺旋雪碧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值