golang最终一致性分布式内存缓存插件库bcache的使用
Golang 最终一致性分布式内存缓存插件库 bcache 的使用
bcache 是一个 Go 库,用于在应用程序内创建分布式内存缓存。
特性
- 可配置最大键数的 LRU 缓存
- 节点之间最终一致性的同步
- 数据会复制到所有节点
- 缓存填充机制。当给定键的缓存不存在时,bcache 会协调缓存填充,确保只有一个调用会填充缓存,避免惊群效应或缓存雪崩
使用场景
- 当外部缓存如 redis 或 memcached 需要额外的网络跳转不可接受时
- 只需要简单的 Set、Get 和 Delete 操作的缓存
- 有足够的内存来保存缓存数据
工作原理
-
节点使用 Gossip 协议相互发现
只需要指定一个或几个节点作为引导节点,所有节点都会使用 gossip 协议相互发现
-
当有缓存 Set 和 Delete 操作时,事件会传播到所有节点
因此,所有节点最终都会有同步的数据
缓存填充
缓存填充机制由 GetWithFiller 函数提供。
当给定键的缓存不存在时:
- 它会调用提供的 Filler 函数
- 使用 Filler 返回的值设置缓存
即使有很多 goroutine 调用 GetWithFiller,给定的 Filler 函数也只会为每个键调用一次。这样可以避免缓存雪崩。
快速开始
服务器 1
bc, err := New(Config{
// PeerID: 1, // 留空,会根据 MAC 地址自动设置
ListenAddr: "192.168.0.1:12345",
Peers: nil, // 为 nil 因为我们将此节点用作引导节点
MaxKeys: 1000,
Logger: logrus.New(),
})
if err != nil {
log.Fatalf("failed to create cache: %v", err)
}
bc.Set("my_key", "my_val", 86400) // 设置缓存,过期时间86400秒
服务器 2
bc, err := New(Config{
// PeerID: 2, // 留空,会根据 MAC 地址自动设置
ListenAddr: "192.168.0.2:12345",
Peers: []string{"192.168.0.1:12345"}, // 连接到服务器1
MaxKeys: 1000,
Logger: logrus.New(),
})
if err != nil {
log.Fatalf("failed to create cache: %v", err)
}
bc.Set("my_key2", "my_val2", 86400)
服务器 3
bc, err := New(Config{
// PeerID: 3, // 会根据 MAC 地址自动设置
ListenAddr: "192.168.0.3:12345",
Peers: []string{"192.168.0.1:12345"}, // 连接到服务器1
MaxKeys: 1000,
Logger: logrus.New(),
})
if err != nil {
log.Fatalf("failed to create cache: %v", err)
}
val, exists := bc.Get("my_key2") // 获取服务器2设置的缓存
GetWithFiller 示例
c, err := New(Config{
PeerID: 3,
ListenAddr: "192.168.0.3:12345",
Peers: []string{"192.168.0.1:12345"},
MaxKeys: 1000,
})
if err != nil {
log.Fatalf("failed to create cache: %v", err)
}
// 使用填充函数获取缓存
val, exp, err := bc.GetWithFiller("my_key2", func(key string) (string, error) {
// 从数据库获取值
// ...
return value, nil
}, 86400) // 设置过期时间为86400秒
完整示例 Demo
package main
import (
"fmt"
"log"
"time"
"github.com/iwanbk/bcache"
"github.com/sirupsen/logrus"
)
func main() {
// 创建第一个节点作为引导节点
node1, err := bcache.New(bcache.Config{
ListenAddr: "127.0.0.1:8001",
Peers: nil, // 引导节点没有对等节点
MaxKeys: 1000,
Logger: logrus.New(),
})
if err != nil {
log.Fatal(err)
}
// 创建第二个节点连接到第一个节点
node2, err := bcache.New(bcache.Config{
ListenAddr: "127.0.0.1:8002",
Peers: []string{"127.0.0.1:8001"},
MaxKeys: 1000,
Logger: logrus.New(),
})
if err != nil {
log.Fatal(err)
}
// 设置缓存
node1.Set("key1", "value1", 60) // 60秒过期
node2.Set("key2", "value2", 60)
// 等待同步
time.Sleep(2 * time.Second)
// 获取缓存
if val, exists := node1.Get("key2"); exists {
fmt.Printf("Node1 got key2: %s\n", val)
}
if val, exists := node2.Get("key1"); exists {
fmt.Printf("Node2 got key1: %s\n", val)
}
// 使用填充函数
val, _, err := node1.GetWithFiller("key3", func(key string) (string, error) {
// 模拟从数据库获取值
return "value_from_db", nil
}, 60)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Filled value: %s\n", val)
// 关闭缓存
node1.Close()
node2.Close()
}
这个示例展示了如何创建两个节点,设置和获取缓存,以及使用填充函数。节点之间会自动同步数据,实现最终一致性。
更多关于golang最终一致性分布式内存缓存插件库bcache的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang最终一致性分布式内存缓存插件库bcache的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang bcache库使用指南
bcache是一个基于Golang实现的最终一致性分布式内存缓存插件库,它提供了简单易用的API来实现分布式缓存功能。下面我将详细介绍bcache的使用方法。
安装
go get github.com/allegro/bigcache/bcache
基本使用
1. 创建缓存实例
package main
import (
"fmt"
"time"
"github.com/allegro/bigcache/bcache"
)
func main() {
// 创建缓存配置
config := bcache.Config{
Shards: 1024, // 分片数量
LifeWindow: 10 * time.Minute, // 条目存活时间
CleanWindow: 5 * time.Minute, // 清理间隔
MaxEntriesInWindow: 1000 * 10 * 60, // 存活窗口内的最大条目数
MaxEntrySize: 500, // 单个条目最大大小(字节)
Verbose: true, // 是否打印详细日志
HardMaxCacheSize: 8192, // 最大缓存大小(MB)
}
// 创建缓存实例
cache, err := bcache.NewBigCache(config)
if err != nil {
panic(err)
}
// 使用缓存...
}
2. 基本操作
// 设置缓存
err := cache.Set("key1", []byte("value1"))
if err != nil {
fmt.Println("Set error:", err)
}
// 获取缓存
entry, err := cache.Get("key1")
if err != nil {
fmt.Println("Get error:", err)
} else {
fmt.Println("Got value:", string(entry))
}
// 删除缓存
err = cache.Delete("key1")
if err != nil {
fmt.Println("Delete error:", err)
}
// 检查缓存是否存在
err = cache.Append("key1", []byte("_append"))
if err == bcache.ErrEntryNotFound {
fmt.Println("Key does not exist")
}
3. 分布式特性使用
bcache本身是一个单机内存缓存,要实现分布式缓存,通常需要结合其他组件。以下是使用bcache作为本地缓存层,结合Redis实现分布式缓存的示例:
package main
import (
"fmt"
"time"
"github.com/allegro/bigcache/bcache"
"github.com/go-redis/redis/v8"
)
type DistributedCache struct {
localCache *bcache.BigCache
redisClient *redis.Client
}
func NewDistributedCache() (*DistributedCache, error) {
// 本地缓存配置
localConfig := bcache.Config{
Shards: 1024,
LifeWindow: 5 * time.Minute,
CleanWindow: 1 * time.Minute,
MaxEntriesInWindow: 1000 * 10 * 60,
MaxEntrySize: 500,
HardMaxCacheSize: 1024,
}
localCache, err := bcache.NewBigCache(localConfig)
if err != nil {
return nil, err
}
// Redis客户端
redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
return &DistributedCache{
localCache: localCache,
redisClient: redisClient,
}, nil
}
func (dc *DistributedCache) Get(key string) ([]byte, error) {
// 先查本地缓存
value, err := dc.localCache.Get(key)
if err == nil {
return value, nil
}
// 本地没有则查Redis
cmd := dc.redisClient.Get(context.Background(), key)
if cmd.Err() == redis.Nil {
return nil, bcache.ErrEntryNotFound
} else if cmd.Err() != nil {
return nil, cmd.Err()
}
// 将Redis结果写入本地缓存
value = []byte(cmd.Val())
_ = dc.localCache.Set(key, value)
return value, nil
}
func (dc *DistributedCache) Set(key string, value []byte) error {
// 先设置Redis
err := dc.redisClient.Set(context.Background(), key, value, 0).Err()
if err != nil {
return err
}
// 再设置本地缓存
return dc.localCache.Set(key, value)
}
高级特性
1. 自定义序列化
// 使用JSON序列化存储结构体
type User struct {
ID int
Name string
}
func setUser(cache *bcache.BigCache, key string, user User) error {
data, err := json.Marshal(user)
if err != nil {
return err
}
return cache.Set(key, data)
}
func getUser(cache *bcache.BigCache, key string) (User, error) {
data, err := cache.Get(key)
if err != nil {
return User{}, err
}
var user User
err = json.Unmarshal(data, &user)
return user, err
}
2. 缓存统计
stats := cache.Stats()
fmt.Printf("Hits: %d, Misses: %d, DelHits: %d, DelMisses: %d\n",
stats.Hits, stats.Misses, stats.DelHits, stats.DelMisses)
最佳实践
-
合理配置分片数:Shards数量应根据预期缓存大小和并发量设置,通常1024是个不错的起点
-
设置适当的过期时间:LifeWindow应根据业务需求设置,避免内存无限增长
-
处理缓存穿透:对于不存在的键,可以在缓存中设置空值标记
-
批量操作优化:对于批量操作,考虑使用pipeline
-
监控缓存命中率:定期检查stats,优化缓存策略
bcache作为本地缓存性能极高,在分布式环境中通常作为一级缓存使用,配合Redis等持久化存储实现多级缓存架构,既能保证性能又能确保数据的最终一致性。