通过对比来更深入理解golang和redis的hashmap的实现。并通过相应实现来理解hashmap。 能否帮我(从 数据结构、哈希方式、哈希冲突解决、rehash、)对比一下 golang 的 map 和 redis 的 HashMap 的区别?关键字:双 table、渐进式 rehash、扩容条件、缩容条件、bgsave、COW 机制?
table:
- name: 数据结构
相似: 内部两个哈希表,用于扩容,但Go中叫做buckets和oldbuckets,Redis中是一个数组,大小为2
golang: |
- [层次不同] 两层结构(Go实际只有三层。这导致两种实现后续功能的差异,如:是否支持缩容。)
- [size、used存储方式] Go在顶层结构中存储了B字段,表示有2^B个bucket,并存储了count字段表示已有数据个数
- [k-v排列方式] 8*key+8*val作为一个bucket,后面可以链式挂接更多overflow的bucket
redis: |
- [层次不同] 三层结构(Redis第二层存储了子表的信息,第三层作为子表,存储的是实际数据的地址)
- [size、used存储方式] Redis在第二层(每个字表信息)中
- [k-v排列方式] dictEntity 链式(key+val作为一个dictEntity,后面可以链式挂接更多dictEntity)
- name: 哈希方式
相似: 根据不同类型,调用不同的hash方法后,求余得到索引
golang: golang 无法直接获取,还需要再根据tophash->key的查找
redis: 得到bucket索引后即得到bucket数据
- name: 哈希冲突
相似: 开放寻址法
golang: golang由8个k-v组成一个bucket,然后再挂接overflow bucket
redis:
- name: rehash
相似: 都有负载因子 + 渐进扩容
golang: |
- [触发扩容的时机] golang在插入新key时检测装载因子和拉链长度
- [触发扩容的条件] bucket内(可以存储8个k-v)的平均个数超过6.5或单个bucket的overflow超过bucket数会进行扩容
redis: |
- [触发扩容的时机] Redis在增删查改时都会检查是否需要rehash
- [触发扩容的条件] 每个key平均存储了一个数据,则进行扩容;每个key平均存储了不到0.1个数据,则进行缩容
- name: 扩容机制
相似: 都是先分配空间,再渐进迁移
golang: golang只在增删时迁移
redis: redis在所有操作时都会渐进式迁移
- name: 缩容机制
相似: ---
golang: false(因为golang只保存了当前的bucket size,新bucket一定是旧bucket大小的两倍,不支持缩容)
redis: true(因为redis两个子表记录了自己的大小,缩容即扩容的逆过程)