Cassandra 低读取性能和高 SSTable 计数
Cassandra low read performance with high SSTable count
我正在构建一个处理非常大的数据(超过 300 万)的应用程序。我是 cassandra 的新手,我正在使用 5 节点 cassandra 集群来存储数据。我有两个列族
Table 1 : CREATE TABLE keyspace.table1 (
partkey1 text,
partkey2 text,
clusterKey text,
attributes text,
PRIMARY KEY ((partkey1, partkey2), clusterKey1)
) WITH bloom_filter_fp_chance = 0.01
AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
AND comment = ''
AND compaction = {'min_threshold': '4', 'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32'}
AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
AND dclocal_read_repair_chance = 0.1
AND default_time_to_live = 0
AND gc_grace_seconds = 864000
AND max_index_interval = 2048
AND memtable_flush_period_in_ms = 0
AND min_index_interval = 128
AND read_repair_chance = 0.0
AND speculative_retry = '99.0PERCENTILE';
Table 2 : CREATE TABLE keyspace.table2 (
partkey1 text,
partkey2 text,
clusterKey2 text,
attributes text,
PRIMARY KEY ((partkey1, partkey2), clusterKey2)
) WITH bloom_filter_fp_chance = 0.01
AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
AND comment = ''
AND compaction = {'min_threshold': '4', 'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32'}
AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
AND dclocal_read_repair_chance = 0.1
AND default_time_to_live = 0
AND gc_grace_seconds = 864000
AND max_index_interval = 2048
AND memtable_flush_period_in_ms = 0
AND min_index_interval = 128
AND read_repair_chance = 0.0
AND speculative_retry = '99.0PERCENTILE';
注意:clusterKey1 和 clusterKey2 是随机生成的 UUID
我关心的是 nodetool cfstats
我在 Table1 上获得了良好的吞吐量,统计数据:
- SSTable 计数:2
- Space 使用(总计):365189326
- Space 快照使用(总数):435017220
- SSTable压缩比:0.2578485727722293
- 内存table 细胞数:18590
- 内存table数据大小:3552535
- 内存table 开关数:171
- 本地读取计数:0
- 本地读取延迟:NaN 毫秒
- 本地写入计数:2683167
- 本地写入延迟:1.969 毫秒
- 等待刷新:0
- 布隆过滤器误报:0
- 布隆过滤器错误率:0.00000
- 布隆过滤器 space 使用:352
至于 table2,我的统计数据读取性能非常差:
- SSTable 数:33
- Space 使用(实时):212702420
- Space 使用(总计):212702420
- Space 快照使用(总计):262252347
- SSTable压缩比:0.1686948750752438
- 内存table 细胞计数:40240
- 内存table数据大小:24047027
- 内存table开关数:89
- 本地读取计数:24027
- 本地读取延迟:0.580 毫秒
- 本地写入计数:1075147
- 本地写入延迟:0.046 毫秒
- 等待刷新:0
- 布隆过滤器误报:0
- 布隆过滤器错误率:0.00000
- 布隆过滤器 space 使用:688
我想知道为什么 table2 创建了 33 个 SSTable,为什么其中的读取性能非常低。谁能帮我弄清楚我做错了什么?
这就是我查询 table 的方式:
BoundStatement selectStamt;
if (selectStamt == null) {
PreparedStatement prprdStmnt = session
.prepare("select * from table2 where clusterKey1 = ? and partkey1=? and partkey2=?");
selectStamt = new BoundStatement(prprdStmnt);
}
synchronized (selectStamt) {
res = session.execute(selectStamt.bind("clusterKey", "partkey1", "partkey2"));
}
在另一个线程中,我正在对这个 table 以相同的方式对不同的数据进行一些更新操作。
在测量吞吐量的情况下,我测量每秒处理的记录数,其处理量仅为 50-80 条记录。
当你有很多 SSTables 时,数据在这些 SSTables 之间的分布非常重要。由于您使用的是 SizeTieredCompactionStrategy,当有 4 个相同大小的 SSTables 时,SSTables 会被压缩和合并。
如果您经常在不同时间更新同一分区内的数据,您的数据很可能分布在许多 SSTable 中,这会降低性能,因为将多次读取您的 SSTable。
在我看来,确认这一点的最好方法是执行 cfhistograms on your table:
nodetool -h localhost cfhistograms keyspace table2
根据您安装的 cassandra 版本,输出会有所不同,但它会包含针对给定读取操作读取的 SSTable 数量的直方图。
如果您经常在不同时间更新同一分区内的数据,您可以考虑使用 LeveledCompactionStrategy (When to use Leveled Compaction)。 LCS 会将来自同一分区的数据保存在同一个 SSTable 中,这大大提高了读取性能,但代价是更多磁盘 I/O 进行压缩。根据我的经验,如果您的读写比率很高,额外的压缩磁盘 I/O 会在读取性能方面获得更多回报。
编辑:关于您的吞吐量问题,有很多因素限制了您的吞吐量。
- 一个可能的大问题是,除非您有多个线程同时进行相同的查询,否则您将连续发出请求(一次一个)。这样做会严重限制您的吞吐量,因为在您收到 Cassandra 的响应之前无法发送另一个请求。此外,由于您在 selectStmt 上进行同步,因此即使此代码由多个线程执行,无论如何一次也只能执行一个请求。您可以通过让多个工作线程为您发出请求(如果您尚未这样做)或更好的用户 executeAsync 来异步执行许多请求,从而显着提高吞吐量。有关请求流程在驱动程序中的工作方式以及如何有效地使用驱动程序进行许多查询的说明,请参阅 Asynchronous queries with the Java driver。
- 如果您每次进行查询时都执行相同的代码,则每次调用 'session.prepare' 创建 PreparedStatement 都会创建一个额外的往返。 session.prepare 向 cassandra 发送请求以准备您的声明。您只需要这样做一次,每次进行查询时都可以重用 PreparedStatement。您可能已经在执行此操作,因为您的语句已进行空值检查(没有更多代码无法判断)。
- 与其重复使用 selectStmt 并在其上同步,只需在每次进行查询时使用的单个 PreparedStatement 创建一个新的 BoundStatement。这样就完全不需要同步了。
除了switching compaction strategies(这很昂贵,你会在改变后努力压缩一段时间)正如Andy所建议的那样肯定会帮助你的读取性能,你也可以调整你当前的压缩策略来尝试摆脱一些碎片:
- 如果您有待处理的压缩 (nodetool compactionstats) -- 然后尝试通过增加压缩节流来赶上进度。将并发压缩器保持在 CPU 核心的 1/2,以避免压缩占用所有核心。
- 增加存储桶大小(增加 bucket_high,降低存储桶大小)- 规定 sstables 的大小必须相似到什么程度才能压缩在一起。
- Drop Compaction threshold - 指示在压缩发生之前,桶中必须容纳多少 sstables。
有关 2 和 3 的详细信息,请查看 compaction subproperties
注意:不要使用 nodetool compact。这会将整个 table 放在一个巨大的 sstable 中,您将失去一次压缩切片的好处。
- 在紧急情况下使用 JMX --> 强制用户定义压缩以强制进行次要压缩
您有很多 SSTable 并且读取速度很慢。您应该做的第一件事是找出每个 SELECT.
读取了多少 SSTable
最简单的方法是检查相应的 MBean:在 MBean 域 "org.apache.cassandra.metrics" 中找到您的键空间,在它下面是您的 table,然后是 SSTablesPerReadHistorgram MBean。 Cassandra 记录最小值、最大值、平均值以及百分位数。
SSTablesPerReadHistorgram 中第 99 个百分位数的一个非常好的值是 1,这意味着您通常只读取单个 table。如果数量与 SSTable 的数量一样高,则 Cassandra 正在检查所有 SSTable。在后一种情况下,无论是否对整个主键执行 select,您都应该 double-check 您的 SELECT。
我正在构建一个处理非常大的数据(超过 300 万)的应用程序。我是 cassandra 的新手,我正在使用 5 节点 cassandra 集群来存储数据。我有两个列族
Table 1 : CREATE TABLE keyspace.table1 (
partkey1 text,
partkey2 text,
clusterKey text,
attributes text,
PRIMARY KEY ((partkey1, partkey2), clusterKey1)
) WITH bloom_filter_fp_chance = 0.01
AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
AND comment = ''
AND compaction = {'min_threshold': '4', 'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32'}
AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
AND dclocal_read_repair_chance = 0.1
AND default_time_to_live = 0
AND gc_grace_seconds = 864000
AND max_index_interval = 2048
AND memtable_flush_period_in_ms = 0
AND min_index_interval = 128
AND read_repair_chance = 0.0
AND speculative_retry = '99.0PERCENTILE';
Table 2 : CREATE TABLE keyspace.table2 (
partkey1 text,
partkey2 text,
clusterKey2 text,
attributes text,
PRIMARY KEY ((partkey1, partkey2), clusterKey2)
) WITH bloom_filter_fp_chance = 0.01
AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
AND comment = ''
AND compaction = {'min_threshold': '4', 'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32'}
AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
AND dclocal_read_repair_chance = 0.1
AND default_time_to_live = 0
AND gc_grace_seconds = 864000
AND max_index_interval = 2048
AND memtable_flush_period_in_ms = 0
AND min_index_interval = 128
AND read_repair_chance = 0.0
AND speculative_retry = '99.0PERCENTILE';
注意:clusterKey1 和 clusterKey2 是随机生成的 UUID
我关心的是 nodetool cfstats 我在 Table1 上获得了良好的吞吐量,统计数据:
- SSTable 计数:2
- Space 使用(总计):365189326
- Space 快照使用(总数):435017220
- SSTable压缩比:0.2578485727722293
- 内存table 细胞数:18590
- 内存table数据大小:3552535
- 内存table 开关数:171
- 本地读取计数:0
- 本地读取延迟:NaN 毫秒
- 本地写入计数:2683167
- 本地写入延迟:1.969 毫秒
- 等待刷新:0
- 布隆过滤器误报:0
- 布隆过滤器错误率:0.00000
- 布隆过滤器 space 使用:352
至于 table2,我的统计数据读取性能非常差:
- SSTable 数:33
- Space 使用(实时):212702420
- Space 使用(总计):212702420
- Space 快照使用(总计):262252347
- SSTable压缩比:0.1686948750752438
- 内存table 细胞计数:40240
- 内存table数据大小:24047027
- 内存table开关数:89
- 本地读取计数:24027
- 本地读取延迟:0.580 毫秒
- 本地写入计数:1075147
- 本地写入延迟:0.046 毫秒
- 等待刷新:0
- 布隆过滤器误报:0
- 布隆过滤器错误率:0.00000
- 布隆过滤器 space 使用:688
我想知道为什么 table2 创建了 33 个 SSTable,为什么其中的读取性能非常低。谁能帮我弄清楚我做错了什么?
这就是我查询 table 的方式:
BoundStatement selectStamt;
if (selectStamt == null) {
PreparedStatement prprdStmnt = session
.prepare("select * from table2 where clusterKey1 = ? and partkey1=? and partkey2=?");
selectStamt = new BoundStatement(prprdStmnt);
}
synchronized (selectStamt) {
res = session.execute(selectStamt.bind("clusterKey", "partkey1", "partkey2"));
}
在另一个线程中,我正在对这个 table 以相同的方式对不同的数据进行一些更新操作。
在测量吞吐量的情况下,我测量每秒处理的记录数,其处理量仅为 50-80 条记录。
当你有很多 SSTables 时,数据在这些 SSTables 之间的分布非常重要。由于您使用的是 SizeTieredCompactionStrategy,当有 4 个相同大小的 SSTables 时,SSTables 会被压缩和合并。
如果您经常在不同时间更新同一分区内的数据,您的数据很可能分布在许多 SSTable 中,这会降低性能,因为将多次读取您的 SSTable。
在我看来,确认这一点的最好方法是执行 cfhistograms on your table:
nodetool -h localhost cfhistograms keyspace table2
根据您安装的 cassandra 版本,输出会有所不同,但它会包含针对给定读取操作读取的 SSTable 数量的直方图。
如果您经常在不同时间更新同一分区内的数据,您可以考虑使用 LeveledCompactionStrategy (When to use Leveled Compaction)。 LCS 会将来自同一分区的数据保存在同一个 SSTable 中,这大大提高了读取性能,但代价是更多磁盘 I/O 进行压缩。根据我的经验,如果您的读写比率很高,额外的压缩磁盘 I/O 会在读取性能方面获得更多回报。
编辑:关于您的吞吐量问题,有很多因素限制了您的吞吐量。
- 一个可能的大问题是,除非您有多个线程同时进行相同的查询,否则您将连续发出请求(一次一个)。这样做会严重限制您的吞吐量,因为在您收到 Cassandra 的响应之前无法发送另一个请求。此外,由于您在 selectStmt 上进行同步,因此即使此代码由多个线程执行,无论如何一次也只能执行一个请求。您可以通过让多个工作线程为您发出请求(如果您尚未这样做)或更好的用户 executeAsync 来异步执行许多请求,从而显着提高吞吐量。有关请求流程在驱动程序中的工作方式以及如何有效地使用驱动程序进行许多查询的说明,请参阅 Asynchronous queries with the Java driver。
- 如果您每次进行查询时都执行相同的代码,则每次调用 'session.prepare' 创建 PreparedStatement 都会创建一个额外的往返。 session.prepare 向 cassandra 发送请求以准备您的声明。您只需要这样做一次,每次进行查询时都可以重用 PreparedStatement。您可能已经在执行此操作,因为您的语句已进行空值检查(没有更多代码无法判断)。
- 与其重复使用 selectStmt 并在其上同步,只需在每次进行查询时使用的单个 PreparedStatement 创建一个新的 BoundStatement。这样就完全不需要同步了。
除了switching compaction strategies(这很昂贵,你会在改变后努力压缩一段时间)正如Andy所建议的那样肯定会帮助你的读取性能,你也可以调整你当前的压缩策略来尝试摆脱一些碎片:
- 如果您有待处理的压缩 (nodetool compactionstats) -- 然后尝试通过增加压缩节流来赶上进度。将并发压缩器保持在 CPU 核心的 1/2,以避免压缩占用所有核心。
- 增加存储桶大小(增加 bucket_high,降低存储桶大小)- 规定 sstables 的大小必须相似到什么程度才能压缩在一起。
- Drop Compaction threshold - 指示在压缩发生之前,桶中必须容纳多少 sstables。
有关 2 和 3 的详细信息,请查看 compaction subproperties
注意:不要使用 nodetool compact。这会将整个 table 放在一个巨大的 sstable 中,您将失去一次压缩切片的好处。
- 在紧急情况下使用 JMX --> 强制用户定义压缩以强制进行次要压缩
您有很多 SSTable 并且读取速度很慢。您应该做的第一件事是找出每个 SELECT.
读取了多少 SSTable最简单的方法是检查相应的 MBean:在 MBean 域 "org.apache.cassandra.metrics" 中找到您的键空间,在它下面是您的 table,然后是 SSTablesPerReadHistorgram MBean。 Cassandra 记录最小值、最大值、平均值以及百分位数。
SSTablesPerReadHistorgram 中第 99 个百分位数的一个非常好的值是 1,这意味着您通常只读取单个 table。如果数量与 SSTable 的数量一样高,则 Cassandra 正在检查所有 SSTable。在后一种情况下,无论是否对整个主键执行 select,您都应该 double-check 您的 SELECT。