Golang’s sync.Map internals
Table of Contents
Great article on the topic — https://victoriametrics.com/blog/go-sync-map/index.html.
The whole series of articles is solid, so please take a look if you are interested.
Let me share the most important insights.
How it works
Internal structure is:
1 2 3 4 5 6 |
type Map struct { mu Mutex read atomic.Pointer[readOnly] dirty map[any]*entry misses int } |
So it uses 2 maps under the hood — a fast read-only map, and a «dirty» map, guarded by a mutex, where all writes happen.
Both of them store pointers to the same value, so we don’t store the same data twice.
When we write data, a goroutine acquires the lock, and then writes to the dirty map.
When a read occurs, and a value is found in the fast map, we read it lock-free («happy path»). If not, we acquire the lock, and search through the dirty map as well.
When we delete data, we set a value in «read» map to nil. It means data needs to be cleaned from the «dirty» map later. When it’s cleaned from the «dirty» as well, it is set to a special value.
When we fail to find a key in the «read» map, we increment the «misses» counter. When it reaches some threshold, the «dirty» map is promoted to the «read» one.
Conclusion
Sync.Map is not a silver bullet. It was implemented to optimize the read-intensive cases.
If you write too often or even reading keys that do not exist, it could act slower than a simple map+mutex combination.
Similar Posts
LEAVE A COMMENT
Для отправки комментария вам необходимо авторизоваться.