实战-Cassandra之性能调优

 

配置文件和各个表上都有大量设置。尽管默认设置对很多用例都很使用,但有些情况下可能还需要做些改变。

 

管理性能

设置性能目标

规划一个集群时,要了解这个集群将如何使用,这一点很重要,包括客户数量,预期使用模式和预计峰值负载时间等。这对规划出事集群容量和集群的扩展会很有用。

性能调优都要做出一些取舍,明确性能目标

  • 启用SSTable压缩可以节省空间,但要以额外的CPU处理为代价
  • 如果限制使用网络和线程,这可以用来保持网络和CPU的使用处于控制之下,但代价是会减少吞吐量和增加延迟
  • 可以增加或减少分配给特定任务(如读,写或合并)的线程数,来影响这个任务相对其他任务的优先级,或者支持额外的客户
  • 可以增加堆的大小来减少查询时间 

 

性能监控

随着集群规模的增长,客户数量的增加以及增加更多的键空间和表,对集群的需求可能开始往不同的方向牵引。经常依据集群目标测量集群的性能变得越来越重要。

通过 nodetool tpstats / tbalestats 查看加载和延迟问题

nodetool proxyhistograms 显示读请求、写请求和区间请求的延迟,这里所请求的节点作为协调器。

nodetool tablehistograms 显示特定表的性能

 

分析性能问题

当心大分区:除了 nodetool tablehistograms,还可以搜索日志,查找提到"写大分区" ("Writing large partition") 或 "合并大分区" ("Compacting large partition") 的 WARN 消息来检测大分区。大分区合并的警告阈值由 cassandra.yaml 文件中的 compaction_large_partition_warning_threshold_mb 属性设置

 

跟踪

通过tracing命令跟踪了解各个查询中设计的客户端与节点之间的通信,以及每一步花费的时间。这会帮助我们了解有关数据模型的设计决策和所选择的副本因子和一致性级别对性能会有怎样的影响。

输出内容:准备语句,读修复,键缓存搜索,memtable和SSTable数据查找,节点间交互等活动以及每一步的响应时间 (单位是微秒)

cqlsh> tracing;
Tracing is currently disabled. Use TRACING ON to enable.
cqlsh> tracing on;
Now Tracing is enabled
cqlsh> select * from keyspace1.standard1 limit 2;

 key                    | C0                                                                     | C1                                                                     | C2                                                                     | C3                                                                     | C4
------------------------+------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------------
 0x3335503436334b333630 | 0x89a7f8588f1f3866788af519bedb8ff284e72843faa37694fcca88b8e24f3d6861d4 | 0xa9b176a554a1d1c1acb860d9b49e05b4f21ab8b7933357e49e48d353147d7ccf7ba7 | 0xd310294bbc90b9a1a99642d85625744a9df753f9688f18074ecf3ae17d806491ff02 | 0x022c59df825545f71d1cd301919821544eb2125b23f2f31c3addd1aa97c163a3aa44 | 0xf8dabfd49bee232435eabd034263706b6d50417a005f25f4320a88bad0adff847aec
 0x4f36393733364b393930 | 0x62cebaf3cf2491d39bae52e3589943737f3c44b02de67dc0f213623412d4167e3b0e | 0x0d01092e67706b72e44ed4774c6a77b63fcda402adf6d8e63048e65f462593e62098 | 0x27ecd293bed41cc177547fac81011e72f4398a22fe74825ec2b2b822e7787e09be69 | 0x3ded968484b698f60b1732b8ab4802520bc653d94084ed8a6434396c62c7a52bec7a | 0xfb1392dbcd6d5d21ed699a61f4a40914d7560cc929fea3168353fee189416bb97045

(2 rows)

Tracing session: 1f01b040-4a68-11ea-8692-bffd44047be6

 activity                                                                                                                          | timestamp                  | source        | source_elapsed | client
-----------------------------------------------------------------------------------------------------------------------------------+----------------------------+---------------+----------------+---------------
                                                                                                                Execute CQL3 query | 2020-02-08 06:42:44.548000 | 192.168.56.12 |              0 | 192.168.56.12
                                       RANGE_SLICE message received from /192.168.56.12 [MessagingService-Incoming-/192.168.56.12] | 2020-02-08 06:32:42.997000 | 192.168.56.13 |             82 | 192.168.56.12
                      Executing seq scan across 7 sstables for (min(-9223372036854775808), max(3074457345618258602)] [ReadStage-2] | 2020-02-08 06:32:43.002000 | 192.168.56.13 |           5637 | 192.168.56.12
                                                                              Read 2 live rows and 0 tombstone cells [ReadStage-2] | 2020-02-08 06:32:43.007000 | 192.168.56.13 |          10073 | 192.168.56.12
                                                                                Enqueuing response to /192.168.56.12 [ReadStage-2] | 2020-02-08 06:32:43.007000 | 192.168.56.13 |          10402 | 192.168.56.12
                               Sending REQUEST_RESPONSE message to /192.168.56.12 [MessagingService-Outgoing-/192.168.56.12-Small] | 2020-02-08 06:32:43.007000 | 192.168.56.13 |          10789 | 192.168.56.12
                                                  Parsing select * from keyspace1.standard1 limit 2; [Native-Transport-Requests-1] | 2020-02-08 06:42:44.549000 | 192.168.56.12 |            188 | 192.168.56.12
                                                                                 Preparing statement [Native-Transport-Requests-1] | 2020-02-08 06:42:44.549000 | 192.168.56.12 |            374 | 192.168.56.12
                                                                           Computing ranges to query [Native-Transport-Requests-1] | 2020-02-08 06:42:44.549000 | 192.168.56.12 |            665 | 192.168.56.12
 Submitting range requests on 4 ranges with a concurrency of 1 (2.0803392E7 rows per range expected) [Native-Transport-Requests-1] | 2020-02-08 06:42:44.549001 | 192.168.56.12 |            845 | 192.168.56.12
                                                                 Enqueuing request to /192.168.56.13 [Native-Transport-Requests-1] | 2020-02-08 06:42:44.549001 | 192.168.56.12 |            982 | 192.168.56.12
                                                               Submitted 1 concurrent range requests [Native-Transport-Requests-1] | 2020-02-08 06:42:44.549001 | 192.168.56.12 |           1168 | 192.168.56.12
                                    Sending RANGE_SLICE message to /192.168.56.13 [MessagingService-Outgoing-/192.168.56.13-Small] | 2020-02-08 06:42:44.550000 | 192.168.56.12 |           1988 | 192.168.56.12
                                  REQUEST_RESPONSE message received from /192.168.56.13 [MessagingService-Incoming-/192.168.56.13] | 2020-02-08 06:42:44.564000 | 192.168.56.12 |          16138 | 192.168.56.12
                                                                  Processing response from /192.168.56.13 [RequestResponseStage-2] | 2020-02-08 06:42:44.565000 | 192.168.56.12 |          16395 | 192.168.56.12
                                                                                                                  Request complete | 2020-02-08 06:42:44.565211 | 192.168.56.12 |          17211 | 192.168.56.12


cqlsh> tracing off;
Disabled Tracing.
cqlsh> tracing ;
Tracing is currently disabled. Use TRACING ON to enable.

Cassandra将查询的结果存储到 system_traces 键空间中。通过修改cassandra.yaml配置中的tracetype_query_ttl 和 tracetype_repair_ttl 属性配置表的TTL

 

 

调优方法

推荐方法,一次修改一个配置参数。

一般情况下,可能只需要增加更多的资源(如内存或额外的节点) 就能恢复性能,不过要确保不能因此遮盖底层的设计或配置问题。

还有一些情况,可能会发线无法单独铜鼓哦调优达到期望的目标,而需要改变设计。

配置文件 cassandra.yaml  和 cassandra-env.sh

 

 

缓存

缓存可以用来提高读操作的响应性。需要使用额外的内存来存储数据,从而尽可能减少必须完成的磁盘读。随着缓存大小的增加,可以从内存提供服务的"命中"数也会增加。

缓存策略应该根据一下几个因素调整:

  • 考虑你的查询,使用最适合这些查询的缓存类型。
  • 考虑堆大小与缓存大小的比值,不要让缓存占用过多的堆空间
  • 考虑行大小与键大小的比值,通常键要比整个行小得多

键缓存

键缓存存储了分区键和行索引之间的一个映射。以利于更快的访问存储在磁盘上的SSTable。可以对每个表分别配置键缓存的使用。

cqlsh> desc table keyspace1.standard1;

CREATE TABLE keyspace1.standard1 (
    key blob PRIMARY KEY,
    "C0" blob,
    "C1" blob,
    "C2" blob,
    "C3" blob,
    "C4" blob
) WITH COMPACT STORAGE
    AND bloom_filter_fp_chance = 0.01
    AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'}
    AND comment = ''
    AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'}
    AND compression = {'enabled': 'false'}
    AND crc_check_chance = 1.0
    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 = '99PERCENTILE';

由于键缓存可以大大提高操作性能,而且不会占用大量额外的内存,所以默认会启动键缓存。

关闭键缓存:

cqlsh> alter table keyspace1.standard1 with caching = {'keys':'NONE', 'rows_per_partition': 'NONE'};

key_cache_size_in_mb设置表示将用于键缓存的最大内存量,这些内存要由所有的表共享。默认值为总JVM堆的5% 或者 100MB (取二者中较小的一个)


行缓存

行缓存可以缓存整个行,对于频繁访问的行,行缓存可以提高读访问的速度,但代价是要使用更多的内存。

要小心使用行缓存大小设置,因为很容易导致更多的性能问题。对于小数据集,如果所有行都在内存中,行缓存可以得到非常好的性能结果,而对于更大的数据集,必须从磁盘读取数据时,这只会使性能下降。

如果表读操作远多于写操作,配置一个非常大的行缓存会不必要的耗费相当多的服务器资源。

默认值NONE,可用的选项是一个正整数或ALL,例子:将行缓存设置为200行

cqlsh> alter table keyspace1.standard1 with caching = {'keys':'NONE', 'rows_per_partition': '200'};

 

计数器缓存

计数器缓存通过减少对最常访问的计数器的锁竞争来提高计数器性能。对于计数器缓存配置,没有提供为每个表单独配置的选项

counter_cache_size_in_mb 设置确定了将用于计数器缓存的最大内存量,这将由所有表共享。默认值是总JVM堆的2.5%或者50MB (取二者中较小的一个)

 

保存缓存设置

  • 缓存文件保存在 data/saved_caches 目录下,会按key_cache_save_period(默认14000/4小时), row_cache_save_period(默认0/禁用), counter_cache_save_period(默认7200/2小时) 属性指定的间隔 (单位为秒) 写文件
  • 缓存按键值索引。文件中保存的键的个数由 key_cache_keys_to_save, row_cache_keys_to_save, counter_cache_keys_to_save 属性指示。
  • 通过nodetool管理缓存(重启时恢复为 cassandra.yaml文件中设置的值)
  1. 使用invalidatecountercache,invalidatekeycache,invalidaterowcache命令清除缓存。
  2. 使用setcachecapacity命令覆盖为键缓存和行缓存容量配置的设置
  3. 使用setcachekeystosave命令覆盖为保存缓存元素所配置的设置。即一个文件中要保存多少个键缓存和行缓存元素

 

 

Memtable

Cassandra使用memtable来提高写操作的速度,对应所存储的每个表会维护一个memtable。达到提交日志阈值或memtable阈值时,cassandra会把memtable刷新输出到磁盘(作为SSTable)

Cassandra将memtable存储在Java堆或堆外(原生)内存中,也可能二者都有。对于堆和堆外内存的限制分别通过属性memtable_heap_space_in_mb 和 memtable_offheap_sapce_in_mb 设置

默认,Cassandra将这两个值都设置为cassandra-env.sh文件中设置的总的堆大小的 1/4.

另一个和memtable调优有关的元素是 memtable_flush_writers 默认设置为2,指示了在必要时用来写memtable的线程数。如果你的数据目录是ssd,就应该把这个数增加到内核数,但不能超过最大值8.如果有一个很大的堆,把这个数设置得更大可以提高性能,因为线程在磁盘I/O时会阻塞。

还可以通过CQL CREATE TABLE 或 ALTER TABLE 命令启用哥哥表上的定期刷新输出。 memtable_flush_period_in_ms 选项可以设置 memtable 刷新输出到磁盘的间隔。设置这个属性会带来可预测的写I/O,不过也导致更多的SSTable和更频繁的合并。可能会影响读性能。默认0表示禁用定期刷新输出。只是在达到commit log阈值或memtable阈值时才会刷新输出。

 

提交日志

提交日志的正常写操作会阻塞,要求客户端等待这些写操作结束,设置允许的提交日志最大值,达到这个规模后就要停止向这个文件追加新的写操作。而应创建一个新的提交日志文件。这个值用commitlog_segment_size_in_mb属性设置。默认地,这个值为32MB。

为提交日志分配的总的空间由 commitlog_total_space_in_mb属性指定。如设置一个较大的值,Cassandra不需要台频繁的刷新输出到磁盘。

提交日志会定期删除,成功地将所追加的所有数据刷新输出到指定的SSTable后就会删除。由于这个原因,提交日志不扩展得大到接近SSTable文件的大小。

要想增加提交日志包含的写操作数量,可以通过commitlog_compression属性启用日志压缩。目前支持的压缩算法包括 LZ4,Snappy 和 Deflate。使用压缩的代价是需要额外CPU

还有与提交日志同步操作有关,邮commitlog_sync元素表示。值:periodic 和 batch。默认periodic,表示服务器只按指定的间隔保证写操作是持久的。这个间隔时间由commitlog_sync_perion_in_ms属性指定,默认10000(10秒)。倘若服务器设置为只是在周期时间内保证写操作是持久的,就有可能丢失一些数据,因为在这个周期时间里,那些数据可能还没有从"后台写"(write-behind),缓存同步到磁盘。如果设置为batch,则会阻塞,直到写操作同步到磁盘(提交日志同步到磁盘完成之前,Cassandra不会确认写操作完成),如果要求更快地写,就会限制它管理自己资源的自由。修改为batch,需要设置commitlog_sync_batch_window_in_ms为一个合理的值,每次同步的间隔时间。

 

SSTable

Cassandra会一部地向磁盘写SSTable文件,如果普通硬盘,建议将数据文件和提交日志保存到不同的磁盘上,如果SSD,最好使用同一个磁盘。

从磁盘读取SSTable文件时,Cassandra使用了一种缓冲缓存,也称为缓冲池,来帮助减少数据库文件I/O。这个缓存使用堆外内存,不过它的大小默认为512MB或Java堆的1/4(取二者中较小的一个)。可以使用file_cache_size_in_mb属性设置这个缓存大小。还可以把buffer_pool_use_heap_if_exhausted设置为true,允许Cassandra在堆外缓存满时使用Java堆作为缓冲区。

Cassandra在内存中维护SSTable索引摘要来加快对SSTable文件的访问,默认Java堆的5%分配给这些索引,通过cassandra.yaml中index_summary_capacity_in_mb属性覆盖这个设置。为了保持在所分配的限制内,会所见不常读取的表的相关索引。因为读操作数可能随时间变化,会按一定频率堆磁盘上存储的文件重建索引。这个频率由 index_summary_resize_interval_in_minutes属性指定,默认为60.

Cassandra还提供了一种机制,可以影响为不同表的索引分配的相对空间大小。通过CQL CREATE TABLE 或 ALTER TABLE 命令对表进行设置,min_index_interval (每个SSTable存储的最小索引条目数) 和 max_index_interval (每个SSTable存储的最大的索引条目数)

 

 

提示移交

协调器节点可以代表一个宕机节点在一定时间内保存数据的一个副本。

hinted_handoff_throttle_in_kb 控制提示传送时使用的带宽,或使用nodetool sethintedhandoffthrottleke 来控制。默认1MB/s, 对节点接收提示所需的带宽设置一个上限。例如在一个3节点的集群中,两个节点向第三个节点传送提示,这两个节点会分别将提示传送使用的带宽控制为这个值的一半,512kb/s

3.0版本开始,Cassandra将提示存储到 hints_directory 属性指定的一个目录中。默认  data/hints/。 可以通过 max_hints_file_size_in_mb 属性设置存储提示的磁盘空间大小。

nodetool truncatehints 命令,清除等待传送的提示。提示最好会在 max_hint_window_in_ms 属性指定的时间之后过期。

 

 

合并

Cassandra为合并提供了一些配置,包括一个节点上合并所使用的资源,以及每个表使用的合并策略

SizeTieredCompactionStrategy

STCS是默认是合并策略。这种策略将SSTable分组为层(tier)按大小组织,如果一层以及由足够数量的SSTableI (默认为4个或更多),就会运行一个合并,将条目组合为一个更大的SSTable。随着数据量的增加,会创建越来越多的层。STCS特别适合用于写密集的表,而对于读密集的表则不是那么使用,因为某一行的数据可能分布在平均10个左右的SSTable上。

LeveledCompactionStrategy

LCS会创建固定大小的SSTable(默认为5MB),将他们分组为级(level),每一级包含前一级10倍的SSTable,LCS保证给定的一行在每一级最多出现在一个SSTable中,LCS在I/O上开销更大,以尽可能减少包含某一行的SSTable个数,给定的一个平均会出现在1.11个SSTable中。如果读写比很高或者要求延迟可预测时,应当使用这个策略。如果集群以及是I/O密集的,LCS也不会由太好的表现。如果写远远大于读,Cassandra可能就难以为继了。

DateTieredCompactionStrategy

DTCS是2.0.11和2.1.1版本中引入的。这个策略是为了提高时间序列数据的读性能,特别是访问模式涉及要访问最近写入的数据时。它的做法是将按时间窗SSTable分组,由数据的写入时间组织。合并只能在这些时间窗内完成。因为DTCS相对较新,而且针对的是一种特定的用例,在使用之前一定要仔细研究。

 

合并阈值

合并阈值需要逐个表设置。合并阈值是指一个小合并启动之前队列中要合并的SSTable数。默认min=4,max=32。太小Cassandra会花大量时间与客户端争夺资源,来完成大量频率而且没必要的合并。太大,会花费大量的资源一次完成多个合并。

修改合并阈值。通过CQL CREATE TABLE 或 ALTER TABLE命令逐个表的设置。也可以使用 nodetool setcompactionthreshold 进行设置

[cassandra@node2 bin]$ ./nodetool getcompactionthreshold test01 test01
Current compaction thresholds for test01/test01: 
 min = 4,  max = 32
[cassandra@node2 bin]$ ./nodetool setcompactionthreshold test01 test01 8 64
[cassandra@node2 bin]$ ./nodetool getcompactionthreshold test01 test01
Current compaction thresholds for test01/test01: 
 min = 8,  max = 64

 

合并操作

合并操作的I/O和CPU开销都很大,所以Cassandra提供了一种功能可以监控合并过程,并影响合适发生合并。可以使用 nodetool compactionstats 命令监控一个节点上合并的状态,这会列出每个活跃合并已完成的字节数和总字节数。

如果看到合并堆积,可以使用nodetool getcompactionthroughput 和 setcompactionthroughput 查看和设置Cassandra读i整个集群应用的合并节流设置。对应配置文件compaction_throughput_mb_per_sec属性。设置为0会禁用节流控制。不过对于大多数非密集的情况,默认16MB/s 已经足够了

如果还不能解决问题,可以通过设置配置文件 concurrent_compactors属性增加用于合并的线程数。或者在运行时可以通过CompactionManagerMBean来设置。这个属性默认为磁盘数和内核数中的较小值,最小值为2,最大值为8.

很大的合并hi影响集群性能,使用nodetool stop 命令停止一个节点上的所有活跃合并。还可以按ID明确指定停止哪一个合并。折里的ID由nodetool compactionstats输出得到。Cassandra会重新调度以后再运行这些停止的合并。

可以通过 nodetool compact 命令强制完成一个主合并。手动启动一个合并之前,记住,合并是一个开销非常大的操作。取决于所用的合并策略,合并期间nodetool compact 的行为会有所变化。 nodetool compactionhistory 命令会打印已完成的合并的统计信息,包括合并前和合并后的数据大小,以及合并的行数,这个输出非常详细。

 

 

并发和线程

Cassandra与很多数据库不同的一点是,相比于读性能,它可以提供更快的写性能。关于多少个线程完成读和写操作的设置:concurrent_reads 和 concurrent_writes。

concurrent_reads

设置确定节点可以服务多少个并发的读请求。默认32,不过应该设置为用于数据存储的硬盘个数 * 16.这是因为数据集大于可用内存时,读操作是I/O密集型的。

concurrent_writes

这与并发写服务器的客户端个数有关。如果Cassandra再支持一个web应用服务器,可以把这个设置从默认的32调整为这个应用服务器连接Cassandra的线程数,再java应用服务器中,通常希望数据库连接池不超过20或30个线程,不过如果你再使用一个集群中的多个应用服务器,就要把这一点也考虑在内。

concurrent_counter_writes

计数器写操作,设置cr 和 cw 较小者。

concurrent_materialized_view_writes

物化视图的写操作,设置cr 和 cw 较小者。

max_hint_delivery_threads

用于提示传送的最大线程数

memtable_flush_writers

用于将memtable刷新输出到磁盘的线程数

concurrent_compactors

用于运行合并的线程数

native_transport_max_threads

用于处理到来CQL请求的最大线程数(还可以注意到类似的属性rpc_min_threads 和 rpc_max_threads,他们对应以及废弃的 Thrift 接口)

 

 

网络和超时

节点超时设置

read_request_timeout_in_ms: 默认值 5000/5s  协调器等待读完成的时间

range_request_timeout_in_ms: 默认值 10000/10s 协调器等待区间读完成的时间

write_request_timeout_in_ms: 默认值 2000/2s 协调器等待写完成的时间

counter_write_request_timeout_in_ms: 默认值 5000/5s 协调器等待计数器写完成的时间

cas_contention_timeout_in_ms: 默认值 1000/1s 协调器继续重试一个轻量级事务的时间

truncate_request_timeout_in_ms: 默认值 60000/1分钟 协调器等待删除完成(包括快照)的时间

request_timeout_in_ms: 默认值 10000/10s 其他各种操作的默认超时时间

streaming_socket_timeout_in_ms: 3600000/1小时 一个节点等待流传输完成的时间

cross_node_timeout: 默认值 false 启用NTP,对于长时间运行的请求,准确的估计系欸掏钱何时超时并更快地释放资源

stream_throughput_outbound_megabits_per_sec 和 inter_dc_stream_throughput_outbound_megabits_per_sec 属性分别再单个线程上设置一个节流阀,可以控制将文件传输到本地和远程数据中心其他节点的吞吐量。

hinted_handoff_throttle_in_kb 和 batchlog_replay_throttle_in_kb 属性指定的值是集群的最大吞吐量

native_transport_max_frame_size_in_mb: 属性指定默认最大帧大小是256.控制集群客户端应用数据流。

native_transport_max_concurrent_connections: 属性限制节点最大并发客户连接数,默认 -1 (不限制)。如果配置这个值,肯恶搞要确保满足concurrent_readers 和 concurrent_writers属性的前提下这个值是由意义的。

native_transport_max_concurrent_connections_per_ip: 限制来自某个源IP地址的并发连接数。默认 -1 (不限制)

 

 

内存

默认Cassandra使用以下算法来设置JVM堆的大小: 如果机器RAM小于1GB, 堆设置为RAM的50%。如果机器的RAM大于4GB,堆设置为RAM的25%,最大不超过8GB.

要堆堆大小的最小和最大值调优,可以使用-Xms和-Xmx标志。这些设置应该设置为相同的值,这样整个堆就会锁定再内存中,而不会被操作系统患处。不建议设置大于8GB,会歹来更长时间的GC(垃圾回收)

 

 

 Linux性能监控

https://tobert.github.io/pages/als-cassandra-21-tuning-guide.html

 

你可能感兴趣的:(实战-Cassandra之性能调优)