【问题标题】:How to design querying multiple tags on analytics database如何设计在分析数据库上查询多个标签
【发布时间】:2017-11-16 01:45:28
【问题描述】:

我想在每笔交易中存储用户购买的自定义标签,例如如果用户购买了鞋子,那么标签是"SPORTS", "NIKE", SHOES, COLOUR_BLACK, SIZE_12,..

这些标签是卖家有兴趣查询回以了解销售情况。

我的想法是,当有新标签出现时,为该标签创建新代码(类似于哈希码但顺序),代码从"a-z" 26 个字母开始,然后"aa, ab, ac...zz" 继续。现在,将一笔交易中给定的所有标签保留在名为tag (varchar) 的一列中,并用"|" 分隔。

让我们假设映射是(在应用程序级别)

"SPORTS" = a
"TENNIS" = b
"CRICKET" = c
...
...
"NIKE"  = z        //Brands company
"ADIDAS" = aa
"WOODLAND" = ab
...
...
SHOES   = ay
...
...
COLOUR_BLACK = bc
COLOUR_RED = bd
COLOUR_BLUE = be
...
SIZE_12 = cq
...

所以存储上面的购买交易,标签就像tag="|a|z|ay|bc|cq|" 现在允许卖家通过添加WHERE 条件tag LIKE %|ay|% 来搜索售出的鞋子数量。现在的问题是我不能将索引(redshift db 中的排序键)用于“LIKE 以 % 开头”。那么如何解决这个问题,因为我可能有 1 亿条记录?不想全表扫描..

有什么办法可以解决这个问题吗?

更新_1: 我没有遵循bridge table 概念(交叉引用表),因为我想在搜索指定标签后对结果执行分组。当两个标签在单个事务中匹配时,我的解决方案将只给出一行,但桥表会给我两行?那么我的 sum() 将加倍。

我得到了如下建议

存在(从 transaction_tag 中选择 1,其中 tag_id = 'zz' 和 trans_id = tr.trans_id) 在 WHERE 子句中为每个标签执行一次(注意:假设 tr 是周围查询中事务表的别名)

我没有关注这个;因为我必须对标签执行 AND 和 OR 条件,例如 ("SPORTS" AND "ADIDAS") ---- "SHOE" AND ("NIKE" OR "ADIDAS")

更新_2: 我没有关注位域,因为不知道 redshift 有这种支持,所以我假设我的系统是否将有至少 3500 个标签,并为每个标签分配一个位;这导致每笔交易有 437 个字节,尽管最多只能为一笔交易提供 5 个标签。这里有什么优化吗?

解决方案_1:

我曾考虑将最小值 (SMALL_INT) 和最大值 (SMALL_INT) 与标签列一起添加,并对其应用索引。

像这样的

"SPORTS" = a = 1
"TENNIS" = b = 2
"CRICKET" = c = 3
...
...
"NIKE"  = z  = 26
"ADIDAS" = aa = 27

所以我的列值是

`tag="|a|z|ay|bc|cq|"` //sorted?
`minTag=1`
`maxTag=95` //for cq

搜索鞋子的查询(ay=51)是

maxTag <= 51 AND tag LIKE %|ay|%

搜索鞋子(ay=51) AND SIZE_12 (cq=95) 的查询是

minTag >= 51 AND maxTag <= 95 AND tag LIKE %|ay|%|cq|%

这会带来什么好处吗?请提出任何替代方案。

【问题讨论】:

标签: performance amazon-redshift database-tuning


【解决方案1】:

您可以在文件加载到 S3 时实现自动标记。在数据库级别进行标记在此过程中为时已晚。繁琐且涉及大量硬编码

  1. 在加载到 S3 时,使用 AWS s3API 对其进行标记 下面的例子 aws s3api put-object-tagging --bucket --key --tagging "TagSet=[{Key=Addidas,Value=AY}]"

通过发送和作为参数动态捕获标签

2.将标签加载到 dynamodb 作为元数据存储

3.使用 S3 COPY 命令将数据加载到 Redshift

【讨论】:

  • 谢谢!但我需要研究一下,然后回复你。我对 S3 复制命令的理解是;它用于从文件中批量插入行。对我来说,每一行都有不同的标签,但复制命令对于所有批量插入都是一次......
【解决方案2】:

您可以将 tags 列存储为 varchar 位掩码,即严格定义的 1 或 0 的位序列,这样如果购买被标签标记,则为 1,否则为 0,依此类推。对于每个行,您将有一个 0 和 1 的序列,其长度与您拥有的标签数量相同。这个序列是可排序的,但是您仍然需要查找中间,但您会知道要查找的特定位置,因此您不需要like,只需substring。为了进一步优化,您可以将此位掩码转换为整数值(它将对每个序列都是唯一的)并基于此进行匹配,但 AFAIK Redshift 尚不支持开箱即用,您必须自己定义规则。

UPD:看起来最好的选择是将标签保存在单独的表中,并创建一个 ETL 流程,将标签解包为order_id, tag_id 的表格结构,由order_id 分发并按tag_id 排序。或者,您可以创建一个视图,将这个视图与订单表连接起来。然后查找具有特定标签的订单和订单的进一步聚合应该是有效的。在平面表中优化这一点没有灵丹妙药,至少我不知道与“关系”解决方案相比不会带来很多不必要的复杂性。

【讨论】:

  • 谢谢! @亚历克斯是的。我想到了这一点,但我假设我的系统将至少有 3500 个标签,这导致每个事务的 437 个字节,尽管最多可以为事务提供 5 个标签。这里有什么优化吗?
  • 一种方法是将所有内容编码为整数,但我在这里不能提供太多建议,因为我没有尝试过,另一种我认为更“关系”的方法 - 你创建一个表order_id, tag_id,由order_id 分发并按tag_id 排序,并创建一个将这个与订单表连接起来的视图。然后查找特定标签和进一步聚合订单应该是有效的。我不认为有任何其他方法可以在平坦的行中优化它。
  • 除上述之外,您的标签似乎不是自定义的。根据您指定的内容,它只是“名称”、“品牌”、“颜色”和“尺寸”,最好将它们作为原始值存储在单独的编码列中。
猜你喜欢
  • 2013-09-03
  • 2016-12-21
  • 2011-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多