逻辑数据建模
现在您已经定义了查询,就可以开始设计 Cassandra 表了。首先,创建一个包含每个查询的表的逻辑模型,从概念模型中捕获实体和关系。
要命名每个表,您将识别要查询的主要实体类型,并使用该类型作为实体名称的开头。如果您要通过其他相关实体的属性进行查询,请将这些属性附加到表名,并用 by
分隔。例如,hotels_by_poi
。
接下来,您将识别表的 主键,根据所需的查询属性添加分区键列,并按顺序添加聚类列以保证唯一性并支持所需的排序顺序。
主键的设计非常重要,因为它将决定每个分区中存储的数据量以及这些数据在磁盘上的组织方式,进而影响 Cassandra 处理读取的速度。
通过添加查询识别的任何其他属性来完成每个表。如果这些其他属性中的任何一个对于分区键的每个实例都是相同的,请将该列标记为静态。
现在,我们对一个相当复杂的过程进行了快速描述,因此,逐步完成一个详细的示例将非常有益。首先,让我们介绍一种用于表示逻辑模型的符号。
Cassandra 社区中的几个人提出了以图表形式捕获数据模型的符号。本文档使用 Artem Chebotko 推广的一种符号,它提供了一种简单、信息丰富的可视化方法,用于可视化设计中查询和表之间的关系。此图显示了 Chebotko 符号的逻辑数据模型。
每个表都显示其标题和列列表。主键列通过符号标识,例如分区键列的 K 以及表示聚类列的 C↑ 或 C↓。进入表或表之间的线表示每个表设计为支持的查询。
酒店逻辑数据模型
下图显示了与酒店、兴趣点、房间和便利设施相关的查询的 Chebotko 逻辑数据模型。您会立即注意到,Cassandra 设计不包含专门用于房间或便利设施的表,就像您在关系设计中所做的那样。这是因为工作流程没有识别任何需要这种直接访问的查询。
让我们探索每个表的详细信息。
第一个查询 Q1 是查找靠近兴趣点的酒店,因此您将此表称为 hotels_by_poi
。按命名兴趣点进行搜索是一个线索,表明兴趣点应该是主键的一部分。让我们通过名称引用兴趣点,因为根据工作流程,用户将以此方式开始搜索。
您会注意到,在给定兴趣点附近可能有多家酒店,因此您需要主键中的另一个组件,以确保每个酒店都有一个唯一的分区。因此,您将酒店键添加为聚类列。
设计表的主键时,一个重要的考虑因素是确保它定义了一个唯一的数据元素。否则,您可能会意外地覆盖数据。
现在,对于第二个查询 (Q2),您需要一个表来获取有关特定酒店的信息。一种方法是将酒店的所有属性都放在 hotels_by_poi
表中,但您只添加了应用程序工作流程所需的属性。
从工作流程图中,您知道 hotels_by_poi
表用于显示酒店列表,其中包含每个酒店的基本信息,并且应用程序知道返回的酒店的唯一标识符。当用户选择酒店以查看详细信息时,您可以使用 Q2,它用于获取有关酒店的详细信息。因为您已经从 Q1 中获得了 hotel_id
,所以您可以将其用作要查找的酒店的参考。因此,第二个表只称为 hotels
。
另一种选择是在酒店表中存储一组 poi_names
。这是一种同样有效的方法。您将通过经验了解哪种方法最适合您的应用程序。
Q3 只是 Q1 的反向——查找靠近酒店的兴趣点,而不是靠近兴趣点的酒店。但是,这次您需要访问每个兴趣点的详细信息,如 pois_by_hotel
表所示。与之前一样,您将兴趣点名称添加为聚类键以保证唯一性。
现在,让我们考虑如何支持查询 Q4,以帮助用户在他们感兴趣的住宿日期找到所选酒店的可用房间。请注意,此查询涉及开始日期和结束日期。因为您是在一个范围内查询,而不是在单个日期上查询,所以您知道您需要将日期用作聚类键。使用 hotel_id
作为主键,将每个酒店的房间数据分组到一个分区中,这应该有助于搜索非常快。让我们将其称为 available_rooms_by_hotel_date
表。
为了支持在范围内搜索,使用 clustering columns <clustering-columns>
来存储您需要在范围查询中访问的属性。请记住,聚类列的顺序很重要。
available_rooms_by_hotel_date
表的设计是 宽分区 模式的示例。在讨论支持类似模型的数据库时,此模式有时被称为 宽行 模式,但从 Cassandra 的角度来看,宽分区是一个更准确的描述。该模式的本质是将多个相关行分组到一个分区中,以便在单个查询中快速访问分区中的多行。
为了完善数据模型的购物部分,添加 amenities_by_room
表以支持 Q5。这将允许用户查看在所需住宿日期可用的房间的便利设施。
预订逻辑数据模型
现在让我们转换思路,看看预订查询。该图显示了预订的逻辑数据模型。您会注意到,这些表表示非规范化设计;相同的数据出现在多个表中,但键不同。
为了满足 Q6,reservations_by_guest
表可用于按客人姓名查找预订。您可以想象查询 Q7 代表客人自己在自助网站上使用,或者呼叫中心代理尝试帮助客人使用。因为客人姓名可能不唯一,所以您在此处将客人 ID 也作为聚类列。
Q8 和 Q9 特别提醒您创建支持应用程序各种利益相关者的查询,不仅是客户,还有员工,甚至可能是分析团队、供应商等等。
酒店员工可能希望按日期查看即将到来的预订记录,以便了解酒店的运营情况,例如酒店在哪些日期客满或客源不足。Q8 支持按日期检索给定酒店的预订。
最后,您创建了一个guests
表。这提供了一个用于存储客人信息的单一位置。在这种情况下,您为客人记录指定了一个单独的唯一标识符,因为客人有相同姓名的情况并不少见。在许多组织中,像guests
表这样的客户数据库将是独立的客户管理应用程序的一部分,这就是为什么其他客人访问模式被从示例中省略的原因。
模式和反模式
与其他类型的软件设计一样,Cassandra 的数据建模也有一些众所周知的模式和反模式。您已经在该酒店模型中使用了一种最常见的模式——宽分区模式。
时间序列模式是宽分区模式的扩展。在这种模式下,一系列在特定时间间隔内的测量值存储在宽分区中,其中测量时间用作分区键的一部分。这种模式经常用于商业分析、传感器数据管理和科学实验等领域。
时间序列模式也适用于除测量值以外的其他数据。考虑一个银行应用程序的例子。您可以将每个客户的余额存储在一行中,但这可能会导致大量读写冲突,因为各种客户会查看他们的余额或进行交易。您可能很想在写入周围包装一个事务,只是为了保护余额不被错误更新。相反,时间序列风格的设计会将每个交易存储为一个带时间戳的行,并将计算当前余额的工作留给应用程序。
许多新用户都会陷入的一个设计陷阱是试图将 Cassandra 用作队列。队列中的每个项目都与一个时间戳一起存储在宽分区中。项目被追加到队列的末尾,并从前面读取,在读取后被删除。这是一种看起来很有吸引力的设计,尤其是考虑到它与时间序列模式的明显相似性。这种方法的问题在于,已删除的项目现在是墓碑 <asynch-deletes>
,Cassandra 必须扫描这些墓碑才能从队列的前面读取。随着时间的推移,越来越多的墓碑开始降低读取性能。
队列反模式提醒我们,任何依赖于数据删除的设计都可能是性能不佳的设计。
资料摘自 Cassandra,权威指南。由 O’Reilly Media, Inc. 出版。版权所有 © 2020 Jeff Carpenter, Eben Hewitt。保留所有权利。经许可使用。