Cassandra中的一对多映射

Posted

技术标签:

【中文标题】Cassandra中的一对多映射【英文标题】:One to many mapping in Cassandra 【发布时间】:2016-09-07 14:01:19 【问题描述】:

我是 Cassandra 的新手,想对用户及其车辆进行一对多映射。一个用户可能有多个车辆。我的用户表将包含用户详细信息,如姓名、姓氏等。车辆表将包含车辆详细信息。

我的选择查询将获取特定用户的所有车辆详细信息。

我应该如何在 Cassandra 中设计这个?

【问题讨论】:

【参考方案1】:

您可以轻松地在单个表中对此进行建模:

CREATE TABLE userVehicles (
  userid text,
  vehicleid text,
  name text static,
  surname text static,
  vehicleMake text,
  vehicleModel text,
  vehicleYear text,
  PRIMARY KEY (userid,vehicleid)
);

通过这种方式,您可以一次性查询单个用户的车辆,并且您的用户数据可以是static,以便存储在分区键级别。只要用户对车辆的基数不太大(例如,用户有 1000 辆车辆),这应该可以正常工作。

我上面考虑的案例非常简单。但是,如果我的用户有很多关于 20 到 30 个字段的详细信息,并且对于车辆也是如此。您仍然会建议使用一个表并复制所有车辆的用户数据吗?

这取决于。您的用例是否需要全部退回?如果是这样,那么“是”我仍然会推荐这种方法。从 Cassandra 中获得最佳查询性能的方法是对表进行建模以适应您的查询。当 Cassandra 可以通过特定键或一系列行(按顺序存储)读取单行时,它的效果最好。您希望避免执行多个查询或编写强制 Cassandra 执行随机读取的查询。

拥有 2 个不同的表(如 User 和 Vehicle)以及 Vehicle 表的主键分别为 User_Id 和 Vehicle_Id 的后果是什么?

在分布式系统网络中,时间是敌人。通过拥有两个表,您现在进行了两个查询...假设用户与车辆的比例为 1 比 1。但如果您的用户有 8 辆汽车,您现在需要 9 次查询才能获得结果。通过上面的设计,您可以在 1 个查询中构建您的结果集(最小化网络时间)。此外,使用userid 作为分区键,该查询保证由一个节点提供服务,而不是对车辆数据的额外查询,这很可能需要联系多个节点。

【讨论】:

如果我想获取所有拥有特定车辆的用户怎么办? :D 那个 的情况下,我会构建一个额外的查询表(具有相同的数据)和一个车辆类型和用户 ID 的主键。在 Cassandra 中,磁盘很便宜,所以复制数据并不是什么大问题。 在我看来,OP 至少需要两个单独的“目录”,一个用于用户,一个用于车辆,并希望在这两个表之间执行典型的连接。这个答案使得无法通过 id 或 name 或其他任何方式访问单个车辆。 感谢亚伦的回复。我上面考虑的案例非常简单。但是,如果我的用户有很多关于 20 到 30 个字段的详细信息,并且对于车辆也是如此。您仍然建议使用一个表并复制所有车辆的用户数据吗?拥有 2 个不同的表(如 User 和 Vehicle)以及 Vehicle 表的主键分别为 User_Id 和 Vehicle_Id 的后果是什么? @Aaron,我相信“静态”是避免数据重复的关键。发现一篇很好的文章here【参考方案2】:

这看起来就像拥有两张表一样简单,一张保存您的所有车辆数据,另一张用于满足您的查询:

CREATE TABLE vehicles (
    vehicle_id bigint,
    vehicle_type int,
    vehicle_name text,
    ...
    PRIMARY KEY (vehicle_type)
)

CREATE TABLE vehicles_to_users (
    user_id bigint,
    vehicle_id bigint,
    vehicle_type int,
    vehicle_name text,
    ...
    PRIMARY KEY (user_id, vehicle_type)
)

然后你会查询:

SELECT * FROM vehicles_to_users WHERE user_id = 9;

或类似的东西来获取属于特定用户的所有特定车辆类型:

SELECT * FROM vehicles_to_users WHERE user_id = 9 AND vehicle_type = 1;

这是一种具有非规范化数据的解决方案,您应该始终考虑这种方法,而不是采用以下方法:

CREATE TABLE vehicles (
    vehicle_id bigint,
    vehicle_type int,
    vehicle_name text,
    ...
    PRIMARY KEY (vehicle_type)
)

CREATE TABLE vehicles_to_users (
    user_id bigint,
    vehicle_id bigint,
    PRIMARY KEY (user_id)
)

因为它属于关系数据库领域,您必须运行 N+1 次查询才能满足您的要求:一次获取属于特定用户的所有 id,然后进行 N 次查询以获取每个用户的所有信息车辆:

SELECT * FROM vehicles_to_users WHERE user_id = 9;
SELECT * FROM vehicles WHERE vehicle_id = 115;
SELECT * FROM vehicles WHERE vehicle_id = 116;
SELECT * FROM vehicles WHERE vehicle_id = ...;

不要试图像这样使用IN clausole:

SELECT * FROM vehicles WHERE vehicle_id IN (115,116,....);

因为协调节点必须做的额外工作会导致性能更差。

【讨论】:

感谢您的回复。但是我不明白为什么我们有车辆表时需要车辆表?还是您在上面定义 Vehicle 表而不是 User 表? 对不起,我可能不太清楚。我假设你已经有了users 表,我没有写它,因为它很普通。我假设你已经有了vehicles 表也是(因为您可能需要直接收集有关车辆的信息,例如通过其 ID)。 vehicles_to_users 是您的答案,因为允许您通过 user_id 查询车辆。一个查询 --> 通常是一张表...

以上是关于Cassandra中的一对多映射的主要内容,如果未能解决你的问题,请参考以下文章

Cassandra 中的列表大小

我应该或不应该如何一起使用 Cassandra 和 Redis 来构建可扩展的一对一聊天应用程序?

Apache Cassandra随笔之多节点跨数据中心集群配置以及日常操作

cassandra.yaml中的群集配置问题,用于多节点群集,其中只有1个公共IP

cassandra中的分区计数

Cassandra 4.0 使用 java 驱动程序进行多选