Проверяет понимание проблемы cache stampede и способов её предотвращения в конкурентных системах.
Cache stampede — это ситуация, когда при истечении срока действия кэша или его отсутствии множество одновременных запросов обращаются к базе данных или другому медленному источнику. Это приводит к избыточной нагрузке и может вызвать деградацию производительности или даже отказ системы.
В Go для решения этой проблемы часто используется паттерн singleflight. Он гарантирует, что при одновременных запросах на одни и те же данные только один запрос выполнит обращение к источнику, а остальные будут ждать его результат. Это достигается за счёт группировки запросов по ключу.
import "golang.org/x/sync/singleflight"
var group singleflight.Group
func getData(key string) (string, error) {
v, err, _ := group.Do(key, func() (interface{}, error) {
// Здесь обращение к БД или другому источнику
return fetchFromDB(key)
})
if err != nil {
return "", err
}
return v.(string), nil
}В этом примере все вызовы getData с одинаковым ключом будут объединены, и только один из них выполнит fetchFromDB. Остальные получат тот же результат после завершения первого.
Другой способ — использовать мьютекс или канал для сериализации доступа к источнику. Однако это менее эффективно, так как блокирует все запросы, даже если данные уже загружены в кэш.
var mu sync.Mutex
var cache = make(map[string]string)
func getData(key string) string {
mu.Lock()
defer mu.Unlock()
if val, ok := cache[key]; ok {
return val
}
val := fetchFromDB(key)
cache[key] = val
return val
}Здесь мьютекс защищает кэш, но все запросы будут ждать, пока первый загрузит данные. Это проще, но менее масштабируемо.
Для высоконагруженных систем рекомендуется использовать singleflight, так как он минимизирует нагрузку на источник данных и эффективно обрабатывает конкурентные запросы. Блокировки подходят для простых случаев с низкой конкуренцией.