Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support eviction #176

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

support eviction #176

wants to merge 1 commit into from

Conversation

suger-no
Copy link
Contributor

support lfu and lru

db.updateEvictionMark(read)
defer func() {
db.RWUnLocks(write, read)
db.Eviction()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eviction 只用定时任务调用就行了,放在主流程里太拖性能了

defer mem.Lock.Unlock()
var memFreed uint64 = 0
var memToFree uint64
mem.GetMaxMemoryState(memToFree)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

直接让 GetMaxMemoryState 返回 memToFree 就好了, 不需要模仿 C/C++ 这种传指针进去赋值的风格,

@@ -294,3 +307,88 @@ func (db *DB) ForEach(cb func(key string, data *database.DataEntity, expiration
return cb(key, entity, expiration)
})
}

//eviction
func (db *DB) Eviction() {
Copy link
Owner

@HDT3213 HDT3213 May 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
  }
}

@HDT3213
Copy link
Owner

HDT3213 commented May 20, 2023

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 吧

@suger-no
Copy link
Contributor Author

image

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

@suger-no
Copy link
Contributor Author

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似乎都不符合

@HDT3213
Copy link
Owner

HDT3213 commented May 21, 2023

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

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

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

@suger-no
Copy link
Contributor Author

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

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

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

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

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

@HDT3213
Copy link
Owner

HDT3213 commented May 28, 2023

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants