为什么这段代码会生成重复的ID?

时间:2019-06-07 23:00:50

标签: multithreading scala

我有以下代码来生成唯一的eventId。这不是纯GUID生成器,而是一种在不同机器/进程之间生成唯一ID的方法。

import java.util.concurrent.atomic.AtomicInteger

object EventIdGenerator extends Serializable {
  val totalBits = 64
  private val epochBits = 42
  private val partitionIdBits = 10
  private val sequenceBits = 12
  private val sequenceInt = new AtomicInteger(0)

  private val maxPartitionId = (Math.pow(2, partitionIdBits) - 1).toInt
  private val maxSequenceId = (Math.pow(2, sequenceBits) - 1).toInt

  def nextEventId(partitionId: Int): Long = {
    assert(partitionId <= maxPartitionId)

    val nextSequence = sequenceInt.incrementAndGet() % maxSequenceId

    val timestamp =  if (nextSequence == 0) {
      Thread.sleep(1)
      System.currentTimeMillis()
    } else System.currentTimeMillis()

    timestamp << (totalBits - epochBits) |
    partitionId << (totalBits - epochBits - partitionIdBits) |
    nextSequence
  }
}

应该从在不同机器上的多个JVM上运行的分布式程序中调用它。在这里,分区ID在不同的JVM和机器之间将是唯一的。

当我在单个线程中运行该程序时,它可以正常工作。但是,当我在多个线程中运行每个线程使用唯一的分区ID调用nextEventId时,有时会得到重复的事件ID。我无法弄清楚下面的代码是什么问题。

1 个答案:

答案 0 :(得分:1)

nextSequence仅具有1024个不同的值。因此,如果您的请求速率超过每毫秒1024个(对于1个分区),则有约100%的冲突机会。实际上,由于时钟精度,我认为冲突将以更小的速率出现。

我发现,如果您检测到sleep(1)溢出,则尝试与nextSequence回避。但这在多线程环境中不起作用。特定分区甚至可能永远不会看到nextSequence等于0。