判断Go语言handleCity函数是否线程安全及原因分析
问题:handleCity函数是否线程安全?
代码实现
通用缓存CacheMap
type CacheMap[K comparable, V any] struct { sync.Map } func (cache *CacheMap[K, V]) Set(key K, value V) { cache.Store(key, value) } func (cache *CacheMap[K, V]) Get(key K) (V, bool) { var value V data, ok := cache.Load(key) if !ok { return value, false } return data.(V), true }
全局缓存与锁
var CitiesCache CacheMap[string, int64] var CityLock sync.Mutex
handleCity函数
func handleCity(cityName string) (pgtype.Int8, error) { if cityName == "" { return pgtype.Int8{}, nil } cityId, exists := CitiesCache.Get(cityName) if exists { return pgtype.Int8{Int64: cityId, Valid: true}, nil } filters := url.Values{ "name": []string{cityName}, "__limit": []string{"1"}, } CityLock.Lock() defer CityLock.Unlock() dbResponse, err := dbutils.ReadCities(context.Background(), DB_POOL, filters) if err != nil { return pgtype.Int8{}, fmt.Errorf(`error while reading cities: %v`, err) } if len(dbResponse.Cities) == 0 { newCity := models.City{ Name: pgtype.Text{String: cityName, Valid: true}, } cityId, err = dbutils.InsertCity(context.Background(), DB_POOL, &newCity) if err != nil { return pgtype.Int8{}, fmt.Errorf(`error while inserting city: %v`, err) } } else { cityId = dbResponse.Cities[0].Id.Int64 } CitiesCache.Set(cityName, cityId) return pgtype.Int8{Int64: cityId, Valid: true}, nil }
分析结论
这个handleCity函数是线程安全的,但存在可优化的冗余操作。
线程安全的依据
- 缓存操作安全:
CitiesCache基于sync.Map实现,其Get(底层调用sync.Map.Load)和Set(底层调用sync.Map.Store)本身就是线程安全的,不会出现数据竞争问题。 - 数据库操作互斥:
CityLock作为全局互斥锁,确保同一时间只有一个goroutine能执行锁内的数据库查询与插入逻辑:- 多个goroutine发现缓存缺失后,会排队等待获取锁;
- 第一个拿到锁的goroutine完成数据库操作(查询并按需插入)后,将城市ID存入缓存;
- 后续goroutine拿到锁后,查询数据库会发现城市已存在,直接获取现有ID并更新缓存(重复更新不破坏数据一致性)。
可优化点(性能层面)
当前实现缺少双重检查锁定的关键步骤:在获取CityLock后没有再次检查缓存。这会导致多个goroutine在缓存缺失场景下,即使第一个goroutine已经将ID存入缓存,后续goroutine仍会执行冗余的数据库查询。
优化后的锁内逻辑应添加缓存二次检查:
CityLock.Lock() defer CityLock.Unlock() // 双重检查:确认缓存是否已被其他goroutine更新 if cityId, exists := CitiesCache.Get(cityName); exists { return pgtype.Int8{Int64: cityId, Valid: true}, nil } // 原数据库查询与插入逻辑...
内容的提问来源于stack exchange,提问作者Tushar
相关产品推荐
相关产品推荐

