godis icon indicating copy to clipboard operation
godis copied to clipboard

support eviction

Open suger-no opened this issue 2 years ago • 6 comments

support lfu and lru

suger-no avatar May 17 '23 02:05 suger-no

Eviction 这边有几个问题:

  1. runtime.ReadMemStats 需要 StopTheWorld, 成本极高
  2. 对象可能已经不再使用但仍未 GC
  3. 不确定需要逐出多少对象

我后来考虑了一下可以这样解决:

  1. 首先使用 gopsutil 从操作系统中获取当前进程内存占用。它的成本很低不会影响正常运行。
  2. 若 gopsutil 发现内存超限手动调用 runtime.GC 优先进行垃圾回收
  3. 垃圾回收后使用 runtime.ReadMemStats 再次检查内存,若仍超限再进行逐出
  4. 设置一个逐出的 batch size,再逐出一批对象后再次调用 runtime.GC 和 runtime.ReadMemStats 直到内存符合要求:

伪代码

func eviction() {
  if gopsutil.RSS < maxMemory {
    return
  }
  for i:=0; i < loopLimit; i++ { // go 的 gc 不一定立即完成,还是设个上限
    runtime.GC()
    if runtime.ReadMemStats < maxMemory {
      return
    }
    const batchSize = 1024
    evict(batchSize)
  }
}

另外 :

  • EvictionPolicy 的几个 public 函数和 db.Eviction() 是没有注释的,麻烦按照 go lint 的要求加一下注释.
  • GetMaxMemoryState 直接返回 memToFree 就好,不用传指针进去
  • db.Eviction() 在主协程调用太消耗性能了,开一个 cron 吧

HDT3213 avatar May 20 '23 08:05 HDT3213

image

image 这个属性在gc后,并没有减少

suger-no avatar May 21 '23 02:05 suger-no

image

image 这个属性在gc后,并没有减少

info中

Eviction 这边有几个问题:

  1. runtime.ReadMemStats 需要 StopTheWorld, 成本极高
  2. 对象可能已经不再使用但仍未 GC
  3. 不确定需要逐出多少对象

我后来考虑了一下可以这样解决:

  1. 首先使用 gopsutil 从操作系统中获取当前进程内存占用。它的成本很低不会影响正常运行。
  2. 若 gopsutil 发现内存超限手动调用 runtime.GC 优先进行垃圾回收
  3. 垃圾回收后使用 runtime.ReadMemStats 再次检查内存,若仍超限再进行逐出
  4. 设置一个逐出的 batch size,再逐出一批对象后再次调用 runtime.GC 和 runtime.ReadMemStats 直到内存符合要求:

伪代码

func eviction() {
  if gopsutil.RSS < maxMemory {
    return
  }
  for i:=0; i < loopLimit; i++ { // go 的 gc 不一定立即完成,还是设个上限
    runtime.GC()
    if runtime.ReadMemStats < maxMemory {
      return
    }
    const batchSize = 1024
    evict(batchSize)
  }
}

另外 :

  • EvictionPolicy 的几个 public 函数和 db.Eviction() 是没有注释的,麻烦按照 go lint 的要求加一下注释.
  • GetMaxMemoryState 直接返回 memToFree 就好,不用传指针进去
  • db.Eviction() 在主协程调用太消耗性能了,开一个 cron 吧

info中,有数据的属性只用rss和vms似乎都不符合

suger-no avatar May 21 '23 02:05 suger-no

RSS 是物理内存大小, VMS 是虚拟内存空间大小,这里当然使用 RSS.

GC 后内存读数未减少的原因很复杂,比如 runtime.GC 不一定立即完成清理、回收的内存不一定立即还给操作系统。 runtime.GC() 后再次检查内存使用 runtime.ReadMemStat 而不用系统的 RSS 就是为了拿到尽可能实时的内存占用量。

我做了一些实验,总的来说由于 GC 和操作系统内存管理的各种机制存在很难拿到准确的内存使用量, eviction 只能尽力而为吧

HDT3213 avatar May 21 '23 07:05 HDT3213

RSS 是物理内存大小, VMS 是虚拟内存空间大小,这里当然使用 RSS.

GC 后内存读数未减少的原因很复杂,比如 runtime.GC 不一定立即完成清理、回收的内存不一定立即还给操作系统。 runtime.GC() 后再次检查内存使用 runtime.ReadMemStat 而不用系统的 RSS 就是为了拿到尽可能实时的内存占用量。

我做了一些实验,总的来说由于 GC 和操作系统内存管理的各种机制存在很难拿到准确的内存使用量, eviction 只能尽力而为吧

如果这样的话,很容易下一次检查的时候发现内存超出限制,但是实际上已经腾出了内存,只能延长时间间隔,难搞 ~J$B$M}$DZ1)K9RYHS3NWLS

改用定时任务的话,就需要遍历 所有的(16个)db实例,也会消耗性能吧, image

suger-no avatar May 24 '23 08:05 suger-no

如果这样的话,很容易下一次检查的时候发现内存超出限制 这事我之前还没考虑过,只能每次 eviction 之后一段时间内暂停 OOM 检测。

放到定时任务里是为了保证线上快速响应命令,每次执行命令都要查一下RSS会导致命令执行非常非常慢,定时任务不会影响命令执行速度。

HDT3213 avatar May 28 '23 12:05 HDT3213