我可以使UUID更加随机吗?

1 投票
4 回答
1511 浏览
提问于 2025-04-16 20:46

我有一个程序,它负责把消息发送到不同的进程中。我需要让这些进程的工作量大致相同,但不需要非常精确,只要数量差不多就可以。每条消息都有一个 uuid 字段,所以我想通过这个 uuid 的值来进行负载均衡。经过测试,我发现这个 uuid 的随机性没有我想象中那么好。我发现最后一个和第一个的数量差了大约 80%。这个差距太大了,所以我想知道有没有什么算法可以让它更随机一些。

这是我的测试代码。

import uuid
from collections import Counter

COUNT = 3000

def b(length):
    holder = []
    for i in xrange(COUNT):
        holder.append(str(uuid.uuid4())[:length])
    return Counter(holder)

def num(part_count):
    sep = 0xffffffffffffffffffffffffffffffff / part_count
    parts = []
    for i in xrange(COUNT):
#        str_hex = str(uuid.uuid4())[:4]
        num = int(uuid.uuid4().hex,16)
        divide = num/sep
        if divide == part_count:
            divide = part_count - 1
        parts.append(divide)
    return Counter(parts)

if __name__ == "__main__":
    print num(200) 

然后我得到的输出是这样的:

Counter({127L: 29, 198L: 26, 55L: 25, 178L: 24, 184L: 24, 56L: 23, 132L: 23, 143L: 23, 148L: 23, 195L: 23, 16L: 21, 30L: 21, 44L: 21, 53L: 21, 97L: 21, 158L: 21, 185L: 21, 13L: 20, 146L: 20, 149L: 20, 196L: 20, 2L: 19, 11L: 19, 15L: 19, 19L: 19, 46L: 19, 58L: 19, 64L: 19, 68L: 19, 70L: 19, 89L: 19, 112L: 19, 118L: 19, 128L: 19, 144L: 19, 156L: 19, 192L: 19, 27L: 18, 41L: 18, 42L: 18, 51L: 18, 54L: 18, 85L: 18, 87L: 18, 88L: 18, 93L: 18, 94L: 18, 104L: 18, 106L: 18, 115L: 18, 4L: 17, 22L: 17, 45L: 17, 59L: 17, 79L: 17, 81L: 17, 105L: 17, 125L: 17, 138L: 17, 150L: 17, 159L: 17, 167L: 17, 194L: 17, 3L: 16, 18L: 16, 28L: 16, 31L: 16, 33L: 16, 62L: 16, 65L: 16, 83L: 16, 111L: 16, 123L: 16, 126L: 16, 133L: 16, 145L: 16, 147L: 16, 163L: 16, 166L: 16, 183L: 16, 188L: 16, 190L: 16, 5L: 15, 6L: 15, 9L: 15, 23L: 15, 26L: 15, 34L: 15, 35L: 15, 38L: 15, 69L: 15, 73L: 15, 74L: 15, 77L: 15, 82L: 15, 86L: 15, 107L: 15, 108L: 15, 109L: 15, 110L: 15, 114L: 15, 136L: 15, 141L: 15, 142L: 15, 153L: 15, 160L: 15, 169L: 15, 176L: 15, 180L: 15, 186L: 15, 0L: 14, 1L: 14, 36L: 14, 39L: 14, 43L: 14, 60L: 14, 71L: 14, 72L: 14, 76L: 14, 92L: 14, 113L: 14, 131L: 14, 135L: 14, 157L: 14, 171L: 14, 172L: 14, 181L: 14, 189L: 14, 7L: 13, 17L: 13, 20L: 13, 24L: 13, 25L: 13, 32L: 13, 47L: 13, 49L: 13, 101L: 13, 102L: 13, 117L: 13, 121L: 13, 122L: 13, 124L: 13, 130L: 13, 151L: 13, 152L: 13, 165L: 13, 179L: 13, 14L: 12, 21L: 12, 29L: 12, 50L: 12, 63L: 12, 67L: 12, 80L: 12, 84L: 12, 90L: 12, 91L: 12, 96L: 12, 120L: 12, 129L: 12, 139L: 12, 140L: 12, 182L: 12, 193L: 12, 197L: 12, 52L: 11, 75L: 11, 78L: 11, 103L: 11, 116L: 11, 119L: 11, 134L: 11, 137L: 11, 161L: 11, 173L: 11, 12L: 10, 37L: 10, 66L: 10, 98L: 10, 100L: 10, 162L: 10, 170L: 10, 175L: 10, 177L: 10, 187L: 10, 191L: 10, 199L: 10, 48L: 9, 155L: 9, 164L: 9, 174L: 9, 10L: 8, 95L: 8, 99L: 8, 168L: 8, 8L: 7, 40L: 7, 57L: 7, 61L: 7, 154L: 6})

最后一个是 6,第一个是 29,差不多有 5 倍的差距。

4 个回答

2

UUID其实不是为了随机而设计的,而是为了确保唯一性。通常它是根据计算机的名字、IP地址、日期等信息生成的。它的目的不是让值随机,而是确保每次生成的值都是不同的,并且不同计算机生成的ID不会重复。如果你想了解更多细节,可以查看官方规范 (RFC 4122)

现在,如果你的负载均衡器想用这个作为平衡的标准,我觉得你的设计有问题。如果你想让它更随机一些,可以对它进行哈希处理(比如使用sha-256),这样可以把原本的随机性分散到所有的位上(这就是哈希的作用)。

6

你的测试方法有点问题(见下文)。不过,首先给你介绍一下uuid4的实现:

def uuid4():
    """Generate a random UUID."""

    # When the system provides a version-4 UUID generator, use it.
    if _uuid_generate_random:
        _buffer = ctypes.create_string_buffer(16)
        _uuid_generate_random(_buffer)
        return UUID(bytes=_buffer.raw)

    # Otherwise, get randomness from urandom or the 'random' module.
    try:
        import os
        return UUID(bytes=os.urandom(16), version=4)
    except:
        import random
        bytes = [chr(random.randrange(256)) for i in range(16)]
        return UUID(bytes=bytes, version=4)

libuuid(也就是ctypes调用)、os.urandomrandom.randrange返回的随机性,对于大多数非加密的应用来说,应该是足够好的。

编辑:我猜测你测试方法出问题的原因是:你在计算的数字(divide)有两个偏差:首先,它是通过一个不是2的幂的数字(在这个例子中是200)来进行除法运算的,这样会引入模偏差。其次,if divide == part_count: divide = part_count - 1这段代码又增加了更多的偏差。

另外,在你能解读任何随机数生成器测试的结果之前,你需要弄清楚置信区间是什么。不过,我的统计知识不太好,所以这方面我帮不了你……

8

UUID(通用唯一识别码)并不是为了随机生成的,而是为了确保每个UUID都是独一无二的。如果你的负载均衡器需要根据UUID来进行操作,最好先把它们通过一个哈希函数处理一下,这样可以得到你想要的随机性:

import hashlib
actually_random = hashlib.sha1(uuid).digest()

撰写回答