sync.map原理解析
Posted hello_读书就是赚钱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sync.map原理解析相关的知识,希望对你有一定的参考价值。
最新一个高并发项目中需要大量使用sync.map,为了更好的评估sync.map对的cpu和内存影响,深入探究一下sync.map的原理,最好总结,为后面的选型做好准备.
1、sync.map的数据结构
相比与Java中的CurrentHashMap来说sync.map的实现简单了许多.一句话总结sync.map使用了写时复制的技术实现了高并发的map.
我们先来看看sync.map的数据结构类图.这里就不贴代码,最好结合代码再来看这张图.
从在这里,我们可以看到sync.map封装了真正value的指针.当key存在的时候,那么可以直接通过value的unsafe指针做原子替换,做到无锁替换值的效果
.当key不存在的时候,就相对比较复杂.我们再来看看Stroe函数的流程.
2、Store函数
- 从图中我们可以看到蓝色的路径是无锁路径,
即当key在写表与读表同时存在时,那么直接拿到entry然后替换真正value的指针即可
当key的值在读表中不存在、或者key的值只在读表中存在(即entry的指针指向expunge)时.那么就会开始加锁,并把记录往写表的里面写.
- 这里用到我们熟悉的双重判断
如果在最开始的时候写入一大波不同key,那么会一直加锁,但只要key被插入过一次,且没被删除,那么就一直不用加锁.
这个跟java有一定的区别通过expunge不用加锁就可以知道写表中是否存在这个key
,这个点记住,因为后面的函数会多次用这个字段来判断key在写表中是否存在- 可以理解读表是作为一层缓存的存在,写的时候加锁写真正的map.读的时候从缓存读,缓存没有再击穿进去读.
如果一直在写新的key,但一直在读sync.map中不存在的key,也会导致一直加锁
3、Delete函数
删除的关键动作就是找到key,并把entry中指向的值置为nil
,所以这里也存在找key的动作在找key的时候,如果key不存在,那么一定要加锁去写表中找
- 这里有两个关键信息,就是
读表删除的时候,是将entry中的value指向nil.到写表中删除的时候,是直接删除这个key
4、Load函数
- 读出的流程跟删除的流程差不多,这里就不再累述
5、总结
- go的sync.map的实现主要是用了读写两张表的方式实现的,在实现上面比java的简单很多.对于那种不是很频繁插入key的场景比较合适.不像java是使用分桶key实现
- 在这个意义上,可以理解读表是写表的一个缓存,当读取的次数达到len(写表)的数量时,那么就刷一次缓存到读表.当写表为空的时候,需要复制一份写表的数据出来.如果此时数据比较多的话,那么也会消费比较多的时间片.
以上是关于sync.map原理解析的主要内容,如果未能解决你的问题,请参考以下文章