Maison  >  Article  >  développement back-end  >  Vous avez peut-être entendu parler de faire du GC sur la carte Golang ?

Vous avez peut-être entendu parler de faire du GC sur la carte Golang ?

藏色散人
藏色散人avant
2021-08-13 14:26:462542parcourir

Golang, lors de la suppression de la paire clé-valeur, n'est pas réellement supprimée, mais marquée. Alors, comme il y a de plus en plus de paires clé-valeur, cela entraînera-t-il beaucoup de gaspillage de mémoire ? Tout d'abord, la réponse est oui, cela est très susceptible de provoquer un MOO, et il y a une discussion à ce sujet : github.com/golang/go/issues/20135. La signification générale est que dans une grande map, l'opération delete ne libère pas réellement la mémoire et peut provoquer un MOO mémoire.

L'approche générale est donc la suivante : map 中,delete 操作没有真正释放内存而可能导致内存 OOM。

所以一般的做法:就是 重建map。而 go-zero 中内置了 safemap 的容器组件。safemap 在一定程度上可以避免这种情况发生。

那首先我们看看 go 原生提供的 map 是怎么删除的?

原生map删除

1  package main
2
3  func main() {
4      m := make(map[int]string, 9)
5      m[1] = "hello"
6      m[2] = "world"
7      m[3] = "go"
8
9      v, ok := m[1]
10     _, _ = fn(v, ok)
11
12     delete(m, 1)
13  }
14
15 func fn(v string, ok bool) (string, bool) {
16     return v, ok
17 }

测试代码如上,我们可以通过 go tool compile -S -N -l testmap.go | grep "CALL"

0x0071 00113 (test/testmap.go:4)        CALL    runtime.makemap(SB)
0x0099 00153 (test/testmap.go:5)        CALL    runtime.mapassign_fast64(SB)
0x00ea 00234 (test/testmap.go:6)        CALL    runtime.mapassign_fast64(SB)
0x013b 00315 (test/testmap.go:7)        CALL    runtime.mapassign_fast64(SB)
0x0194 00404 (test/testmap.go:9)        CALL    runtime.mapaccess2_fast64(SB)
0x01f1 00497 (test/testmap.go:10)       CALL    "".fn(SB)
0x0214 00532 (test/testmap.go:12)       CALL    runtime.mapdelete_fast64(SB)
0x0230 00560 (test/testmap.go:7)        CALL    runtime.gcWriteBarrier(SB)
0x0241 00577 (test/testmap.go:6)        CALL    runtime.gcWriteBarrier(SB)
0x0252 00594 (test/testmap.go:5)        CALL    runtime.gcWriteBarrier(SB)
0x025c 00604 (test/testmap.go:3)        CALL    runtime.morestack_noctxt(SB)

执行第12行的 delete,实际执行的是 runtime.mapdelete_fast64

这些函数的参数类型是具体的 int64mapdelete_fast64 跟原始的 delete 操作一样的,所以我们来看看 mapdelete

mapdelete

长图预警!!!

Vous avez peut-être entendu parler de faire du GC sur la carte Golang ?

大致代码分析如上,具体代码就留给大家去阅读了。其实大致过程:

  1. 写保护,防止并发写
  2. 查询要删除的 key 是否存在
  3. 存在则对其标志做删除标记
  4. count--

所以你在大面积删除 key ,实际 map 存储的 key 是不会删除的,只是标记当前的key状态为 empty

其实出发点,和 mysql 的标记删除类似,防止后续会有相同的 key 插入,省去了扩缩容的操作。

但是这个对有些场景是不妥的,如果开发者在未来时间内都不会再插入相同的 key ,很可能会导致 OOM

所以针对以上情况,go-zero 开发了 safemap 。下面我们看看 safemap 是如何避免这个问题的?

safemap

直接从操作 safemap 中分析为什么要这么设计:

Vous avez peut-être entendu parler de faire du GC sur la carte Golang ?

  1. 预设一个 删除阈值,如果触发会放到一个新预设好的 newmap
  2. 两个 map 是一个整体,所以 key 只能留一份

所以为什么要设置两个 map 就很清楚了:

  1. dirtyOld 作为存储主体,如果 delete 操作达到阈值,则会触发迁移。
  2. dirtyNew 作为暂存体,会在到达阈值时,存放部分 key/value

所以在迁移操作时,我们需要做的就是:将原先的 dirtyOld 清空,存储的 key/value 通过 for-range 重新存储到 dirtyNew,然后将 dirtyNew 指向 dirtyOld

可能会有疑问:不是说 key/value 没有删除吗,只是标记了 tophash=empty

其实在 for-range 过程中,会过滤掉 tophash 的 key

这样就实现了不需要的 key 不会被加入到 dirtyNew,进而不会影响 dirtyOldreconstruire la carte

. Le composant conteneur de safemap est intégré à go-zero. safemap peut empêcher cela dans une certaine mesure.

Vous avez peut-être entendu parler de faire du GC sur la carte Golang ?Alors voyons d'abord comment supprimer la map fournie nativement par go ?

Suppression de la carte native

rrreee

Le code de test est comme ci-dessus, nous pouvons passer go tool compile -S -N -l testmap.go | grep "CALL" : rrreeeExecute line 12 delete, l'exécution réelle est runtime.mapdelete_fast64.

Les types de paramètres de ces fonctions sont spécifiques à int64, mapdelete_fast64 est le même que l'opération delete d'origine, alors jetons un coup d'œil mapdelete.

mapdeleteAttention longue photo ! ! !

🎜🎜Vous avez peut-être entendu parler de faire du GC sur la carte Golang ? 🎜🎜 L'analyse générale du code est la même que ci-dessus, et le code spécifique est laissé à la lecture de tous. En fait, le processus général est le suivant : 🎜
  1. Protection en écriture pour empêcher l'écriture simultanée
  2. Requête si la clé à supprimer existe
  3. S'il existe, marquez-le. Faites des marques de suppression
  4. compte--
🎜Vous supprimez donc la clé dans une grande zone , la carte réelle. La clé stockée ne sera pas supprimée, mais l'état actuel de la clé sera marqué comme vide. 🎜🎜En fait, le point de départ est similaire à la suppression de balise de mysql, qui empêche la même clé d'être insérée ultérieurement, éliminant ainsi le besoin d'opérations d'expansion et de contraction. 🎜🎜Mais cela est inapproprié pour certains scénarios. Si le développeur n'insère pas à nouveau la même clé à l'avenir, cela provoquera probablement un MOO. 🎜🎜Donc, en réponse à la situation ci-dessus, go-zero a développé safemap. Voyons comment safemap évite ce problème ? 🎜🎜🎜🎜safemap🎜🎜 Analysez pourquoi il est conçu ainsi directement depuis l'opération safemap : 🎜🎜Vous avez peut-être entendu parler de faire du GC sur la carte Golang ?🎜
  1. Préréglez un 🎜seuil de suppression🎜, s'il est déclenché, il sera placé dans un nouveau préréglage newmap
  2. Les deux map forment un tout, donc une seule copie de la key peut être laissée
🎜Il est donc clair pourquoi deux map sont configurées : 🎜
  1. dirtyOld comme sujet de stockage, si le delete l’opération atteint le seuil, la migration sera déclenchée.
  2. dirtyNew En tant que stockage temporaire, lorsque le seuil est atteint, une partie de la clé/valeur
sera stockée 🎜Effacer le dirtyOld d'origine, stocker à nouveau la clé/valeur stockée dans dirtyNew via for-range, puis. remplacez les points dirtyNew par dirtyOld🎜. 🎜
🎜Vous avez peut-être des questions : Cela ne voulait-il pas dire que key/value n'a pas été supprimé ? Il a simplement été marqué comme tophash=empty🎜🎜 ? 🎜en fait dans Pendant le processus for-range, les clés de tophash seront filtrées🎜
🎜De cette façon, les clés inutiles ne seront pas ajouté à dirtyNew , ce qui n'affectera pas dirtyOld. 🎜🎜🎜🎜🎜C'est en fait le concept de l'ancienne génération et de la nouvelle génération de collecte des déchets. 🎜🎜Pour plus de détails sur l'implémentation, vous pouvez consulter le code source ! 🎜🎜🎜🎜Adresse du projet🎜🎜github.com/tal-tech/go-zero🎜🎜Bienvenue pour utiliser go-zero et 🎜star🎜 soutenez-nous ! 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer