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 和 Redis 来构建可扩展的一对一聊天应用程序?
Apache Cassandra随笔之多节点跨数据中心集群配置以及日常操作