Cassandra 缓慢读取(按分区键)获取大数据行

问题描述 投票:0回答:1

我在 AWS 上有一个 5 节点 cassandra (3.11) 集群。 机器规格

Model   vCPU    Memory (GiB)    Instance Storage (GB)   Networking Bandwidth (Gbps)
i3.large    2   15.25   1 x 475 NVMe SSD    Up to 10

对于我的密钥空间,复制因子 = 2。

我需要将车辆的移动记录存储在表中。

这些列非常简单 -vehicleId、lat、lng、timestamp

我们拥有 10 万辆车辆,并且拥有每辆车辆 10 年的历史数据。

总数据量约为20亿行。

我需要读取 ASC 中给定车辆的所有数据,以便对其进行一些处理。

之前我们的表结构是

CREATE TABLE vehicle_movement (
vehicle_id int,
timestamp timestamp,
lat double,
lng double,
PRIMARY KEY (vehicle_id, timestamp)
) WITH CLUSTERING ORDER BY (timestamp ASC)

Where vehicle_id was PARTITION KEY. 
AND the query was

SELECT lat, lng, timestamp from vehicle_movement where vehicle_id = xyz

读取查询的性能相当慢(~5秒)。我们发现,由于每个vehicle_id单独作为分区键是不够的,因为我们每辆车有300K+记录,这可能会导致很大的分区。

所以我们稍微改变了我们的模式

以下是我更新的表格

    CREATE TABLE vehicle_movement (
    vehicle_id int,
    year int,
    timestamp timestamp,
    lat double,
    lng double,
    PRIMARY KEY ((vehicle_id, year), timestamp)
    ) WITH CLUSTERING ORDER BY (timestamp ASC)

    Here vehicle_id and year are composite partition keys 
which ensures that each partition may not contain more than 10k-15k records.

但是由于我的问题陈述仍然相同 - 按 ASC 顺序获取给定车辆的所有记录, 我进一步将查询拆分为多个

Eg
SELECT lat, lng, timestamp from vehicle_movement where vehicle_id = xyz and year IN (2018, 2019)


SELECT lat, lng, timestamp from vehicle_movement where vehicle_id = xyz and year IN (2020, 2021)

SELECT lat, lng, timestamp from vehicle_movement where vehicle_id = xyz and year IN (2022, 2023)


与之前相比,这种方法减少了延迟,但仍然不能令人满意(与之前的 5 秒相比,延迟为 2 到 2.5 秒)。

我的客户端是基于spring的java应用程序,其中我使用spring-data-cassandra。

我执行这些多个查询并累积所有结果。

对于读取,我使用了consistency = LOCAL_ONE(读取速度最快)

我尝试了查询的串行和并行执行,但总体延迟仍然或多或少相同。

所以我的疑问是:

  1. 如何进一步改善延迟 - 我必须按时间戳 ASC 读取给定车辆订单的所有数据才能进行处理?
  2. 考虑到我的读取吞吐量很高,cassandra 是正确的选择吗?
  3. 我什至尝试了 SCYLLA DB,因为它是 cassandra 的直接替代品,具有更好的基准。 SCYLLA 使我的延迟提高了约 30%

编辑2

我对我的架构进行了一些反规范化,它显着改善了我的读取延迟。

CREATE TYPE movement_point (
     lat DOUBLE,
     lng DOUBLE,
     ts TIMESTAMP
 );
 
  CREATE TABLE vehicle_movement_denorm (
      vehicle_id INT,
      year INT,
      points LIST<FROZEN<movement_point>>, // stored in ASC order
      count INT, // As there is no built in function for count
      PRIMARY KEY (vehicle_id, year)
  ) WITH CLUSTERING ORDER BY (year ASC);

 

幸运的是,对于每辆车和年份,我们有 7000-8000 条记录,这些记录很好地融入冻结列表,同时维持秩序。

读取数据时,一次调用就查询了3-4年的数据。 所以 2-3 个调用来获取我的数据。

SELECT points from vehicle_movement_denorm where vehicle_id = xyz and year in (2020, 2021, 2022) 

SELECT points from vehicle_movement_denorm where vehicle_id = xyz and year in (2023, 2024)

这种方法给我带来了 200-400 毫秒的读取延迟,这非常好。 这种非规范化方法是否良好且可扩展?

amazon-ec2 cassandra spring-data-cassandra scylla
1个回答
0
投票

首先,是的 - 如果你有一个相当短的列表(7000 条记录并不短,但它在合理的范围内。如果它可以变得更大甚至无限,你就会遇到大问题)并且你永远不需要修改 - 只需读取旧的历史值,那么冻结列表确实是一个不错的选择。

尽管我很惊讶使用冻结列表而不是分区可以大大减少延迟。从分区读取连续的、排序的数据不应该比读取冻结列表慢多少。也许由于某种原因,原始读取进行了更多的分页,并且您的页面较小和/或您的网络延迟较高(?),这导致分页速度较慢?

原则上,获得低延迟的最佳方法是使问题更加并行化,而不是更少。在最新的解决方案中,您从单个分区读取所有数据,因此从单个节点(或最多 3 个节点)读取所有数据。在 ScyllaDB 中,情况更糟 - 集群的 10 个 CPU 中只有 1 或 3 个 CPU。我认为如果使用 (vehicle_id,year) 一个复合分区键(年份将是分区键的一部分,而不是集群键),那么您将获得更低的延迟,并且您将并行执行不同年份的所有 SELECT。其中每一个都将被发送到不同的 CPU,利用更多集群的处理能力,并更快地完成(更低的延迟)。

© www.soinside.com 2019 - 2024. All rights reserved.