Go 语言 缓存穿透 雪崩与击穿的解决方案

Go阿木 发布于 2025-06-22 5 次阅读


Go 语言缓存穿透、雪崩与击穿解决方案

在分布式系统中,缓存是提高系统性能、减轻数据库压力的重要手段。缓存的使用也带来了一系列问题,如缓存穿透、缓存雪崩和缓存击穿。本文将围绕这些问题,探讨Go语言中的解决方案。

缓存穿透

什么是缓存穿透

缓存穿透是指查询一个根本不存在的数据,导致请求直接落到数据库上,从而造成数据库压力增大,甚至崩溃。

缓存穿透的解决方案

1. 布隆过滤器:在查询数据之前,先通过布隆过滤器判断数据是否可能存在于数据库中。如果不存在,则直接返回,避免查询数据库。

go

package main

import (


"github.com/bsm/bloom"


)

func main() {


// 创建布隆过滤器


b := bloom.New(1024, 0.01)

// 添加数据


b.Add("user1")


b.Add("user2")

// 检查数据是否存在


if b.Test("user1") {


// 存在


} else {


// 不存在


}


}


2. 缓存空值:对于查询不存在的数据,可以将空值缓存起来,避免重复查询数据库。

go

package main

import (


"github.com/patrickmn/go-cache"


)

func main() {


// 创建缓存


c := cache.New(5time.Minute, 10time.Minute)

// 查询数据


if _, found := c.Get("user1"); !found {


// 缓存中不存在,查询数据库


// ...


// 将空值缓存


c.Set("user1", "", 5time.Minute)


}


}


缓存雪崩

什么是缓存雪崩

缓存雪崩是指缓存中大量数据同时过期,导致请求直接落到数据库上,造成数据库压力剧增。

缓存雪崩的解决方案

1. 设置不同的过期时间:为缓存数据设置不同的过期时间,避免大量数据同时过期。

go

package main

import (


"github.com/patrickmn/go-cache"


)

func main() {


// 创建缓存


c := cache.New(5time.Minute, 10time.Minute)

// 设置不同过期时间的缓存


c.Set("user1", "value1", 1time.Minute)


c.Set("user2", "value2", 2time.Minute)


c.Set("user3", "value3", 3time.Minute)


}


2. 使用持久化存储:将缓存数据持久化存储到数据库或文件中,当缓存失效时,可以从持久化存储中读取数据。

go

package main

import (


"github.com/patrickmn/go-cache"


"gorm.io/driver/sqlite"


"gorm.io/gorm"


)

func main() {


// 创建数据库连接


db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})


if err != nil {


panic("failed to connect database")


}

// 创建缓存


c := cache.New(5time.Minute, 10time.Minute)

// 查询数据


if _, found := c.Get("user1"); !found {


// 缓存中不存在,查询数据库


var user User


db.First(&user, "name = ?", "user1")


if user.ID != 0 {


// 将数据缓存


c.Set("user1", user, 5time.Minute)


}


}


}


缓存击穿

什么是缓存击穿

缓存击穿是指热点数据在缓存中过期,第一个访问者会从数据库中读取数据,并将数据写入缓存,后续访问者可以直接从缓存中读取数据。

缓存击穿的解决方案

1. 设置热点数据永不过期:对于热点数据,可以设置永不过期,避免缓存击穿。

go

package main

import (


"github.com/patrickmn/go-cache"


)

func main() {


// 创建缓存


c := cache.New(5time.Minute, 10time.Minute)

// 设置热点数据永不过期


c.Set("hot_data", "value", cache.NoExpiration)


}


2. 使用互斥锁:在读取热点数据时,使用互斥锁保证同一时间只有一个请求从数据库中读取数据。

go

package main

import (


"github.com/patrickmn/go-cache"


"sync"


)

var lock sync.Mutex

func main() {


// 创建缓存


c := cache.New(5time.Minute, 10time.Minute)

// 查询热点数据


if _, found := c.Get("hot_data"); !found {


lock.Lock()


defer lock.Unlock()

// 再次检查缓存


if _, found := c.Get("hot_data"); !found {


// 从数据库读取数据


var data string


// ...


// 将数据缓存


c.Set("hot_data", data, cache.NoExpiration)


}


}


}


总结

本文介绍了Go语言中缓存穿透、缓存雪崩和缓存击穿的问题,并提供了相应的解决方案。在实际应用中,可以根据具体场景选择合适的方案,以提高系统性能和稳定性。