第十二章:Cassandra性能调优--Cassandra:The Definitive Guide 2nd Edition

在本章中,我们将介绍如何调整Cassandra以提高性能。配置文件和各个表中有各种设置。虽然默认设置适用于许多用例,但在某些情况下您可能需要更改它们。在本章中,我们将介绍如何以及为何进行这些更改。

我们还了解了如何使用Cassandra附带的cassandra-stress测试工具来生成对Cassandra的负载,并快速了解它在压力测试环境下的行为。然后我们可以适当调整Cassandra,并确信我们已准备好部署到生产环境。

调节性能

为了有效地实现和维护集群中的高性能,将性能管理视为一个从应用程序的体系结构开始并继续进行开发和操作的过程是有帮助的。

设定性能目标

在开始任何性能调优工作之前,无论您是刚开始在Cassandra上部署应用程序还是维护现有应用程序,都必须牢记明确的目标。

规划集群时,了解集群的使用方式非常重要:客户端数量,预期使用模式,预计高峰时段等。这将有助于规划初始集群容量和规划集群增长,我们将在第14章进一步讨论。

此规划工作的一个重要部分是根据吞吐量(每单位时间服务的查询数)和延迟(完成给定查询的时间)确定明确的性能目标。

例如,假设我们正在构建一个使用我们在第5章中设计的酒店数据模型的电子商务网站。我们可能会为集群上的购物操作设置以下性能目标:

群集必须从available_ rooms_by_hotel_date表支持每秒30,000次读取操作,第95百分位读取延迟为3 ms。

这是一个包含吞吐量和延迟目标的语句。我们将学习如何使用nodetool tablestats进行测量。

无论您的具体表现目标如何,重要的是要记住,性能调优都是权衡取舍。明确定义的性能目标将帮助您阐明应用程序可接受的权衡。例如:

  • 启用SSTable压缩以节省磁盘空间,但需要额外的CPU处理。
  • 限制网络使用和线程,可用于控制网络和CPU利用率,但代价是吞吐量降低和延迟增加。
  • 增加或减少分配给特定任务(如读取,写入或压缩)的线程数,以便影响相对于其他任务的优先级或支持其他客户端。
  • 增加堆大小以减少查询时间。

这些只是您在性能调优中会考虑的一些权衡因素。我们将在本章的其余部分重点介绍其他内容。

监控性能

随着集群规模的增长,客户端数量的增加,以及更多的密钥空间和表格的添加,集群的需求将开始向不同的方向发展。频繁使用基线来衡量群集的性能与其目标之间的关系将变得越来越重要。

我们在第10章中了解了通过JMX公开的各种指标,包括Cassandra的StorageProxy和各个表的性能相关指标。在该章中,我们还研究了发布与性能相关的统计信息(如nodetool tpstats和nodetool tablestats)的nodetool命令,并讨论了这些命令如何帮助识别加载和延迟问题。

现在我们将看两个额外的nodetool命令,它们提供格式化为直方图的性能统计信息:proxyhistograms和tablehistograms。首先,让我们检查nodetool proxyhistograms命令的输出:

$ nodetool proxyhistograms

proxy histograms
Percentile      Read Latency     Write Latency     Range Latency
                    (micros)          (micros)          (micros)
50%                   654.95              0.00           1629.72
75%                   943.13              0.00           5839.59
95%                  4055.27              0.00          62479.63
98%                 62479.63              0.00         107964.79
99%                107964.79              0.00         129557.75
Min                   263.21              0.00            545.79
Max                107964.79              0.00         155469.30

输出显示请求的节点作为协调器的读取,写入和范围请求的延迟。 输出以百分位数等级以及以微秒为单位的最小值和最大值表示。 在多个节点上运行此命令有助于识别群集中是否存在慢节点。 大范围延迟(在几百毫秒或更长时间内)可以指示使用低效范围查询的客户端,例如那些需要ALLOW FILTERING子句或索引查找的查询。

虽然代理组合图提供的视图对于识别一般性能问题很有用,但我们经常需要关注特定表的性能。 这就是nodetool tablehistograms允许我们做的事情。 让我们看一下这个命令对available_rooms_by_hotel_date表的输出:

nodetool tablehistograms hotel available_rooms_by_hotel_date

hotel/available_rooms_by_hotel_date histograms
Percentile  SSTables  Write Latency  Read Latency  Partition Size  Cell Count
                      (micros)       (micros)      (bytes)                  
50%         0.00      0.00           0.00          2759            179
75%         0.00      0.00           0.00          2759            179
95%         0.00      0.00           0.00          2759            179
98%         0.00      0.00           0.00          2759            179
99%         0.00      0.00           0.00          2759            179
Min         0.00      0.00           0.00          2300            150
Max         0.00      0.00           0.00          2759            179

此命令的输出类似。它省略了范围延迟统计信息,而是提供每个查询读取的SSTables计数。提供了分区大小和单元计数,这提供了另一种识别大分区的方法。

重置指标

请注意,在通过3.X系列的Cassandra版本中,报告的指标是自节点启动以来的生命周期指标。要重置节点上的度量标准,必须重新启动它。 JIRA问题CASSANDRA-8433要求能够通过JMX和nodetool重置指标。

熟悉这些指标以及他们告诉您有关群集的内容后,您应确定要监控的关键指标,甚至实施自动警报,以指示您的绩效目标未得到满足。您可以通过DataStax OpsCenter或任何基于JMX的度量框架来完成此任务。

分析性能问题

一直运行良好的集群的性能随着时间的推移开始降级并不罕见。当您检测到性能问题时,您需要快速开始分析,以确保性能不会继续恶化。在这些情况下,您的目标应该是确定根本原因并解决它。

在本章中,我们将介绍许多配置设置,这些设置可用于在整个集群空间和表中调整集群中每个节点的性能。尝试将性能问题缩小到特定表甚至查询也很重要。

事实上,数据模型的质量通常是Cassandra集群性能中最具影响力的因素。例如,导致具有越来越多行的分区的表设计可能开始降低群集的性能并在失败的修复中显示,或者在添加新节点时出现流故障。相反,具有限制性太强的分区键的分区可能导致行太窄,需要读取许多分区以满足简单查询。

当心大分区

除了前面讨论的nodetool表组合图之外,您还可以通过搜索引用“编写大分区”或“压缩大分区”的WARN消息的日志来检测大分区。压缩大分区时的警告阈值由cassandra.yaml文件中的compaction_large_partition_warning_ threshold_mb属性设置。

您还需要学习识别第5章中讨论的反模式实例,例如队列或生成大量逻辑删除的其他设计方法。

追踪

如果您可以将搜索范围缩小到特定表格并查询问题,则可以使用跟踪来获取详细信息。 跟踪是一种非常宝贵的工具,用于理解每个查询中涉及的客户端和节点之间的通信以及每个步骤所花费的时间。 这有助于我们了解我们在数据模型中做出的设计决策的性能影响以及复制因素和一致性级别的选择。

有几种方法可以访问跟踪数据。 我们首先来看一下cqlsh中跟踪的工作原理。 首先,我们将启用跟踪,然后执行一个简单的命令:

cqlsh:hotel> TRACING ON
Now Tracing is enabled
cqlsh:hotel> SELECT * from hotels where id='AZ123';

 id    | address | name                            | phone          | pois
-------+---------+---------------------------------+----------------+------
 AZ123 |    null | Super Hotel Suites at WestWorld | 1-888-999-9999 | null

(1 rows)

Tracing session: 6669f210-de99-11e5-bdb9-59bbf54c4f73

 activity           | timestamp                  | source    | source_elapsed
--------------------+----------------------------+-----------+----------------
Execute CQL3 query  | 2016-02-28 21:03:33.503000 | 127.0.0.1 |              0
Parsing SELECT *... | 2016-02-28 21:03:33.505000 | 127.0.0.1 |          41491
...            

为简洁起见,我们已经截断了输出,但是如果你运行这样的命令,你会看到诸如准备语句,读取修复,密钥缓存搜索,memtables和SSTables中的数据查找,节点之间的交互以及 与每一步相关的时间,以微秒为单位。

您需要留意需要大量节点间交互的查询,因为这些可能表明您的架构设计存在问题。 例如,基于二级索引的查询可能涉及与群集中的大多数或所有节点的交互。

完成在cqlsh中使用跟踪后,可以使用TRACING OFF命令将其关闭。

使用DataStax驱动程序的客户端也可以使用跟踪信息。 让我们修改第8章中的一个示例,了解如何通过DataStax Java驱动程序与跟踪进行交互。 突出显示的代码可以跟踪查询并打印出跟踪结果:

SimpleStatement hotelSelect = session.newSimpleStatement(
                "SELECT * FROM hotels WHERE id='AZ123'");
hotelSelect.enableTracing();

ResultSet hotelSelectResult = session.execute(hotelSelect);

QueryTrace queryTrace = hotelSelectResult.getExecutionInfo().getQueryTrace();

System.out.printf("Trace id: %s\n\n", queryTrace.getTraceId());
System.out.printf("%-42s | %-12s | %-10s \n", "activity",
                  "timestamp", "source");

System.out.println("-------------------------------------------" 
 + "--------------+------------");                 

SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");

for (QueryTrace.Event event : queryTrace.getEvents()) {
  System.out.printf("%42s | %12s | %10s\n",    
    event.getDescription(),
    dateFormat.format((event.getTimestamp())),
    event.getSource());
}

使用enableTracing()操作在每个Statement或PreparedStatement上单独启用跟踪。 要获取查询的跟踪,我们查看查询结果。 您可能已经注意到,在我们之前的示例中,Session.execute()操作始终返回ResultSet对象,即使对于SELECT查询以外的查询也是如此。 这使我们能够通过ExecutionInfo对象获取有关请求的元数据,即使没有返回结果也是如此。 此元数据包括已实现的一致性级别,协调器节点和查询中涉及的其他节点,以及有关跟踪和分页的信息。

执行此代码会产生与我们在cqlsh中看到的输出非常相似的输出:

Trace id: 58b22960-90cb-11e5-897a-a9fac1d00bce

activity                                   | timestamp    | source    
-------------------------------------------+--------------+------------
   Parsing SELECT * FROM hotels WHERE id=? | 20:44:34.550 | 127.0.0.1
                       Preparing statement | 20:44:34.550 | 127.0.0.1
                      Read-repair DC_LOCAL | 20:44:34.551 | 127.0.0.1
Executing single-partition query on hotels | 20:44:34.551 | 127.0.0.1
              Acquiring sstable references | 20:44:34.551 | 127.0.0.1
                 Merging memtable contents | 20:44:34.551 | 127.0.0.1
               Merging data from sstable 3 | 20:44:34.552 | 127.0.0.1
    Bloom filter allows skipping sstable 3 | 20:44:34.552 | 127.0.0.1
               Merging data from sstable 2 | 20:44:34.552 | 127.0.0.1
    Bloom filter allows skipping sstable 2 | 20:44:34.552 | 127.0.0.1
         Read 1 live and 0 tombstone cells | 20:44:34.552 | 127.0.0.1

DataStax DevCenter也支持跟踪,默认情况下启用跟踪。 您可以通过选择屏幕中下方的“查询跟踪”选项卡立即查看您在DevCenter中发出的任何请求的跟踪,如图12-1所示。 (“连接”,“CQL脚本”,“架构”和“大纲”面板已经最小化以实现可读性。)

在这里插入图片描述

您可以通过nodetool settraceprobability命令配置各个节点以跟踪其部分或全部查询。此命令采用介于0.0(默认值)和1.0之间的数字,其中0.0禁用跟踪,1.0跟踪每个查询。这不会影响客户端请求的单个查询的跟踪。在改变跟踪概率时要小心,因为典型的跟踪会话涉及10次或更多次写入。将跟踪级别设置为1.0可能会使繁忙的群集过载,因此0.01或0.001等值通常是合适的。

痕迹不是永恒的

Cassandra将查询跟踪结果存储在system_traces键空间中。自2.2版本发布以来,Cassandra还使用跟踪来存储修复操作的结果。 Cassandra限制这些表上的TTL,以防止它们随着时间的推移填满磁盘。您可以通过编辑cassandra.yaml文件中的tracetype_query_ttl和tracetype_repair_ttl属性来配置这些TTL值。

调整方法

一旦确定了与您既定目标相关的性能问题的根本原因,就应该开始调整性能了。调整Cassandra性能的建议方法是一次更改一个配置参数并测试结果。

限制调整时所做的配置更改量非常重要,这样您就可以清楚地识别每个更改的影响。您可能需要多次重复更改过程,直到达到所需的性能目标。

在某些情况下,您可以通过添加更多资源(如内存或额外节点)来恢复性能,但请确保您不仅仅是屏蔽底层设计或配置问题。在其他情况下,您可能会发现通过单独调整无法达到预期目标,并且需要进行设计更改。

考虑到这种方法,让我们看一下您可以配置来调整Cassandra集群的各种选项。我们将在cassandra.yaml和cassandra-env.sh文件中突出显示节点配置属性,以及使用CQL在各个表上配置的选项。

高速缓存

缓存用于提高对读取操作的响应性。附加内存用于保存数据,以最大限度地减少必须执行的磁盘读取次数。随着缓存大小的增加,可以从内存中提供的“命中”数量也会增加。

Cassandra内置了三个缓存:密钥缓存,行缓存和计数器缓存。行缓存为每个分区缓存可配置的行数。如果您对给定表使用行缓存,则也不需要在其上使用密钥缓存。

因此,您的缓存策略应根据以下几个因素进行调整:

  • 考虑您的查询,并使用最适合您的查询的缓存类型。
  • 考虑堆大小与高速缓存大小的比率,并且不允许高速缓存压倒堆。
  • 根据键的大小考虑行的大小。 通常,键将比整行小得多。

让我们考虑每个缓存的一些特定调整和配置选项。

密钥缓存

Cassandra的密钥缓存存储分区键到行索引条目的映射,有助于更快地读取存储在磁盘上的SSTable。 我们可以基于每个表配置密钥缓存的使用。 例如,让我们使用cqlsh来检查酒店表上的缓存设置:

cqlsh:hotel> DESCRIBE TABLE hotels;

CREATE TABLE hotel.hotels (
    id text PRIMARY KEY,
    address frozen<address>,
    name text,
    phone text,
    pois set<text>
) WITH bloom_filter_fp_chance = 0.01
    AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'}
  ...

因为密钥缓存大大增加了读取而不消耗大量额外内存,所以默认情况下启用它; 也就是说,如果在创建表时未指定值,则启用密钥缓存。 我们可以使用ALTER TABLE命令禁用此表的密钥缓存:

cqlsh:hotel> ALTER TABLE hotels 
   WITH caching = { 'keys' : 'NONE', 'rows_per_partition' : 'NONE' };

keys属性的合法选项是ALL或NONE。

key_cache_size_in_mb设置指示将用于密钥缓存的最大内存量,该缓存在所有表中共享。默认值是总JVM堆的5%,或100 MB,以较小者为准。

行缓存

行缓存缓存整行,并且可以加快频繁访问的行的读访问,但代价是更多的内存使用。

您需要仔细使用行缓存大小设置,因为这很容易导致比解决方案更多的性能问题。在许多情况下,当所有行都在内存中时,行缓存可以为小数据集产生令人印象深刻的性能结果,只有在必须从磁盘读取数据时才会在较大的数据集上降级。

如果您的表读取的次数远远超过写入次数,那么配置过大的行缓存将不必要地消耗大量的服务器资源。如果您的表具有较低的读写比率,但其中包含大量数据的行(数百列),那么在设置行缓存大小之前,您需要进行一些数学运算。除非你有一些遭受重创的行和其他受到很少打击的行,否则你不会在这里看到太大的提升。

由于这些原因,行缓存往往比密钥缓存产生更少的好处。您可能希望探索操作系统支持的文件缓存功能,以替代行缓存。

与密钥缓存一样,我们可以基于每个表配置行缓存的使用。 rows_per_partition设置指定每个分区将缓存的行数。默认情况下,此值设置为NONE,表示不会缓存任何行。其他可用选项是正整数或ALL。以下CQL语句将行缓存设置为200行:

cqlsh:hotel> ALTER TABLE hotels
   WITH caching = { 'keys' : 'NONE', 'rows_per_partition' : '200' };

行缓存的实现可通过row_cache_class_name属性插入。这默认为org.apache.cassandra.OHCProvider类实现的堆外缓存提供程序。之前的实现是SerializingCacheProvider。

计数器缓存

计数器高速缓存通过减少最常访问的计数器的锁争用来提高计数器性能。配置计数器缓存没有per-table选项。

counter_cache_size_in_mb设置确定将用于计数器缓存的最大内存量,该缓存在所有表中共享。默认值是总JVM堆的2.5%,或50 MB,以较小者为准。

保存的缓存设置

Cassandra提供了定期将缓存保存到磁盘的功能,以便在启动时可以将它们作为快速加热缓存的方式进行读取。所有三种缓存类型中保存的缓存设置类似:

缓存文件保存在saved_caches属性指定的目录下。文件以key_ cache_ save_period,row_cache_save_period和counter_cache_ save_period指定的间隔(秒)写入,默认分别为14000(4小时),0(禁用)和7200(2小时)。
高速缓存由键值索引。要在文件中保存的密钥数由key_cache_keys_to_save,row_cache_keys_to_save和counter_ cache_ keys_to_save属性指示。

通过nodetool管理缓存

Cassandra还提供了通过nodetool管理缓存的功能:

  • 您可以使用invalidatekeycache,使rowcache无效和invalidatecountercache命令清除缓存。
  • 使用setcachecapacity命令覆盖已配置的密钥和行缓存容量设置。
  • 使用setcachekeystosave命令覆盖已配置的设置,以保存要保存到文件的密钥和行缓存元素的数量。

请记住,这些设置将恢复为节点重新启动时cassandra.yaml文件中设置的值。

Memtables

Cassandra使用memtables加速写入,保存与其存储的每个表相对应的memtable。当达到提交日志阈值或可记录阈值时,Cassandra将memtables作为SSTables刷新到磁盘。

Cassandra将memtables存储在Java堆,堆外(本机)内存或两者上。可以通过属性memtable_heap_space_in_mb和memtable_offheap_space_in_mb分别设置堆和堆外内存的限制。默认情况下,Cassandra将每个值设置为cassandra-env.sh文件中设置的总堆大小的1/4。为memtables分配内存会减少可用于缓存和其他内部Cassandra结构的内存,因此请仔细调整并以较小的增量进行调整。

您可以通过memtable_allocation_type属性影响Cassandra如何分配和管理内存。此属性配置另一个Cassandra的可插入接口,选择抽象类org.apache.cassandra.utils.memory.MemtablePool的哪个实现用于控制每个memtable使用的内存。默认值heap_buffers使Cassandra使用Java New I / O(NIO)API在堆上分配memtables,而offheap_buffers使用Java NIO在堆上和堆外分配每个memtable的一部分。 offheap_objects直接使用本机内存,使得Cassandra完全负责内存管理和memtable内存的垃圾收集。这是一个记录较少的功能,因此最好坚持使用默认值,直到您获得更多经验。

调整memtables的另一个元素是memtable_flush_writers。此设置默认为2,表示在必要时用于写出memtables的线程数。如果您的数据目录由SSD支持,则应将其增加到核心数,而不超过最大值8.如果您有一个非常大的堆,它可以提高性能以将此计数设置得更高,因为这些线程被阻止在磁盘I / O期间。

您还可以通过CQL CREATE TABLE或ALTER TABLE命令在每个表上启用计量刷新。 memtable_flush_period_in_ms选项设置将memtable刷新到磁盘的时间间隔。

设置此属性会导致更可预测的写入I / O,但也会导致更多SSTable和更频繁的压缩,从而可能影响读取性能。默认值0表示禁用定期刷新,并且只会根据提交日志阈值或达到可记忆阈值进行刷新。

提交日志

作为处理更新操作的一部分,Cassandra写入了两组文件:提交日志和SSTable文件。需要考虑它们的不同目的,以便了解在配置期间如何对待它们。

请记住,提交日志可以被视为短期存储,有助于确保在将memtables刷新到磁盘之前,如果节点崩溃或关闭,数据不会丢失。那是因为重新启动节点时,会重播提交日志。实际上,这是唯一一次读取提交日志;客户从不读它。但是对提交日志的正常写入操作会阻塞,因此会损坏性能以要求客户端等待写入完成。

您可以设置允许提交日志在停止向文件追加新写入并创建新文件之前增长的大小值。使用commitlog_segment_size_in_mb属性设置此值。默认情况下,该值为32 MB。这类似于在日志记录引擎(如Log4J或Logback)上设置日志轮换。

分配给提交日志的总空间由commitlog_total_ space_in_mb属性指定。将此值设置为较大值意味着Cassandra需要不太频繁地将表刷新到磁盘。

在将所有附加数据成功刷新到专用SSTable文件之后,会定期删除提交日志。因此,提交日志不会增长到接近SSTable文件大小的任何位置,因此磁盘不需要那么大;在硬件选择期间需要考虑这一点。

要增加提交日志可以容纳的写入量,您需要通过commitlog_compression属性启用日志压缩。支持的压缩算法是LZ4,Snappy和Deflate。使用压缩需要额外的CPU时间来执行压缩。

其他设置与提交日志同步操作有关,由commitlog_sync元素表示。有两种可能的设置:定期和批量。默认值为periodic,这意味着服务器将仅在指定的时间间隔内使写入持久。间隔由commitlog_sync_period_in_ms属性指定,默认为10000(10秒)。

为了保证Cassandra集群的耐用性,您可能需要检查此设置。当服务器设置为定期持久写入时,您可能会丢失尚未从后写高速缓存同步到磁盘的数据。

如果您的提交日志设置为批处理,它将阻塞,直到写入同步到磁盘(在提交日志完全同步到磁盘之前,Cassandra不会确认写入操作)。改变这个值需要采取一些性能指标,因为这里有必要的权衡:迫使Cassandra立即编写,限制了它自由管理自己的资源。如果将commitlog_sync设置为batch,则需要为commit_log_ sync_batch_window_in_ms提供合适的值,其中ms是每次同步工作之间的毫秒数。

SSTables

与提交日志不同,Cassandra异步将SSTable文件写入磁盘。如果您使用的是硬盘驱动器,建议您将数据文件和提交日志存储在不同的磁盘上以获得最佳性能。如果要在固态驱动器(SSD)上进行部署,则可以使用相同的磁盘。

与许多数据库一样,Cassandra尤其依赖于硬盘的速度和CPU的速度。为了利用Cassandra高度并发的结构,拥有多个处理核心而不是一个或两个非常快的处理核心更为重要。因此,如果您能负担得起,请确保QA和生产环境能够获得最快的磁盘 - 固态硬盘。如果您正在使用硬盘,请确保至少有两个硬盘,以便您可以将提交日志文件和数据文件存储在不同的磁盘上,从而避免竞争I / O时间。

从磁盘读取SSTable文件时,Cassandra使用缓冲区缓存(也称为缓冲池)来帮助减少数据库文件I / O.此缓存使用堆外内存,但其大小默认为512 MB,或Java堆的1/4,以较小者为准。您可以使用cassandra.yaml中的file_cache_size_in_mb属性设置缓存大小。通过将buffer_pool_use_heap_if_exhausted设置为true,您还可以允许Cassandra在堆外缓存已满时将Java堆用于缓冲区。

正如我们在第9章中讨论的那样,Cassandra在内存中维护SSTable索引摘要,以加快对SSTable文件的访问。默认情况下,Cassandra将5%的Java堆分配给这些索引,但您可以通过cassandra.yaml中的index_summary_capacity_in_mb属性覆盖它。为了保持在分配的限制范围内,Cassandra将缩小与不常读取的表相关联的索引。由于读取速率可能会随时间发生变化,因此Cassandra还会以index_ summary_ resize_interval_in_minutes属性指定的频率重新采样存储在磁盘上的文件的索引,默认值为60。

Cassandra还提供了一种机制来影响分配给不同表的索引的相对空间量。这是通过min_index_ interval和max_index_interval属性完成的,可以通过CQL CREATE TABLE或ALTER TABLE命令设置这些属性。这些值指定每个SSTable存储的索引条目的最小和最大数量。

hinted handoff

提示切换是Cassandra提供的几种机制之一,用于在整个群集中保持数据同步。正如我们之前所了解到的,协调器节点可以代表节点停留一段时间的数据副本。我们可以根据我们愿意用来存储提示的磁盘空间量以及节点重新联机时提供的提示速度来调整提示切换。

我们可以使用属性hinted_handoff_throttle_in_kb控制提示传递的带宽利用率,或者在运行时通过nodetool sethintedhandoff throttlekb控制。

此限制限制的默认值为1024或1 MB /秒,用于设置接收提示的节点所需带宽的上限阈值。例如,在三个节点的集群中,向第三个节点传递提示的两个节点中的每一个都会将其提示传递限制为该值的一半,或512KB /秒。

请注意,配置此限制与配置其他Cassandra功能略有不同,因为接收提示的节点将观察到的行为完全基于其他节点上的属性设置。您肯定希望在群集中同步使用这些设置的相同值以避免混淆。

在3.0之前的版本中,Cassandra将提示存储在未复制到其他节点的表中,但从3.0版本开始,Cassandra将提示存储在hints_directory属性指定的目录中,该属性默认为Cassandra下的data / hints目录安装。您可以通过max_hints_file_size_in_mb属性设置专用于提示的磁盘空间量上限。

您可以使用带有IP地址或主机名列表的nodetool truncatehints命令清除等待传递到一个或多个节点的任何提示。提示最终在max_hint_window_in_ms属性表示的值之后到期。

正如我们在第10章中所学到的,也可以完全启用或禁用提示切换。虽然有些人会将其用作节省磁盘和带宽的方法,但通常情况下,提示的切换机制与额外的相比不会消耗大量资源。它有助于提供一致性,特别是与修复节点的成本相比。

压实

Cassandra提供压缩的配置选项,包括节点上压缩使用的资源,以及用于每个表的压缩策略。

为表格选择正确的压缩策略当然可以成为提高性能的一个因素。让我们回顾一下可用的策略,并讨论何时应该使用它们。

  • SizeTieredCompactionStrategy
    SizeTieredCompactionStrategy(STCS)是默认的压缩策略,在大多数情况下应该使用它。该策略将SSTable分为按大小组织的层。如果层中有足够数量的SSTable(默认为4个或更多),则运行压缩以将它们组合成更大的SSTable。随着数据量的增长,会创建越来越多的层。 STCS对于写入密集型表执行得特别好,但对于读密集型表而言则较少,因为特定行的数据可能分布在平均10个左右的SSTable上。

  • LeveledCompactionStrategy

    LeveledCompactionStrategy(LCS)创建固定大小的SSTable(默认为5 MB)并将它们分组为级别,每个级别的SSTable数量是前一级别的10倍。 LCS保证给定行每个级别最多出现一个SSTable。 LCS在I / O上花费额外的精力来最小化一行中出现的SSTable数量;给定行的平均SSTable数为1.11。如果需要高读写比率或可预测的延迟,则应使用此策略。如果群集已经受I / O限制,LCS往往不会表现得那么好。如果写作主导读取,卡桑德拉可能很难跟上。

  • DateTieredCompactionStrategy

    DateTieredCompactionStrategy(DTCS)是在2.0.11和2.1.1版本中引入的。它旨在提高时间序列数据的读取性能,特别是涉及访问最近写入的数据的访问模式。它的工作原理是在由数据写入时间组织的窗口中对SSTable进行分组。压缩仅在这些窗口中执行。由于DTCS相对较新且针对特定用例,因此请确保在使用之前仔细研究。

每种策略都有自己的特定参数,可以进行调整。有关详细信息,请查看发行版的文档。
使用Write Survey模式测试压缩策略

如果您想为表使用不同的压缩策略进行测试,则无需在整个群集上更改它以查看其工作原理。相反,您可以创建在写入调查模式下运行的测试节点,以查看新压

缩策略的工作方式。

为此,请将以下选项添加到测试节点的cassandra-env.sh文件中:

JVM_OPTS =“$ JVM_OPTS -Dcassandra.write_survey = true”
JVM_OPTS =“$ JVM_OPTS -Djoin_ring = false”

一旦节点启动,您就可以访问org.apache.cassandra.db> Tables下的表的org.apache.cassandra.db.ColumnFamilyStoreMBean,以便配置压缩策略。将CompactionStrategyClass属性设置为完全限定的压缩策略类名称。

更改此配置后,通过nodetool join将节点添加到环中,以便它可以开始接收写入。写入测试节点会对集群施加最小的额外负载,并且不计入一致性级别。您现在可以使用nodetool tablestats和tablehistograms监视对节点的写入性能。

您可以通过停止节点,将其作为独立计算机启动,然后测试节点上的读取性能来测试新压缩策略对读取的影响。

写入调查模式对于测试其他配置更改甚至是新版本的Cassandra也很有用。

另一个每表设置是压缩阈值。 压缩阈值是指在实际启动次要压缩之前要压缩的队列中的SSTable数。 默认情况下,最小数量为4,最大值为32.您不希望此数字太小,或者Cassandra会花时间与客户争夺资源以执行许多频繁的,不必要的压缩。 您也不希望这个数字太大,或者Cassandra可能会花费大量资源同时执行多个压缩,因此可用于客户端的资源更少。

使用CQL CREATE TABLE或ALTER TABLE命令为每个表设置压缩阈值。 但是,您可以使用nodetool getcompactionthreshold或setcompactionthreshold命令检查或覆盖特定节点的此设置:

$ nodetool getcompactionthreshold hotel hotels
Current compaction thresholds for hotel/hotels:
 min = 4,  max = 32
$ nodetool setcompactionthreshold hotel hotels 8 32

压缩在I / O和CPU方面可能非常密集,因此Cassandra能够监控压实过程并影响压缩发生的时间。

您可以使用nodetool compactionstats命令监视节点上的压缩状态,该命令列出了每个活动压缩的已完成字节和总字节数(为简洁起见,我们省略了ID列)

$ nodetool compactionstats

pending tasks: 1
   id  compaction type  keyspace  table   completed  total      unit   progress
  ...  Compaction       hotel     hotels  57957241   127536780  bytes  45.44%
Active compaction remaining time :   0h00m12s

如果您看到挂起的压缩开始堆叠,则可以使用nodetool命令getcompactionthroughput和setcompactionthroughput来检查和设置Cassandra应用于群集中压缩的限制。这对应于cassandra.yaml文件中的属性compaction_throughput_mb_per_sec。将此值设置为0会完全禁用限制,但对于大多数非写入密集型情况,默认值16 MB / s就足够了。

如果这不能解决问题,可以通过在cassandra.yaml文件中设置concurrent_compactors属性或在运行时通过CompactionManagerMBean来增加专用于压缩的线程数。此属性默认为磁盘数和核心数的最小值,最小值为2,最大值为8。

尽管不常见,但是大型压缩可能会对群集的性能产生负面影响。您可以使用nodetool stop命令停止节点上的所有活动压缩。您还可以通过ID识别要停止的特定压缩,其中ID是从compactionstats输出中获取的。 Cassandra将重新安排任何已停止的压缩以便稍后运行。

您可以通过nodetool compact命令强制执行主要压缩。在手动启动主要压缩之前,请记住这是一项昂贵的操作。压缩期间nodetool compact的行为取决于使用的压缩策略。我们将在第14章讨论每种压缩策略对磁盘使用的影响。

nodetool compactionhistory命令打印有关已完成压缩的统计信息,包括压缩前后的数据大小和合并的行数。输出非常详细,所以我们在这里省略了它。

并发和线程

Cassandra与许多数据存储的不同之处在于它提供比读取性能更快的写入性能。有两个设置与多少线程可以执行读写操作有关:concurrent_reads和concurrent_writes。一般来说,Cassandra开箱即用的默认值非常好。

concurrent_reads设置确定节点可以服务的同时读取请求数。默认值为32,但应设置为用于数据存储的驱动器数量×16。这是因为当数据集大于可用内存时,读取操作受I / O限制。

concurrent_writes设置的行为有所不同。这应该与将同时写入服务器的客户端数量相关联。如果Cassandra支持Web应用程序服务器,您可以将此设置从其默认值32调整为与应用程序服务器可用于连接到Cassandra的线程数相匹配。在Java应用程序服务器中,更喜欢不超过20或30的数据库连接池,但如果您在群集中使用多个应用程序服务器,则还需要将其考虑在内。

另外两个设置 - concurrent_counter_writes和concurrent_materialized_view_writes - 可用于调整特殊形式的写入。因为计数器和物化视图写入都涉及写入之前的读取,所以最好将其设置为concurrent_reads和concurrent_writes中的较低者。

cassandra.yaml文件中还有其他几个属性,用于控制分配给线程池的线程数,这些线程池在Cassandra的SEDA方法中实现了各个阶段。我们已经看过其中的一些了,但这里有一个总结:

  • max_hints_delivery_threads
    专用于提示传递的最大线程数
  • memtable_flush_writers
    专用于将memtables刷新到磁盘的线程数
  • concurrent_compactors
    专用于运行压缩的线程数
    native_transport_max_threads
    用于处理传入CQL请求的最大线程数(您可能还会注意到类似的属性rpc_min_threads和rpc_ max_ threads,这些属性适用于已弃用的Thrift接口)

请注意,其中一些属性允许Cassandra动态分配和释放线程达到最大值,而其他属性则指定静态线程数。向上或向下调整这些属性会影响Cassandra如何使用其CPU时间以及它可以为各种活动投入多少I / O容量。

网络和超时

由于Cassandra是一个分布式系统,它提供了处理网络和节点问题的机制,包括重试,超时和限制。我们已经讨论了Cassandra实现重试逻辑的几种方法,例如DataStax客户端驱动程序中的RetryPolicy,以及驱动程序和节点中的推测性读取执行。

现在让我们来看看Cassandra提供的超时机制,以帮助它避免无限期地挂起等待其他节点做出响应。表12-1中列出的超时属性在cassandra.yaml文件中设置。

Property NameDefault ValueDescription
read_request_timeout_in_ms5000 (5 seconds)How long the coordinator waits for read operations to complete
range_request_timeout_in_ms10000 (10 seconds)How long the coordinator should wait for range reads to complete
write_request_timeout_in_ms2000 (2 seconds)How long the coordinator should wait for writes to complete
counter_write_request_timeout_in_ms5000 (5 seconds)How long the coordinator should wait for counter writes to complete
cas_contention_timeout_in_ms1000 (1 second)How long a coordinator should continue to retry a lightweight transaction
truncate_request_timeout_in_ms60000 (1 minute)How long the coordinator should wait for truncates to complete (including snapshot)
streaming_socket_timeout_in_ms3600000 (1 hour)How long a node waits for streaming to complete

request_timeout_in_ms 10000 (10 seconds) The default timeout for other, miscellaneous operations

这些超时的值通常是可以接受的,但您可能需要针对您的网络环境进行调整。

与超时相关的另一个属性是cross_node_timeout,默认为false。如果在您的环境中配置了NTP,请考虑启用此功能,以便节点可以更准确地估计协调器何时超时长时间运行请求并更快地释放资源。

Cassandra还提供了几个属性,允许您限制它将用于各种操作的网络带宽量。调整这些可以防止Cassandra淹没您的网络,但代价是花费更长的时间来完成这些任务。例如,stream_throughput_outbound_megabits_per_sec和inter_dc_stream_throughput_outbound_megabits_per_sec属性分别指定流式文件传输到本地和远程数据中心中的其他节点的每线程限制。

暗示切换和批量日志重放的限制工作略有不同。 hinted_handoff_throttle_in_kb和batchlog_ replay_ throttle_in_kb指定的值被视为群集的最大吞吐量值,因此根据以下公式按比例分布在节点上:

T x = T t N n − 1 T_x = \frac{T_t}{N_n - 1} Tx=Nn1Tt

也就是说,节点x(Tx)的吞吐量等于总吞吐量(Tt)除以集群中节点数(Nn)的1。

最后,有几个属性可用于限制每个节点上本机CQL端口的流量。在您无法直接控制群集的客户端应用程序的情况下,这些可能很有用。 native_transport_max_frame_size_in_mb属性指定的默认最大帧大小为256.节点将拒绝大于此的帧请求。

节点还可以通过native_transport_max_concurrent_connections属性限制最大并发客户端连接数,但默认值为-1(无限制)。如果配置此值,则需要根据concurrent_readers和concurrent_writers属性确保它有意义。

要限制来自单个源IP地址的同时连接数,请配置native_transport_max_concurrent_connections_per_ip属性,默认为-1(无限制)。

JVM设置

Cassandra允许您配置各种选项,以了解服务器JVM应如何启动,应分配多少Java内存等等。在本节中,我们将介绍如何调整启动。

如果您使用的是Windows,则启动脚本称为cassandra.bat,而在Linux上则为cassandra.sh。您可以通过简单地执行此文件来启动服务器,该文件设置了几个默认值。但conf目录中还有另一个文件,允许您配置与Cassandra启动方式相关的各种设置。此文件名为cassandra-env.sh(Windows上为cassandra-env.ps1),它将某些选项(如JVM设置)分隔为不同的文件,以便于更新。

尝试调整在启动时传递给JVM的选项以获得更好的性能。关于cassandra-env.sh中包含的关键JVM选项以及如何调整它们的指导原则将立即进行讨论。如果要更改任何这些值,只需在文本编辑器中打开cassandra-env.sh文件,更改值,然后重新启动。

jvm.options文件

Cassandra 3.0版本在conf目录中引入了另一个名为jvm.options的设置文件。此文件的目的是将与堆大小和垃圾回收相关的JVM设置从cassandra.in.sh文件移动到单独的文件,因为这些是最常调整的属性。 cassandra-env.sh文件包含(源代码)jvm.options和cassandra.in.sh

内存

默认情况下,Cassandra使用以下算法来设置JVM堆大小:如果计算机的RAM小于1 GB,则堆将设置为RAM的50%。如果机器具有超过4 GB的RAM,则堆设置为RAM的25%,上限为8 GB。要调整最小和最大堆大小,请使用-Xms和-Xmx标志。应将这些值设置为相同的值,以允许整个堆锁定在内存中而不会被操作系统换出。如果使用并行标记扫描(CMS)垃圾收集器,则不建议将堆设置为大于8GB,因为大于此值的堆大小往往会导致更长的GC暂停。

在进行性能调优时,最好只设置堆最小和最大选项,而不是先设置其他任何选项。只有在您的环境中使用实际情况并在堆分析工具的帮助下进行一些性能基准测试并观察特定应用程序的行为之后,您才应该深入调整更高级的JVM设置。如果您调整JVM选项并看到一些成功,请不要太兴奋。您需要在真实条件下进行测试。

一般情况下,如果遇到内存不足错误(这是cassandra-env.sh中的默认值,由-XX设置),您可能希望确保已指示堆转储其状态: + HeapDumpOnOutOfMemory选项。如果您开始遇到内存不足错误,这只是一个很好的做法。

垃圾收集

Java堆大致分为两个对象空间:年轻和旧。年轻空间被细分为一个用于新对象分配(称为“伊甸园空间”),另一个用于仍在使用的新对象(“幸存者空间”)。

Cassandra在年轻一代使用并行复制收集器;这是通过-XX:+ UseParNewGC选项设置的。较旧的对象仍然有一些参考,因此幸存了几个垃圾收集,因此幸存者比率是堆的年轻对象部分中的伊甸园空间与幸存者空间的比率。增加比率对于具有大量新对象创建和低对象保留的应用程序是有意义的;减少它对于具有较长寿命对象的应用程序是有意义的。 Cassandra通过-XX:SurvivorRatio选项将此值设置为8,这意味着伊甸园与幸存者空间的比率为1:8(每个幸存者空间将是伊甸园大小的1/8)。这相当低,因为对象在memtables中的寿命更长。

每个Java对象的标题中都有一个age字段,表示在年轻代空间中复制了多少次。当对象在年轻代垃圾收集中存活时,它们被复制到新的空间中,并且这种复制具有成本。由于长生命对象可能会被多次复制,因此调整此值可以提高性能。默认情况下,Cassandra通过-XX:MaxTenuringThreshold选项将此值设置为1。将其设置为0可立即将年轻代集合中存活的对象移动到旧代。将幸存者比率与期限阈值一起调整。

默认情况下,现代Cassandra版本使用旧版本的Concurrent Mark Sweep(CMS)垃圾收集器;这是通过-XX:+ UseConcMarkSweepGC选项设置的。此设置使用更多RAM和CPU电源在应用程序运行时进行频繁的垃圾收集,以便将GC暂停时间降至最低。使用此策略时,将堆的最小值和最大值设置为相同的值非常重要,以防止JVM最初花费大量时间来增加堆。

移动到垃圾第一垃圾收集器

垃圾第一垃圾收集器(也称为G1GC)是在Java 7中引入的,作为CMS GC的改进和最终替代,特别是在具有更多内存的多处理器机器上。

G1GC将堆分成多个相等大小的区域,并动态地将它们分配给eden,survivor和old代,以便每一代都是不需要是内存中连续区域的区域的逻辑集合。这种方法使垃圾收集能够持续运行,并且需要更少的“停止世界”暂停,这是传统垃圾收集器的特征。

G1GC通常需要较少的调整决策;预期用途是您只需要定义最小和最大堆大小以及暂停时间目标。较短的暂停时间将导致GC更频繁地发生。

G1GC正在成为Cassandra 3.0版本的默认版本,但作为JIRA问题CASSANDRA-10403的一部分被撤销,原因是它对于小于8 GB的堆大小,其性能不如CMS。

如果您想在Cassandra 2.2或更高版本上尝试使用G1GC和更大的堆大小,可以在cassandra-env.sh文件(或jvm.options)文件中随时使用这些设置。

寻找G1GC成为未来Cassandra版本的默认版本,最有可能与Java 9的支持相结合,其中G1GC将成为Java默认版本。

为了更深入地了解Cassandra节点中的垃圾收集活动,有几个选项。 cassandra.yaml文件中的gc_warn_threshold_in_ms设置确定将导致Cassandra在WARN日志记录级别生成日志消息的暂停长度。此值默认为1000毫秒(1秒)。您还可以通过设置cassandra-env.sh或jvm.options文件中的选项来指示JVM打印垃圾收集详细信息。

Using cassandra-stress

Cassandra附带了一个名为cassandra-stress的实用程序,可用于在Cassandra集群上运行压力测试。要运行cassandra-stress,请导航到 / tools / bin目录并运行命令:

$ cassandra-stress write n=1000000
Connected to cluster: test-cluster
Datatacenter: datacenter1; Host: localhost/127.0.0.1; Rack: rack1
Datatacenter: datacenter1; Host: /127.0.0.2; Rack: rack1
Datatacenter: datacenter1; Host: /127.0.0.3; Rack: rack1
Created keyspaces. Sleeping 1s for propagation.
Sleeping 2s...
Warming up WRITE with 50000 iterations...
Running WRITE with 200 threads for 1000000 iteration
...

输出列出了工具所连接的节点(在本例中为使用ccm创建的集群),并创建了一个可以写入数据的示例键空间和表。 通过执行50,000次写入来预热测试,然后该工具在继续写入时开始输出度量,为简洁起见,我们省略了这些度量。 该工具创建一个线程池(在我的系统上默认为200),执行一个接一个的写入,直到它插入一百万行。 最后,它打印结果摘要:

Results:
op rate                   : 7620 [WRITE:7620]
partition rate            : 7620 [WRITE:7620]
row rate                  : 7620 [WRITE:7620]
latency mean              : 26.2 [WRITE:26.2]
latency median            : 2.6 [WRITE:2.6]
latency 95th percentile   : 138.4 [WRITE:138.4]
latency 99th percentile   : 278.8 [WRITE:278.8]
latency 99.9th percentile : 393.3 [WRITE:393.3]
latency max               : 820.9 [WRITE:820.9]
Total partitions          : 1000000 [WRITE:1000000]
Total errors              : 0 [WRITE:0]
total gc count            : 0
total gc mb               : 0
total gc time (s)         : 0
avg gc time(ms)           : NaN
stdev gc time(ms)         : 0
Total operation time      : 00:02:11

让我们解开一下。 我们所做的工作是在两分多钟内生成并插入一个完全未调整的三节点集群中的一百万个值,这表示每秒7,620次写入的速率。 每次操作的中间延迟为2.6毫秒,尽管少量写入花费的时间更长。

现在我们在数据库中拥有了所有这些数据,让我们使用测试来读取一些值:

$ cassandra-stress read n=200000 
...
Running with 4 threadCount
Running READ with 4 threads for 200000 iteration

如果检查输出,您将看到它以少量线程开始并且斜坡上升。 在一次测试运行中,它达到峰值超过600个线程,如下所示:

Results:
op rate                   : 13828 [READ:13828]
partition rate            : 13828 [READ:13828]
row rate                  : 13828 [READ:13828]
latency mean              : 67.1 [READ:67.1]
latency median            : 9.9 [READ:9.9]
latency 95th percentile   : 333.2 [READ:333.2]
latency 99th percentile   : 471.1 [READ:471.1]
latency 99.9th percentile : 627.9 [READ:627.9]
latency max               : 1060.5 [READ:1060.5]
Total partitions          : 200000 [READ:200000]
Total errors              : 0 [READ:0]
total gc count            : 0
total gc mb               : 0
total gc time (s)         : 0
avg gc time(ms)           : NaN
stdev gc time(ms)         : 0
Total operation time      : 00:00:14
Improvement over 609 threadCount: 7%

该工具会定期打印出有关最后几次写入的统计信息。 前面的输出来自最后一个统计数据块。 正如你所看到的,Cassandra的读取速度几乎和它写的一样快; 平均读取延迟大约为10毫秒。 但请记住,这是开箱即用的,未经调整的单线程,在运行其他程序的常规工作站上,并且数据库大小约为2 GB。 无论如何,这是一个很好的工具,可以帮助您针对您的环境进行性能调优,并获得一组数字,指示您的群集中有什么期望。

我们还可以通过在yaml文件中创建规范来对我们自己的表运行cassandra-stress。 例如,我们可以创建一个cqlstress-hotel.yaml文件来描述从酒店键空间中的表读取的查询。 此文件定义了我们用来强调available_rooms_by_hotel_date表的查询:

keyspace: hotel
table: available_rooms_by_hotel_date

columnspec:
  - name: date
    cluster: uniform(20..40)

insert:
  partitions: uniform(1..50)
  batchtype: LOGGED
  select: uniform(1..10)/10

queries:
   simple1:
      cql: select * from available_rooms_by_hotel_date 
        where hotel_id = ? and date = ?
      fields: samerow             
   range1:
      cql: select * from available_rooms_by_hotel_date 
        where hotel_id = ? and date >= ? and date <= ?
      fields: multirow   

然后我们可以在cassandra-stress运行中执行这些查询。 例如,我们可能会运行写入,单项查询和范围查询的混合加载,如下所示:

$ cassandra-stress user profile=~/cqlstress-hotel.yaml 
  ops\(simple1=2,range1=1,insert=1\) no-warmup

与每个查询相关联的数字表示所需的使用比率。此命令为每次写入执行三次读取。

关于cassandra压力的额外帮助

有很多选择。您可以执行cassandra-stress帮助以获取支持的命令和选项列表,并使用cassandra-stress help 获取有关特定命令的更多信息。

您可能还想研究cstar_perf,这是一个由DataStax提供的Cassandra开源性能测试平台。此工具支持压力测试的自动化,包括在多个Cassandra版本中启动集群和运行性能测试的能力,或者用于比较目的的具有不同配置设置的集群。它提供了一个基于Web的用户界面,用于创建和运行测试脚本以及查看测试结果。您可以下载cstar_perf并阅读http://datastax.github.io/cstar_perf/setup_cstar_perf_tool.html 上的文档。

总结

在本章中,我们研究了管理Cassandra性能以及Cassandra中可用的设置,以帮助进行性能调整,包括缓存设置,内存设置和硬件问题。我们还设置并使用cassandra-stress测试工具来编写然后读取一百万行数据。

如果您对Linux系统有些新手并且想要在Linux上运行Cassandra(推荐使用),您可能需要查看Al Tobey关于调优Cassandra的博客文章。这将介绍几种Linux性能监视工具,以帮助您了解底层平台的性能,以便您可以在正确的位置进行故障排除。虽然这篇文章引用了Cassandra 2.1,但它所包含的建议广泛适用。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值