现象
我们网关的metrics数据的存储用的是时序数据库influxdb,每天500亿的调用量,30秒一个点,有很多维度的监控,应用,接口,机器,域名维度,机房维度,响应码,等等指标的统计,用了2台influxdb,分别负责不同的mertics存储。
经常出现写influxdb慢,导致监控数据延迟,影响监控和报警,我们是怀疑influxdb写wal日志性能问题引起的,现在chatgpt这么活,也是先和chatgpt 请教了一番,说怎么优化,chatgpt 提到了influxdb 和wal
相关的一个优化参数:wal-fsync-delay
, 官方的解释是:
# The amount of time that a write will wait before fsyncing. A duration
# greater than 0 can be used to batch up multiple fsync calls. This is useful for slower
# disks or when WAL write contention is seen. A value of 0s fsyncs every write to the WAL.
# Values in the range of 0-100ms are recommended for non-SSD disks.
wal-fsync-delay = "10ms"
官方建议0-100ms,这个事我们后来改为10ms,
chatgtp的解析是:
wal-fsync-delay是InfluxDB中的一个参数,用于控制WAL文件的持久化策略。
WAL是InfluxDB中的Write-Ahead-Log,即先写入WAL文件,再写入内存。这种方式可以提高写入性能,但也存在一定的风险,因为WAL文件在发生故障时可能会导致数据的丢失。因此,InfluxDB提供了wal-fsync-delay参数来控制WAL文件的持久化策略,从而在性能和数据安全之间做出平衡。
具体来说,wal-fsync-delay参数控制了WAL文件的持久化延迟时间,即多久将WAL文件中的数据写入磁盘。默认情况下,wal-fsync-delay参数设置为0,表示每次写入WAL文件后立即将数据写入磁盘。这种方式可以保证数据的安全性,但也会对写入性能造成影响。
如果将wal-fsync-delay参数设置为正整数,则表示延迟一定的时间后才将WAL文件中的数据写入磁盘。例如,将wal-fsync-delay设置为100ms,则表示每次写入WAL文件后,等待100ms再将数据写入磁盘。这种方式可以提高写入性能,但也会增加数据丢失的风险。
就是必须大于0,write 操作等待这个delay的时间,因为这个配置项是fsync delay,我们知道wal日志刷盘必须要做系统调用fsync操作,才能保障日志到真正持久化到磁盘,否则只是在os的缓冲区,机器挂了还是会丢数据,所以当时我们理解是越延迟fsync,性能就越高,因为默认是写一次就刷一次,这样会影响性能。
而且这个配置默认是100ms,按照chatgpt的理解,所以我们调大到1s,也就是想1s刷一次盘,但是改完后,写入操作反而越来越慢,监控延迟越来越大。没办法,只能改回去
后来看了influxdb的代码:
func (l *WAL) scheduleSync() {
// If we're not the first to sync, then another goroutine is fsyncing the wal for us.
if !atomic.CompareAndSwapUint64(&l.syncCount, 0, 1) {
return
}
// Fsync the wal and notify all pending waiters
go func() {
var timerCh <-chan time.Time
// time.NewTicker requires a > 0 delay, since 0 indicates no delay, use a closed
// channel which will always be ready to read from.
if l.syncDelay == 0 {
// Create a RW chan and close it
timerChrw := make(chan time.Time)
close(timerChrw)
// Convert it to a read-only
timerCh = timerChrw
} else {
t := time.NewTicker(l.syncDelay)
defer t.Stop()
timerCh = t.C
}
for {
select {
case <-timerCh:
l.mu.Lock()
if len(l.syncWaiters) == 0 {
atomic.StoreUint64(&l.syncCount, 0)
l.mu.Unlock()
return
}
l.sync()
l.mu.Unlock()
case <-l.closing:
atomic.StoreUint64(&l.syncCount, 0)
return
}
}
}()
}
每个写请求都会调用scheduleSync() 函数
经过分析代码,发现这个参数如果配置100ms,那每个请求都会等待100ms,等100ms内的写请求一起刷盘,最后返回,不知道influxdb为啥这样设计,每个写请求还要wait,不是我们认为的写可以立即返回,只是刷盘延迟100ms
分析出了这个问题,我们把wal-fsync-delay 参数调整为10ms,果然监控监控就不在延迟了,而且实时性也提高了,之前不出问题,正常延迟在2分钟,现在延迟只有30秒了,明显提高了写性能。
另外,我们也想直接关闭influxdb的wal日志,因为我们事监控数据,挂了丢了数据也能接受,只有写得快,influxdb 需要更高的版本才能支持关闭,我们的版本还不支持。
另外聪明的同学也会问,为啥不用vm存储引擎,不错,vm存储引擎就事不写wal日志的,所以事mertics的最合适的存储引擎了,我们的rpc 服务的监控目前就事用的这个,非常稳定,我们只是目前没有时间来迁移,后面是要迁移到vm的。
所以现在chatgpt 是很火,但他的答案我们还是要辩证分析,才能用之。