Cassandra UUID与TimeUUID的优缺点比较

63

鉴于TimeUUID在CQL中允许您方便地使用now(),您是否有任何理由不直接始终使用TimeUUID而不是普通的UUID?

3个回答

75

UUIDTIMEUUID在Cassandra中以相同的方式存储,它们实际上只代表两种不同的排序实现。

TIMEUUID列首先按其时间组件进行排序,然后再按其原始字节进行排序,而UUID列首先按其版本进行排序,如果两者都是版本1,则按其时间组件进行排序,最后按其原始字节进行排序。有趣的是,UUIDTypeTimeUUIDType之间的时间组件排序实现在Cassandra代码中是重复的,除了格式不同。

我认为UUIDTIMEUUID的问题主要是文档:如果您选择TIMEUUID,则表示您正在按照时间顺序存储内容,并且这些内容可以同时发生,因此简单的时间戳不足够。使用UUID表示您不关心顺序(即使在实践中,如果将版本1的UUID放入其中,列也会按时间排序),只是想确保事物具有唯一的ID。

即使使用NOW()生成UUID值很方便,但对于其他人阅读您的代码来说也非常令人惊讶。

总体来说,即使在大局上没有太大关系,但是对非1版本的UUID进行排序会比版本1稍快一些,因此如果您有一个UUID列并自己生成UUID,则可以选择其他版本。


1
非版本1的UUID排序如何更快?例如,版本4的UUID是完全随机的,我预计它会提供最差的排序性能。我确实同意这个问题应该是无关紧要的。如果您正在使用UUID,则有几个很好的理由,但性能不在其中。幸运的是,今天的计算机可以处理UUID所需的空间和排序要求。 - Basil Bourque
3
UUID的内容与排序算法的性能无关。在Cassandra中,非版本1的UUID排序速度更快,因为不需要将字节解包成时间戳。这只是一个非常微小的差异,我觉得这很有趣。 - Theo
现在()函数是生成timeuuid的唯一方式吗?是否可以生成自定义的timeuuid?我只需要自定义的timeuuid进行测试。 - Charlie Parker
好问题,匹诺曲。也许不是答案,但我知道有minTimeuuid()和maxTimeuuid()。例子:insertion_time < minTimeuuid('2015-04-04 22:05+0000') AND insertion_time > maxTimeuuid('2015-04-03 22:05+0000'); - Melroy van den Berg
@Theo 当你说“你只是想确保事物有唯一的ID”时,你是指timeuuid可能不是唯一的吗?例如,我可以使用timeuuid作为分区键存储用户吗? - Ced

31

根据文档TimeUUID其实就是一个普通的UUID

UUID实际上是一个128位的值想象一下这是一个非常大的数字。

特定的位可以通过多种方法确定。最原始的方法是将计算机网络硬件的MAC地址、当前日期和时间、加上一个任意数和一个随机数相结合。将所有这些内容压缩在一起,得到一个几乎是唯一的数字。

后来,由于各种原因(安全、隐私),在生成UUID值时发明了其他方法来组装位。这些其他方法省略了日期时间和/或MAC地址作为配料。重点是:并非所有UUID值都具有嵌入的日期时间值。
Cassandra文档错误地将其TimeUUID称为“Type 1 UUID”。正确的术语是版本1 UUID。该版本有时被称为“基于时间的版本”。

一些建议

Cassandra似乎用这个特定版本的UUID来提取128位中的日期和时间部分。从UUID中提取日期时间是一个不好的想法

首先,UUID从未旨在用于此类历史跟踪。事实上,UUID的规范明确承认(a)计算机时钟可能被重置,因此(b)后生成的UUID可能比以前的UUID记录了更早的日期时间。不从UUID中提取日期时间的另一个原因是,您可能会拥有并非通过时间方法生成的UUID,因此您将基于实际上并不代表创建日期时间的位构建数据时间值。第三个原因是,当编程代码以后进行重构时,UUID可能会在不同的时间生成,而数据库记录则不同,因此使用UUID的日期时间会引导错误。

如果您需要跟踪日期时间历史,请显式地进行跟踪。在您的数据中创建日期时间字段。顺便一提,要在UTC中跟踪该日期时间,但这是另一个话题。


3
值得一提的是,Cassandra文档明确建议使用NTP来同步所有节点的系统时间。http://www.datastax.com/documentation/cassandra/1.2/webhelp/cassandra/install/installRecommendSettings.html - John
17
同意使用协调世界时(UTC)。但为了解决你的其他问题:1)时间戳也会受到时钟漂移的影响,因此对于时间序列数据来说,它们在这方面并不比TimeUUID更好。2)在使用TimeUUID数据类型的Cassandra模式的CQL3上下文中,可以合理地期望这些列中的所有UUID都是时间编码的,即类型1的UUID。3)在CQL3中,您可以使用NOW()或指定的日期时间来创建插入的TimeUUID。因此,在处理旧数据时,仍然可以在Cassandra表中获得历史上正确的TimeUUID。 - platforms
2
@平台 将两个不同的目的混为一体的做法是一个原则上的坏主意,一个不好的实践。在这种情况下,1. 日期时间历史记录跟踪和2. 主键标识符。当你想要与其他系统/来源/汇集导入或导出数据时,你会后悔的。进一步证明了无谓的混淆,同时没有任何回报,请重新阅读本页面的问题! - Basil Bourque
3
到目前为止,已经在生产中使用了一年以上的系统,包括数据导出和导入,并没有任何遗憾。但我理解你的原则性观点,并且认同类似于分离关注点的思想可能是形成你观点的原因。在实践中,对于在Cassandra上索引时间序列数据的目的,我发现使用TimeUUID非常有用。但是,原则上,我会选择任何形式的UUID来存储时间值吗?不会。 - platforms
3
Cassandra 中的 TimeUUID 类型是元数据,就像任何 Cassandra 类型一样,它允许 Cassandra 知道如何解释数据(例如获取日期并创建基于日期或当前时间的 UUID)。使用它的好处是,如果您需要直接访问某一行并按日期排序列出行,则可以防止数据重复。它只有作为复合主键才有意义。如果您有两个字段(日期和唯一标识符),则将先按日期排列,在复合主键中放置 id,以进行排序,并在第二个表中将 id 放在前面,日期放在后面(用于直接访问)。 - Kazaag
在大多数情况下,记录UTC日期是最好的选择,但对于时间序列数据,最好在指定的时区中预分区数据。 - KingOfHypocrites

2
总之,你需要生成一些内容来相信它们。Timeuuids只是似乎随机化了前8个字符,因此存在一定的冲突可能性,但仍然比使用时间戳更好。如果uuid的随机性很重要,使用版本/级别4 UUID是更好的选择,几乎不可能发生冲突。因此,如果你不关心跨分区的唯一性,而你的分区是宽行时间序列数据,具有高写入并且需要每个事件(时间)的唯一标识符,那么这是一个很好的选择,也具有聚类、分页等优点。
insert into test_tuuid(1, now())
insert into test_tuuid(1, now())
insert into test_tuuid(1, now())
insert into test_tuuid(1, now())

49cbda60-961b-11e8-9854-134d5b3f9cf8
49d1a6c1-961b-11e8-9854-134d5b3f9cf8
49d59e61-961b-11e8-9854-134d5b3f9cf8
49d8d2b1-961b-11e8-9854-134d5b3f9cf8

1
实际上,前8个字符并不是随机的。CQL驱动程序会经过一些额外的步骤,以确保在生成新的TIMEUUID值时没有冲突。https://docs.datastax.com/en/cql-oss/3.x/cql/cql_reference/timeuuid_functions_r.html - Ian Goldby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接