Ответ
sync.Map
— это встроенный в Go тип данных, представляющий собой конкурентно-безопасную хэш-таблицу, оптимизированную для очень специфичных сценариев использования.
Как он устроен (упрощенно)?
sync.Map
внутренне состоит из двух хэш-таблиц:
read
— основная таблица, доступная для чтения без блокировок.dirty
— таблица для новых записей, доступ к которой требует блокировки.
- Чтение (Load): Сначала ключ ищется в
read
. Если найден — возвращается результат (быстро, без блокировки). Если нет — ставится блокировка и ключ ищется вdirty
. - Запись (Store): Всегда требует блокировки и происходит в
dirty
. - Периодически, если
dirty
содержит ключи, которых нет вread
,dirty
«продвигается» и становится новойread
-таблицей.
Эта архитектура объясняет, почему sync.Map
быстр на чтение, но медленнее на запись.
Когда использовать sync.Map
?
Его стоит применять только в двух случаях:
- Сценарий "write-once, read-many": Когда ключ записывается один раз, а затем многократно читается множеством горутин. Например, кэш редко изменяемых данных.
- Раздельные наборы ключей: Когда множество горутин работает с картой, но каждая горутина обращается к своему собственному, непересекающемуся с другими набору ключей. Это минимизирует конкуренцию за
dirty
.
Когда использовать map
+ sync.RWMutex
?
Это предпочтительный вариант в большинстве случаев:
- Когда количество чтений и записей примерно сопоставимо.
- Когда требуется строгая типизация (например,
map[string]int
), так какsync.Map
работает только сinterface{}
. - Когда карта инициализируется один раз, а затем передается в горутины только для чтения (становится неизменяемой).
- Когда производительность не является узким местом, а простота и читаемость кода важнее.
Пример использования sync.Map
:
var m sync.Map
// Запись значения
m.Store("user:1", "Alice")
// Чтение значения
value, ok := m.Load("user:1")
if ok {
fmt.Println(value.(string)) // Требуется приведение типа
}
// Удаление
m.Delete("user:1")
Вывод: В большинстве стандартных ситуаций map
с sync.RWMutex
является более простым, понятным и зачастую более производительным решением. sync.Map
— это узкоспециализированная оптимизация.