Amazon DynamoDB(多对多关系)

Amazon DynamoDB (many-to-many relationships)

我对 NoSQL 数据库还很陌生,通常没有问题,但在我最新的移动应用程序中,我尝试使用亚马逊的 DynamoDB,但遇到了困难。

一般来说,如果 table 包含唯一的单个条目,我不会有任何问题。但是,如果 table 包含与特定属性关联的项目列表,我不确定如何正确地 retrieve/query 数据。

例如下面是一个table。我们称它为AWS_DDB_SESSIONS。它包含有关用户会话的所有信息(其中 SessionID 是哈希键):

假设table数据表示如下:SessionID(hash), StartTime, StopTime, UserEmail


    Table姓名:AWS_DDB_SESSIONS
    "AAADFR101"、“01:10:00.000Z”、“11:10:00.000Z”、"john.doe@mydomain.com"
    "BBBLWL102", "12:20:00.000Z", "18:20:00.000Z", "jane.smith@mydomain.com"
    "CCCUFE103"、“03:00:00.000Z”、“03:30:00.000Z”、"john.doe@mydomain.com"


如果指定散列键值 (SessionID),我可以轻松获得单个条目。例如,如果我想要列表中的第一项 (AAADFR101),我只需使用指定的 SessionID 发出 "getItem" 请求:

亚马逊的 Java SDK 伪代码:
  AmazonSDK.API.Table.getItem("AAADFR101");

SQL 等价物是:
  select * 来自 AWS_DDB_SESSIONS 其中 SessionID='AAADFR101';

预期成绩:
  "AAADFR101", "01:10:00.000Z", "11:10:00.000Z", "john.doe@mydomain.com"


但是,如果我想获得与 "john.doe@mydomain.com" 关联的所有会话,我不知道该怎么做。


    亚马逊的 Java SDK 伪代码:
      // 创建条件...
      条件 userEmailCondition = new Condition()
                .withComparisonOperator(ComparisonOperator.EQ.toString())
                .withAttributeValueList(new AttributeValue().withS("john.doe@mydomain.com"));
      // 创建条件映射...
      映射 userEmailConditionMap = new HashMap();
      userEmailConditionMap.put("userEmail", userEmailCondition);
      QueryRequest qRequest = new QueryRequest().withTableName("AWS_DDB_SESSIONS")
                    .withKeyConditions(userEmailConditionMap);
      QueryResult = AmazonSDK.API.Table.query(qCondition);
      // 注意:这里出错是因为查询不包含 "SessionID"
      // 查询中的值(这是此 table 的主要 key/hash)。

    SQL 等价物是:
      select * 来自 AWS_DDB_SESSIONS 其中 UserID="john.doe@mydomain.com";

    预期成绩:
      "AAADFR101"、“01:10:00.000Z”、“11:10:00.000Z”、"john.doe@mydomain.com"
      "CCCUFE103"、“03:00:00.000Z”、“03:30:00.000Z”、"john.doe@mydomain.com"

如上所述,Java 伪代码不起作用,因为 Amazon 的 DynamoDB API 在您执行查询时需要主键 (SessionID)。即使您将 DynamoDB table 的架构更改为使用散列键 (SessionID) 和范围 (UserEmail),也没有用。本质上,你 运行 遇到了同样的问题。

我试图想出一些替代解决方案,但无论我如何处理它,我似乎都会回到同样的问题。



选择#1 我想到的第一个替代方案是添加另一个属性,它是一个唯一标识符。所以你会改变模式:


    原始架构:
      SessionID(散列)、StartTime、StopTime、UserEmail

    新 Table 架构:
      RecordID(哈希)、SessionID、StartTime、StopTime、UserEmail

这将创建一个类似于您将在关系数据库 (RDB) 中使用的 table。但是,它遇到了无法在UserEmail上查询的原始问题。


备选方案 #2 经过一些在线研究后,据说您可以根据各种属性 (userEmail) 将 table 拆分成更小的 table。因此,对于每个用户,您都可以创建一个 table 来存储他们的会话信息。因此,我们的原始样本将被分成 tables,其中包含以下数据:


    Table姓名:AWS_DDB_SESSIONS_jane.smith@mydomain.com
    "BBBLWL102", "12:20:00.000Z", "18:20:00.000Z"


    Table姓名:AWS_DDB_SESSIONS_john.doe@mydomain.com
    "AAADFR101", "01:10:00.000Z", "11:10:00.000Z"
    "CCCUFE103", "03:00:00.000Z", "03:30:00.000Z"

在某些情况下,建议使用这种方法,因为它会促使您朝着删除冗余数据 (UserEmail) 的方向发展,并且它可能会降低您的成本,因为它在查询 table 时扫描的数据较少。但这似乎会产生可扩展性问题。如果您有 5 个人使用您的应用程序,那么您只需担心 5 tables。但是,如果您的用户群增长到 1000、100K 或 100 万,那么您将有大量的 table 需要跟踪。所以我不确定这是否是最好的方法。


选择#3 我尝试为我认为重要的属性添加一些索引,但它并没有像我想的那样工作,并且仍然有同样的原始问题。


选择#4 将散列键从 SessionID 更改为 UserEmail,然后使用 SessionID 作为范围键。但似乎这在某些情况下可能不起作用,例如当这些值重叠、具有不同的属性字段或属性具有相同的值时。例如,如果 John Doe 有另一个 ID 为 "AAADFR101" 的会话,那么它将覆盖现有条目,而不是有 2 个具有相似信息的条目。您可以通过添加新属性 (RecordID) 并将其用作主键(哈希值)来克服这一挑战。但是,你又回到了原来的问题,你会尝试查询吗。


呃……我的头开始疼了。有人能指出我正确的方向吗?

提前致谢,

-兰迪

欢迎使用 dynamoDB,一个很棒的非关系解决方案!

如果您只想添加通过电子邮件搜索的功能,您可以使用全局索引来实现,请参阅 http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html。这将有效地让亚马逊为您保留第二个 table ……所以请考虑双倍的成本,但允许您使用任一键。由于您提到了缩放问题,如果您担心电子邮件的分布,您可以先对它们进行 md5 哈希处理。您可以考虑使用开始或停止时间作为范围键。

您将无法按属性对所有数据进行排序。如果需要,您需要寻找其他解决方案。