我目前正致力于将用户当前位置历史记录存储到HBase表中的解决方案原型。 (假设有数亿用户)。 每个用户的位置试验都存储在HBase表中。 然后,这些位置跟踪将用作少数离线数据分析工作的一部分。
以下是两种主要的数据访问模式:
我应该能够从存储的位置试验中扫描特定用户的全部或部分位置(基于时间范围)。
对于离线数据分析,我应该能够扫描所有位置 在一个时间范围内的所有用户。
鉴于上述要求,我想出了以下行键设计:
<uid>_<timestamp>
其中'uid'代表用户ID,'timestamp'代表检测和保存位置的时间。
使用这种行键设计,实现访问模式#1是直接的 - 扫描请求可以有一个开始键和结束键,并且给定的时间戳附加到特定的uid。
然而,棘手的部分是访问模式#2,我正在寻求HBase专家的帮助。 因为,我需要扫描所有用户说过去6个月,我最终不会使用任何扫描操作键。这会扫描整个HBase表。我觉得效率低下。此外,我的数据大小预计会以2K /秒的写入负载更快地增长。
我看过OpenTSDB,许多人在开放论坛中指出了这一点。但我无法将该解决方案与我的数据访问模式联系起来。
我正在寻求帮助来优化此架构,这将导致避免全表扫描。
答案 0 :(得分:1)
不是将每个位置点存储在一行中,您可以将每个位置存储在其自己的列中,并使用一年的TTL。这与OpenTSDB如何对指标进行分类的想法类似,在某个时间窗口,每个指标的读取都存储在一个单独的列中。
此架构允许您扫描所有用户和扫描作业内部,手动过滤掉您不关心的日期。这仍然是全表扫描,但仅限于您的用户集,而不是您所有位置的集合。
此架构还具有以下优点:只允许一次获取或小扫描(见下文),以便用户访问其整个位置历史记录。
此架构的缺点围绕每个用户的行大小。如果每个用户都有几百或几千个数据点,那么你应该没问题。但是,如果每个用户都有数百万个位置,那么您的行大小可能会增长到与您所在地区相同的大小。由于HBase永远不会跨区域分割行,因此最终会出现由单行组成的区域,这不是最佳的。
要解决此问题,您需要为每个用户(如OpenTSDB)实施自己的签入数据存储。假设每个桶都是uid + weekOfTheYear + year。存储桶粒度在很大程度上取决于用户添加位置数据的频率。这会为每个用户创建多个行,因此需要扫描给定用户的每个存储桶。要访问特定日期范围的数据,只需使用内置于扫描仪的时间戳过滤。
答案 1 :(得分:0)
您可以做的一件简单事情是在钥匙中推广一些时间 - 例如在这种情况下添加一个月前缀,常规查询可能需要查看多个扫描(假设在常见用途中您只需要最新记录而不是大多数不会出现问题)但是运行时间越长将受到月份的限制。
顺便说一句,如果在常规使用中你想要最新的记录,你可能想要存储从最新到最旧的日期(maxlong - timestamp),以便对时间范围的查询更快
答案 2 :(得分:0)
就个人而言,我会避免在你的行键中使用基于时间的前缀。
让我指出另一个方向,你能承受数据重复吗?
如果答案是肯定的,只需创建另一个表格,其中包含作业所需的最小数据,TTL设置为6个月(如果需要,则另一个表格为3个月TTL),并立即写入所有表格(您可以根据需要缓冲对该表的写入)。此外,您的桌子是否有几个家庭,您可以将短期家庭添加到同一张桌子,但我宁愿选择不同的家庭(个人喜好)。
如果答案为否,您仍然可以进行基于时间戳的范围扫描,以避免尽可能多地读取数据。如果(正如你所说的那样)该表将有一年的TTL,你可以负担得起,这不像是只需要检索几天就可以对30年的数据进行全表扫描。
顺便说一下,我建议你根据数字uuid(modulo,crc32,md5 ......)至少包含一个2-3字节的前缀,以便在区域之间获得均匀分布并更好地处理非活动状态(或非常活跃的用户。您无法预测用户的活跃程度。