【问题标题】:Cassandra Read/Get PerformanceCassandra 读取/获取性能
【发布时间】:2023-03-27 13:59:02
【问题描述】:

我的 Cassandra 表具有以下架构

CREATE TABLE cachetable1 (
id text,
lsn text,
lst timestamp,
PRIMARY KEY ((id))
) WITH
bloom_filter_fp_chance=0.010000 AND
caching='{"keys":"ALL", "rows_per_partition":"ALL"}' AND
comment='' AND
dclocal_read_repair_chance=0.100000 AND
gc_grace_seconds=864000 AND
read_repair_chance=0.000000 AND
default_time_to_live=0 AND
speculative_retry='99.0PERCENTILE' AND
memtable_flush_period_in_ms=0 AND
compaction={'class': 'SizeTieredCompactionStrategy'} AND
compression={'sstable_compression': 'LZ4Compressor'};

上表包含 2.21 亿行(大约 16 GB 数据)。 CassandraDaemon 使用 4GB 堆空间运行,我为行缓存配置了 4GB 内存。我正在尝试像这样从我的 java 代码中运行选择查询

for(int i = 0; i < 1000; i ++)
    {
        int id = random.nextInt(20000000 - 0) + 0;
        for(j = id; j <= id + 100; j++)
        {
            ls.add(j+"");
        }

           Statement s = QueryBuilder.select("lst","lsn").from("ks1" , "cachetable1").where(QueryBuilder.in("id",ls.toArray()));
           s.setFetchSize(100);

               ResultSet rs=sess.execute( s );
              List<Row> lsr=rs.all();
               for(Row rw:lsr)
               {
                   //System.out.println(rw.toString());
                   count++;
               }

        ls.clear();
    }

在上面的代码中,我试图获取 10 万条记录。但是读取/获取性能非常糟糕。获取 10 万行需要 400-500 秒。有没有更好的方法通过 Java 从 Cassandra 读取/获取记录?除了行缓存大小和 Cassandra 堆大小之外,是否需要进行一些调整?

【问题讨论】:

    标签: java cassandra


    【解决方案1】:

    您似乎希望检索 100 行块中的数据。这听起来像是一个很好的聚类列候选者。

    更改您的架构以使用 id 作为分区键和块索引作为集群列,即 PRIMARY KEY ( (id), chunk_idx )。插入数据时,您必须弄清楚如何将单个索引映射到 id 和 chunk_idx(例如,可能对其中一个值进行模 100 以生成 chunk_idx)。

    现在,当您查询 id 并且不指定 chunk_idx 时,Cassandra 可以有效地返回所有 100 行,并在分区上读取一个磁盘。如果您不总是想读取整块行,您仍然可以通过指定 chunk_idx 对分区内的单行进行范围查询和检索。

    所以你的错误是每次查询都会生成 100 次随机分区读取,这将影响所有节点并且需要为每个节点单独读取磁盘。请记住,仅仅因为您要查询顺序索引号并不意味着数据存储在一起很近,而在 Cassandra 中恰恰相反,顺序分区键可能存储在不同的节点上。

    您犯的第二个错误是您正在同步执行查询(即您正在发出查询并等待请求完成,然后再发出更多查询)。您想要做的是使用线程池,以便您可以并行运行许多查询,或者在单个线程中使用 executeAsync 方法。由于您的查询效率不高,等待 100 次随机分区读取完成将是一个漫长的等待,并且许多高度流水线化的 Cassandra 容量将坐在那里摆弄拇指等待做某事。如果您想最大限度地提高性能,您希望所有节点都尽可能忙碌。

    另一件事是在连接到集群时使用 TokenAwarePolicy。这允许每个查询直接到达具有分区副本的节点,而不是到达可能必须充当协调器并通过额外跃点获取数据的随机节点。当然,在读取上使用一致性级别 ONE 比更高的一致性级别更快。

    行缓存大小和堆大小不是问题的根源,因此这是错误的路径。

    【讨论】:

      【解决方案2】:

      我猜这是你的罪魁祸首:

      .where(QueryBuilder.in("id",ls.toArray()))
      

      众所周知,在WHERE 子句中使用IN 关系是无效的。在某些情况下,执行多个并行查询可能比使用一个IN 查询更快。来自DataStax SELECT documentation

      何时不使用 IN

      ...使用 IN 会降低性能,因为通常许多节点必须 询问。例如,在具有 30 个 节点,复制因子为 3,一致性级别为 LOCAL_QUORUM,单键查询到两个节点,但如果 查询使用IN条件,被查询的节点数为 很可能甚至更高,最多 20 个节点,具体取决于密钥的位置 属于令牌范围。

      所以你有两个选择(假设使用这个性能不佳的查询不是其中之一):

      1. 重写您的代码,为每个id 发出多个并行请求。

      2. 重新访问您的数据模型,看看您是否有另一个值可以用来作为数据键的意义。例如,如果ls 中的所有ids 碰巧共享一个对他们来说唯一的公共列值,那么这是主键的一个很好的候选者。基本上,找到另一种方法来查询您正在寻找的所有ids,并构建一个特定的查询表来支持它。

      【讨论】:

      • 我已经尝试过你的建议。我将代码更改为 - for(int c =0; c
      • 那么下一个尝试的方法(这是你可能会看到最大的收益的地方)是我的第二个建议,看看是否有一种方法来为查询表建模对于那些 ID。我想您也可以尝试 BATCH 您的 id 查询并将它们一起发送到 Cassandra,但 BATCH 也不以性能着称。
      猜你喜欢
      • 2018-03-20
      • 2020-01-26
      • 2016-08-16
      • 2011-08-10
      • 2015-09-03
      • 2012-05-31
      • 2016-01-05
      • 2021-11-24
      • 2015-05-21
      相关资源
      最近更新 更多