我发现将新节点添加到集群时,查询(只是一个简单的选择查询)会花费很长时间。
我的执行时间日志:
17:49:40.008 [ThreadPoolTaskScheduler-14] INFO task.DiskCounting - void task.DiskCounting.runJob() executed in 8 ms
17:50:00.010 [ThreadPoolTaskScheduler-3] INFO task.DiskCounting - void task.DiskCounting.runJob() executed in 15010 ms
17:50:15.008 [ThreadPoolTaskScheduler-4] INFO task.DiskCounting - void task.DiskCounting.runJob() executed in 10008 ms
17:50:20.008 [ThreadPoolTaskScheduler-16] INFO task.DiskCounting - void task.DiskCounting.runJob() executed in 7 ms
通常大约需要10毫秒,添加节点时突然需要15000毫秒。
我发现它卡住是因为等待新的节点初始化数据
Cassandra日志(新节点):
INFO [HANDSHAKE-/194.187.1.52] 2019-05-31 17:49:36,056 OutboundTcpConnection.java:560 - Handshaking version with /194.187.1.52
INFO [GossipStage:1] 2019-05-31 17:49:36,059 Gossiper.java:1055 - Node /194.187.1.52 is now part of the cluster
INFO [RequestResponseStage-1] 2019-05-31 17:49:36,069 Gossiper.java:1019 - InetAddress /194.187.1.52 is now UP
INFO [GossipStage:1] 2019-05-31 17:49:36,109 TokenMetadata.java:479 - Updating topology for /194.187.1.52
INFO [GossipStage:1] 2019-05-31 17:49:36,109 TokenMetadata.java:479 - Updating topology for /194.187.1.52
INFO [MigrationStage:1] 2019-05-31 17:49:39,347 ViewManager.java:137 - Not submitting build tasks for views in keyspace system_traces as storage service is not initialized
INFO [MigrationStage:1] 2019-05-31 17:49:39,352 ColumnFamilyStore.java:411 - Initializing system_traces.events
INFO [MigrationStage:1] 2019-05-31 17:49:39,382 ColumnFamilyStore.java:411 - Initializing system_traces.sessions
停留在以下时间:节点/194.187.1.52现在是集群的一部分
客户端将等待新节点初始化所有数据
我尝试过的事情:
1. I try use consistency with ONE or QUORUM, and is no difference
2. I try turn replication factor to 1, 2 or 3, and still no difference
当新节点未完全初始化数据时,为什么新节点成为群集的一部分。
有没有办法解决这个问题。
我希望在查询旧节点时,性能不会受到仅等待新节点初始化数据的影响。
答案 0 :(得分:1)
这是一种您会发现一致性过高或没有足够数据副本(复制因子)的行为。当将新节点添加到群集中时,会重新分配令牌的所有权,一旦确定新节点将成为所有者的数据,它将开始流传输该数据,这可能会使网络饱和。
在您的问题中,您没有提及网络设置,或者您是否使用的是云实例,这些实例对这些限制有直接影响,例如,AWS m3.large实例在网络功能方面将比i3受到更多限制。 .4xlarge。
要考虑的其他变量是磁盘配置,如果您使用自己的硬件,请在驱动器设置的IO上限中查找;如果您在云上,则使用实例存储(如果可用)会比外部卷具有更好的性能(例如AWS EBS;如果是这种情况,请确保在实例允许的情况下启用“ EBS优化”选项) )
通常,RF为3且具有Quorum的一致性级别也可以帮助您预防此问题。
答案 1 :(得分:0)
这只是一个理论,但可能的原因是驱动程序客户端选择了新节点作为协调器,在这种情况下,一致性级别和复制不是延迟的主要因素。为您的查询服务。
如果新节点由于某种原因在开始时执行缓慢,并且驱动程序正在向其发送请求,则协调器的行为会影响请求的服务。
runJob
到底在做什么?您建议它是在进行单个查询,但是可能是范围查询吗?
如果这是一个查询,并且要花费10秒的时间,那么这很奇怪,因为默认的read_request_timeout
是5秒。如果是范围查询(涉及多个分区的读取),则默认值为10秒。您要调整这些超时时间吗?
当您看到单个查询的响应时间过长,这可能意味着协调器正在阻碍响应,否则,如果协调器响应并且副本很慢,您会看到ReadTimeoutException
消息已发送给客户端
为了更好地应对这些情况,许多客户端驱动程序实施了一种称为“投机执行”的策略。如documentation for the DataStax Java Driver for Apache Cassandra中所述:
有时,Cassandra节点可能会遇到困难(例如:长时间的GC暂停),并且需要比平时更长的时间来回复。发送到该节点的查询会遇到严重的延迟。
我们可以做的一件改进的事情是,在第一个节点已回复或错误之前,先抢先对另一个节点执行第二次查询。如果第二个节点的回复速度更快,我们可以将响应发送回客户端(我们还取消了第一次执行-请注意,在这种情况下,“取消”只是意味着在稍后到达时丢弃响应,Cassandra不支持取消飞行此阶段的请求)
您可以将驱动程序配置为对幂等请求(例如读取)以恒定的阈值进行推测性执行。在3.x Java驱动程序中,它是通过以下方式完成的:
Cluster cluster = Cluster.builder()
.addContactPoint("127.0.0.1")
.withSpeculativeExecutionPolicy(
new ConstantSpeculativeExecutionPolicy(
500, // delay before a new execution is launched
2 // maximum number of executions
))
.build();
在这种情况下,如果协调员响应速度较慢,则在500毫秒后,驾驶员会选择另一个协调员并提交第二个任务,而第一个协调员会获胜。
请注意,这可能会导致整体上发送到集群的请求的放大,因此您希望以这种方式调整延迟,使其仅在响应时间高度异常时才开始。在您的情况下,如果请求通常花费不到10毫秒,则500毫秒可能是一个合理的数字,具体取决于您的较高百分位数等待时间是什么样。
所有这些,如果您能够确定问题出在新节点上,则其协调员的表现不佳。值得理解为什么。添加推测执行可能是解决该问题的一种好方法,但是尝试了解为什么新节点执行如此缓慢的原因可能更好。进行适当的监视以观察Cassandra的指标可能会更清楚地了解问题。