← Назад к вопросам

Что такое sync.Map?

1.7 Middle🔥 251 комментариев
#Конкурентность и горутины#Производительность и оптимизация

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое sync.Map?

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

Основные характеристики

1. Оптимизирована для конкретных сценариев:

  • Частые чтения, редкие записи (mostly read)
  • Много горутин, обращающихся к разным ключам
  • Редко перезаписываются существующие ключи

2. Как это работает внутри:

sync.Map использует два слоя хранилища:

  • Быстрый read-only слой (без lock'ов) для горячих данных
  • Мьютекс-защищённый слой для новых и изменённых данных

Это позволяет避免 lock contention для частых операций чтения.

API sync.Map

var m sync.Map

// Сохранение значения
m.Store(key, value)

// Получение значения
value, ok := m.Load(key)

// Сохраняет, если ключ отсутствует
actual, loaded := m.LoadOrStore(key, value)
// loaded = true если ключ уже существовал
// actual содержит реальное значение (либо новое, либо существующее)

// Удаление
m.Delete(key)

// Итерация по всем элементам
m.Range(func(key, value interface{}) bool {
    // обработка key, value
    return true // продолжить итерацию
})

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

var cache sync.Map

// Горутины часто читают из cache
go func() {
    for i := 0; i < 1000; i++ {
        val, ok := cache.Load("user_123")
        if ok {
            // используем val
        }
        time.Sleep(time.Millisecond)
    }
}()

// Редко добавляем или обновляем
cache.Store("user_123", userData)

Ограничения и недостатки

НЕ используй sync.Map если:

  1. Часто обновляешь значения — write-heavy workloads будут медленнее обычного map + sync.RWMutex

  2. Нужны atomic операции с несколькими ключами — sync.Map не поддерживает транзакции

  3. Нужно итерировать по всем элементам и иметь консистентный снимок — Range может видеть элементы, добавленные после начала итерации

  4. Хочешь получить размер — нет встроенного метода Len(), нужно лично итерировать

// Ограничение: невозможно реализовать атомарно с несколькими ключами
var m sync.Map
m.Store("a", 1)
m.Store("b", 2)
// Если между этими операциями произойдёт сбой, state может быть inconsistent

Сравнение с альтернативами

Обычный map + sync.Mutex:

var (
    mu sync.Mutex
    m = make(map[string]int)
)

mu.Lock()
val := m["key"]
mu.Unlock()

Прос: Полный контроль, atomic operations; Минус: Все операции заблокированы.

Обычный map + sync.RWMutex:

var (
    mu sync.RWMutex
    m = make(map[string]int)
)

mu.RLock()
val := m["key"]
mu.RUnlock()

Прос: Несколько readers одновременно; Минус: Все равно есть lock contention.

sync.Map: Прос: Оптимизирована для read-heavy workloads, минимум contention; Минус: Ограниченный API, непредсказуемое поведение при частых обновлениях.

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

Идеальный сценарий — кеширование данных в многопоточной среде:

var userCache sync.Map

func GetUser(id string) User {
    // Быстрая проверка в cache
    if cached, ok := userCache.Load(id); ok {
        return cached.(User)
    }
    
    // Если не в cache, загружаем из БД
    user := fetchFromDB(id)
    
    // Сохраняем в cache
    userCache.Store(id, user)
    
    return user
}

Вывод

sync.Map — это специализированный инструмент для конкретной ниши: read-heavy конкурентный доступ к map'у без транзакций между ключами. Понимание её ограничений критично, чтобы не использовать её не по назначению.