卡桑德拉 Read/Get 性能

Cassandra Read/Get Performance

我的 Cassandra table 具有以下架构

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'};

以上 table 包含 2.21 亿行(约 16 GB 数据)。 CassandraDaemon 是 运行 4GB 堆 space,我为行缓存配置了 4GB 内存。我正在尝试从我的 java 代码中 运行 select 查询

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 万条记录。但是 read/get 性能很差。获取 10 万行需要 400-500 秒。有没有更好的方法可以通过 Java 从 Cassandra read/get 记录?除了行缓存大小和 Cassandra 堆大小之外,是否需要进行一些调整?

我猜这是你的罪魁祸首:

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

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

When not to use IN

...Using IN can degrade performance because usually many nodes must be queried. For example, in a single, local data center cluster with 30 nodes, a replication factor of 3, and a consistency level of LOCAL_QUORUM, a single key query goes out to two nodes, but if the query uses the IN condition, the number of nodes being queried are most likely even higher, up to 20 nodes depending on where the keys fall in the token range.

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

  1. 重写您的代码,为每个 id.

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

您似乎想要以 100 行块的形式检索数据。这听起来很适合聚类列。

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

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

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

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

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

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