我想有效地存储和查询用户数据。用户由唯一的UUID标识,并且可以具有数百个不同属性的值(所有布尔值,数字或字符串)。但是,对于大多数用户而言,已知属性的数量非常有限,因此大多数值为空。此外,一些属性本质上是分层的(例如,女性(是 - 否) - >喜欢_用户(是 - 否) - > likes_red_heels(是 - 否))。有1亿不同的用户,并且经常添加新的可能属性。
我正在考虑3个选项:关系表结构(例如Impala),键值存储(例如HBase)和基于JSON的数据库(例如MongoDB)。
目前的重点是执行查询(例如,有多少用户是男性,30岁以上和中国人?)
我期待您的推荐!
答案 0 :(得分:1)
我可以与类似的用例分享我的经验。我们使用HBase存储了数千个这样的属性。请注意,在我们的示例中,属性值始终为true / false / null。 Null意味着我们不能果断地判断它是虚假还是真实。
目标是
我们将所有属性编码为位图数据结构。每个属性都有唯一的偏移值(位图中的位置)。如果设置了一个位,则用户是女性或喜欢的高跟鞋。为了处理null,我们为每个用户存储了一个额外的位图。如果第一个位图中的位为false,那么我们检查第二个位图中的相同位置以查看它是否为真。如果第二个位图设置了位,我们将其视为null。
BitMap本身将占地面积减少了一个数量级。您还可以使用像Roaring Bit Maps这样的稀疏位图结构来减少存储并提高效率。
在位图(字节数组)中查找一点是一个恒定时间操作。然后我们使用HBase协处理器来执行聚合。客户端将传递布尔表达式,如
att1 && att2 && (att3 || att4)
客户端还会传递表达式中每个属性的偏移量。这使得协处理器能够基于偏移来扫描过滤行的位。
我们的行键设计基于用户ID的SHA1。这是
<FIRST 2 BYTES of SHA1><DD-MM-YYYY><40 bytes of SHA1>
这允许我们使用HBase的模糊行过滤器
我已经针对大约6000个没有稀疏位图的属性和具有稀疏位图的大约15000个属性测试了这种方法。在某些情况下,可以在位图中模拟数字属性(显然不是连续值)。
我们希望处理4-5亿用户,并且每个用户被建模大约3次(平均),因此存储了大约150亿个这样的事件(userid - date - 建模属性集)。我们还支持执行不同计数的功能,因为相同的用户可能在不同的日期有不同的建模属性。
我们在Map / Reduce中执行了所有编码,并使用HBase批量加载功能来执行极快的加载。
这种设计的优点之一是,由于我们在HDFS中保留了所有编码数据的副本,因此我们可以编写一个自定义Hive / Impala / Spark UDF来执行过滤/评估以通过SQL进行查询。此外,HDFS中的副本可以保留更长的时间(比HBase中保存的时间长)作为冷层。
我们也考虑过Apache Phoenix,但我们没有选择那个,因为我们想同时支持100个表达式的聚合,而不是一次支持一个表达式。
我希望这会有所帮助。