Что такое `sync.Map`? В каких случаях её следует использовать, а когда лучше выбрать `map` с `Mutex`?

Ответ

sync.Map — это встроенная в Go потокобезопасная структура данных (map), оптимизированная для очень специфичных сценариев конкурентного доступа.

Внутреннее устройство (упрощенно):

sync.Map работает без глобального мьютекса для операций чтения. Она использует две внутренние карты:

  • read: Атомарный указатель на read-only карту, доступ к которой для чтения не требует блокировок.
  • dirty: Обычная map с мьютексом для новых записей и обновлений. Периодически dirty карта "продвигается" до read.

Когда стоит использовать sync.Map:

  1. Кэши "write-once, read-many": Когда ключ записывается один раз, а затем многократно читается множеством горутин. Например, кэш редко изменяемых данных.
  2. Дисперсный доступ: Когда разные горутины работают с разными наборами ключей, минимизируя конфликты при записи.

Когда sync.Map — не лучший выбор (и лучше map + sync.RWMutex):

  1. Частые записи и обновления: Если горутины часто перезаписывают значения по одним и тем же ключам, производительность sync.Map может быть ниже из-за накладных расходов на управление dirty картой.
  2. Нужна строгая типизация: sync.Map хранит ключи и значения как interface{}, что требует приведения типов и лишает статической проверки. С появлением дженериков в Go 1.18 стало проще реализовать собственную типизированную конкурентную карту.
  3. Нужно знать размер (len): У sync.Map нет метода Len(), так как его вычисление было бы медленным и требовало бы блокировки.
  4. Статические данные: Если карта инициализируется один раз и больше никогда не изменяется, обычная map без всяких блокировок будет самым быстрым решением.

Пример использования:

var m sync.Map

// Запись (Store)
m.Store("user:1", "Alice")

// Чтение (Load)
val, ok := m.Load("user:1")
if ok {
    fmt.Println(val.(string)) // Требуется приведение типа
}

// Итерация (Range)
m.Range(func(key, value interface{}) bool {
    fmt.Printf("%s -> %sn", key, value)
    return true // для продолжения итерации
})