kl个人博客 首页>>异常排查记录>>etcdserver: mvcc: database space exceeded异常处理

etcdserver: mvcc: database space exceeded异常处理

etcdserver: mvcc: database space exceeded异常处理

异常描述

我们的binlog应用使用了etcd,用来协调主服务和存储数据源以及订阅相关的元数据信息。程序运行一段时间后,就会抛出mvcc: database space exceeded的异常,详细的堆栈如下:

Caused by: io.grpc.StatusRuntimeException: RESOURCE_EXHAUSTED: etcdserver: mvcc: database space exceeded
	at io.grpc.Status.asRuntimeException(Status.java:530)
	at io.grpc.stub.ClientCalls$UnaryStreamToFuture.onClose(ClientCalls.java:482)
	at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
	at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
	at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
	at io.etcd.jetcd.ClientConnectionManager$AuthTokenInterceptor$1$1.onClose(ClientConnectionManager.java:302)
	at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
	at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
	at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
	at io.grpc.internal.CensusStatsModule$StatsClientInterceptor$1$1.onClose(CensusStatsModule.java:694)
	at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
	at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
	at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
	at io.grpc.internal.CensusTracingModule$TracingClientInterceptor$1$1.onClose(CensusTracingModule.java:397)
	at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:459)
	at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:63)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:546)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$600(ClientCallImpl.java:467)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:584)
	at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
	at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
异常原因

经查,这个异常的message是etcd服务端返回的,用来提示应用etcd服务端空间不足了。在etcd的官方文档常见问题(FAQ)版块针对这个场景有明确的说明,如:

Q、:“ mvcc:database space exceeded”是什么意思,我该如何解决?

A、:etcd中的 多版本并发控制数据模型保留了密钥空间的确切历史记录。如果不定期压缩此历史记录(例如,通过设置--auto-compaction),etcd最终将耗尽其存储空间。如果etcd的存储空间不足,则会发出空间配额警报,以保护群集免于进一步写入。只要发出警报,etcd就会以error响应写请求mvcc: database space exceeded

要从空间不足配额警报中恢复:

  1. Compact etcd的历史。
  2. 每个etcd端点进行碎片整理
  3. 解除警报。

附FQA地址:https://etcd.io/docs/v3.4.0/faq/

解决问题

根据FQA所述,可以通过如下命令4个步骤解决问题:

# 1、获取当前的版本
$ rev=$(ETCDCTL_API=3 etcdctl --endpoints=:2379 endpoint status --write-out="json" | egrep -o '"revision":[0-9]*' | egrep -o '[0-9].*')
# 2、压缩当前版本之前的所有记录
$ ETCDCTL_API=3 etcdctl compact $rev
compacted revision 1516
# 3、清理多余的碎片空间
$ ETCDCTL_API=3 etcdctl defrag
Finished defragmenting etcd member[127.0.0.1:2379]
# 4、解除警告
$ ETCDCTL_API=3 etcdctl alarm disarm
memberID:13803658152347727308 alarm:NOSPACE

执行以上命令无误后,可以尝试写入数据,如果正常写入数据了,代表已成功释放空间了。最后一个解除警告的步骤不能漏,这就是个标记,和真正有无使用空间没有直接的逻辑关系。否则即使空间已释放了,也会提示空间不足。另除了手动压缩外,可以设置自动压缩,指令如下:

# 保留一个小时的历史记录
$ etcd --auto-compaction-retention=1

etcd不同的版本自动压缩的行为有细微差别,详情见:https://etcd.io/#history-compaction

碎片整理

压缩key空间后,会出现内部碎片,这些压缩出来的碎片空间可以被etcd使用,但是不会真正的释放物理空间,需要进行碎片整理,如:

$ etcdctl defrag
Finished defragmenting etcd member[127.0.0.1:2379]
以上指令只作用于当前所在的主机,不会在集群环境中复刻。可以使用--cluster标记指定所有成员以自动查找所有集群成员。如:
$ etcdctl defrag --cluster
Finished defragmenting etcd member[http://127.0.0.1:2379]
Finished defragmenting etcd member[http://127.0.0.1:22379]
Finished defragmenting etcd member[http://127.0.0.1:32379]
kl个人博客