UID 和 TSUID
在 OpenTSDB 中,当您编写时间序列数据点时,它总是与一个度量标准和至少一个标签名称/值对相关联。首次遇到或通过 API 或 CLI 工具为每个 Metrics,标签名称和标签值分配唯一标识符(UID)。度量标准和标记名称/值对的组合将创建时间序列 UID 或 TSUID。
UID
UID 对象的类型包括:
-
metric -诸如
sys.cpu.0
或trades.per.second
之类的 Metrics -
tagk -标记名称,例如
host
或symbol
。这始终是标签键/值对中的“键”(第一个值)。 -
tagv -标记值,例如
web01
或goog
。这始终是标签键/值对中的“值”(第二个值)。
Assignment
UID 是一个正整数,对于 UID 对象的名称及其类型是唯一的。在存储系统中,存在一个针对metric
,tagk
和tagv
递增的计数器。创建新的tsdb-uid
表时,每种类型的计数器都设置为 0.因此,如果您放置一个度量值为sys.cpu.0
和标记对host=web01
的新数据点,则将有 3 个新的 UID 对象,每个对象的 UID 为 1.
将数据点写入 TSD 时,会自动为新的tagk
和tagv
对象分配 UID。 metric
对象也将接收新的 UID,但前提是* auto metric *设置已配置为true
。否则,具有新 Metrics 的数据点将被拒绝。在每个 Importing 数据点的缓存 Map 中查找 UID。如果查找失败,则 TSD 将尝试分配新的 UID。
Storage
默认情况下,UID 在存储中的 3 个字节上进行编码,每种 UID 类型的最大唯一 ID 为 16,777,215.这样做是为了减少存储空间,并减少 TSD 的内存占用。对于绝大多数用户而言,1600 万个唯一 Metrics,1600 万个唯一标签名称和 1600 万个唯一标签值就足够了。但是,如果确实需要更多特定类型的文件,则可以修改 OpenTSDB 源代码,并使用 4 个字节或更多的字节重新编译。从 2.2 版开始,您可以通过配置文件覆盖 UID 大小。
Warning
如果您确实调整了字节编码数,则必须从一个新的tsdb
和一个新的tsdb-uid
表开始,否则结果将是意外的。如果现有设置中有数据,则必须将其导出,删除所有表,从头开始创建它们并重新导入数据。
Display
UID 可以通过几种方式显示。最常见的方法是通过 HTTP API,将 3 字节的 UID 数据编码为十六进制字符串。例如,1
的 UID 将以000000000000000000000001
的二进制形式写入。作为无符号字节值的数组,您可以将其想象为[0, 0, 1]
。编码为十六进制字符串,该值将为000001
,其中该字符串的每个字节用 0 填充。 UID 255 会导致0000FF
(或作为字节数组[0, 0, 255]
)的十六进制值。要在十进制 UID 和十六进制之间转换,请使用您喜欢的任何一种十六进制转换工具,并将 0 放在结果值的前面,直到您总共有 6 个字符。要从十六进制 UID 转换为十进制,只需从前面删除任何 0,然后使用工具将十六进制字符串转换为十进制即可。
在某些 CLI 工具和日志文件中,UID 可能显示为带符号字节的数组(由于 Java),例如上面的[0, 0, 1]
或[0, 0, -28]
的示例。要从此有符号数组转换为无符号字节数组,然后转换为十六进制。例如,-28
将是二进制10011100
,这将导致十进制值156
和十六进制值9C
。
Modification
UID 可以重命名或删除。重命名可以通过 CLI 完成,通常是安全的,但会影响包括重命名 ID 的每个时间序列。例如。如果我们有一个系列sys.cpu.user host=web01
和另一个apache.requests host=web01
并将web01
标记值重命名为web01.mysite.org
,那么这两个系列现在都将反映新的主机名,并且所有引用旧名称的查询都必须更新。如果传入的数据点具有前一个字符串,将分配一个新的 UID。
从 2.2 版开始,删除 UID 可能很棘手。删除 Metrics 很安全,因为用户可能不再查询数据,并且不会显示在对建议 API 的调用中。但是,删除标签名称或值可能导致查询失败。例如。如果您具有主机web01
,web02
,web03
等的度量标准sys.cpu.user
的时间序列,并且删除了web02
的 UID,则任何将扫描包括序列sys.cpu.user host=web02
的数据的查询都会向用户抛出异常,因为数据仍然存在在存储中。强烈建议您运行带有查询的 FSCK 来修复此类问题。
Why UIDs?
这个问题经常被问到,值得在这里列出原因。在 TSD 中查找或分配 UID 会占用宝贵的周期,因此人们想知道使用度量标准的原始名称或将哈希值计算机化不是更快。确实,从写的角度来看,它会稍快一些,但是有许多缺点变得显而易见。
Raw Names
由于 OpenTSDB 使用 HBase 作为存储层,因此可以使用字符串作为行键。在当前模式之后,您可能会拥有一个类似于sys.cpu.0.user 1292148000 host=websv01.lga.mysite.com owner=operations
的行键。排序将类似于现有架构,但是现在您每小时要消耗 70 个字节的存储空间,而不是 19 个字节。此外,必须编写行键并将其与每个查询一起返回给 HBase,因此您在增加网络使用率也一样因此,诉诸 UID 可以帮助节省空间。
Hashes
另一个想法是简单地将 UID 提升到 4 个字节,然后在字符串上计算哈希值,并像我们当前所做的那样,将哈希值与正向和反向 Map 一起存储。这肯定会减少分配 UID 所需的时间,但是存在一些问题。首先,您将遇到冲突,其中不同的名称返回相同的哈希值。您可以尝试使用不同的算法,甚至尝试将哈希增加到 8 个字节,但是始终会遇到哈希冲突的问题。其次,现在您要向每个放置的数据中添加哈希计算,因为它必须确定哈希,然后在 UID 表中查找哈希以查看是否已被 Map。现在,每个数据点仅执行查找。第三,您不能轻易地预分割 HBase 区域。如果您知道系统中将有大约 800 个度量标准(标记与此无关),则可以预先拆分 HBase 表以平均分配这 800 个度量标准并提高初始写入性能。
TSUIDs
将数据点写入 OpenTSDB 时,行键的格式为<metric_UID><timestamp><tagk1_UID><tagv1_UID>[...<tagkN_UID><tagvN_UID>]
。通过简单地从行键中删除时间戳,我们可以将一长串的 UID 组合在一起,形成唯一的时间序列 ID。将字节编码为十六进制字符串将为我们提供一个有用的 TSUID,可将其传递给各种 API 调用。因此,从上面的 UID 示例(其中每个 Metrics,标签名称和值的 UID 为 1),我们的 TSUID(编码为十六进制字符串)将为000001000001000001
。
尽管此 TSUID 格式可能很长且很丑陋,尤其是对于早期 UID 而言,全为 0,但有一些原因使它有用:
-
如果您知道每个 UID 的宽度(默认情况下,如上所述为 3 个字节),则可以轻松地从 UID 字符串中解析每个 Metrics,标签名称和值的 UID。
-
为每个时间序列分配唯一的数字 ID 会导致锁争用和/或同步问题,如果 UID 无法递增,则可能会错过时间序列。