Go语言高速缓存Caching
目录
- GCache
- 分布式缓存
GCache
GCache 简介
定义:GCache 是一个高性能的内存缓存库,用于在 Go 语言中实现本地缓存功能。
特点:
- 高性能
- 支持多种缓存策略
- 简单易用
安装 GCache
go get -u github.com/patrickmn/go-cache
基本使用
初始化缓存
package main
import (
"fmt"
"time"
"github.com/patrickmn/go-cache"
)
func main() {
// 初始化缓存
c := cache.New(5*time.Minute, 10*time.Minute)
// 设置键值对
c.Set("key", "value", cache.DefaultExpiration)
// 获取值
val, found := c.Get("key")
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Key not found")
}
}
高级功能
缓存过期时间
- 默认过期时间:
cache.DefaultExpiration
- 自定义过期时间:
time.Duration
package main
import (
"fmt"
"time"
"github.com/patrickmn/go-cache"
)
func main() {
// 初始化缓存
c := cache.New(5*time.Minute, 10*time.Minute)
// 设置键值对及过期时间
c.Set("key", "value", 1*time.Minute)
// 等待一段时间
time.Sleep(2 * time.Minute)
// 尝试获取值
val, found := c.Get("key")
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Key expired or not found")
}
}
批量操作
- 批量设置:
c.SetMulti
- 批量获取:
c.GetMulti
package main
import (
"fmt"
"time"
"github.com/patrickmn/go-cache"
)
func main() {
// 初始化缓存
c := cache.New(5*time.Minute, 10*time.Minute)
// 批量设置键值对
items := map[string]interface{}{
"key1": "value1",
"key2": "value2",
}
c.SetMulti(items, cache.DefaultExpiration)
// 批量获取值
vals, err := c.GetMulti([]string{"key1", "key2"})
if err == nil {
fmt.Println("Values:", vals)
} else {
fmt.Println("Error:", err)
}
}
缓存清理
- 删除单个键:
c.Delete
- 清空所有键:
c.Flush
package main
import (
"fmt"
"time"
"github.com/patrickmn/go-cache"
)
func main() {
// 初始化缓存
c := cache.New(5*time.Minute, 10*time.Minute)
// 设置键值对
c.Set("key", "value", cache.DefaultExpiration)
// 删除键
c.Delete("key")
// 尝试获取值
val, found := c.Get("key")
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Key deleted or not found")
}
// 清空所有键
c.Flush()
// 尝试获取值
val, found = c.Get("key")
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Cache flushed")
}
}
缓存策略
LRU (Least Recently Used)
LRU 算法:当缓存满时,移除最近最少使用的项。
package main
import (
"fmt"
"time"
"github.com/patrickmn/go-cache"
)
func main() {
// 初始化缓存
c := cache.New(5*time.Minute, 10*time.Minute)
// 设置键值对
c.Set("key1", "value1", cache.DefaultExpiration)
c.Set("key2", "value2", cache.DefaultExpiration)
c.Set("key3", "value3", cache.DefaultExpiration)
// 获取值
val, found := c.Get("key1")
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Key not found")
}
// 再次设置键值对
c.Set("key4", "value4", cache.DefaultExpiration)
// 尝试获取值
val, found = c.Get("key1")
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Key removed by LRU")
}
}
LFU (Least Frequently Used)
LFU 算法:当缓存满时,移除最不常用的项。
package main
import (
"fmt"
"time"
"github.com/patrickmn/go-cache"
)
func main() {
// 初始化缓存
c := cache.New(5*time.Minute, 10*time.Minute)
// 设置键值对
c.Set("key1", "value1", cache.DefaultExpiration)
c.Set("key2", "value2", cache.DefaultExpiration)
c.Set("key3", "value3", cache.DefaultExpiration)
// 获取值
val, found := c.Get("key1")
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Key not found")
}
// 再次获取值
val, found = c.Get("key1")
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Key not found")
}
// 再次设置键值对
c.Set("key4", "value4", cache.DefaultExpiration)
// 尝试获取值
val, found = c.Get("key2")
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Key removed by LFU")
}
}
错误处理
检查错误:if err != nil { ... }
package main
import (
"fmt"
"time"
"github.com/patrickmn/go-cache"
)
func main() {
// 初始化缓存
c := cache.New(5*time.Minute, 10*time.Minute)
// 设置键值对
err := c.Set("key", "value", cache.DefaultExpiration)
if err != nil {
fmt.Println("Error setting key:", err)
return
}
// 获取值
val, found := c.Get("key")
if !found {
fmt.Println("Key not found")
return
}
fmt.Println("Value:", val)
}
高级主题
并发安全
并发安全:GCache 默认是线程安全的。
package main
import (
"fmt"
"sync"
"time"
"github.com/patrickmn/go-cache"
)
func main() {
// 初始化缓存
c := cache.New(5*time.Minute, 10*time.Minute)
var wg sync.WaitGroup
wg.Add(10)
// 并发设置键值对
for i := 0; i < 10; i++ {
go func(i int) {
defer wg.Done()
err := c.Set(fmt.Sprintf("key%d", i), fmt.Sprintf("value%d", i), cache.DefaultExpiration)
if err != nil {
fmt.Println("Error setting key:", err)
return
}
}(i)
}
wg.Wait()
// 获取值
for i := 0; i < 10; i++ {
val, found := c.Get(fmt.Sprintf("key%d", i))
if found {
fmt.Println("Value:", val)
} else {
fmt.Println("Key not found")
}
}
}
集成测试
使用测试框架:testing
package main
import (
"testing"
"time"
"github.com/patrickmn/go-cache"
)
func TestCache(t *testing.T) {
// 初始化缓存
c := cache.New(5*time.Minute, 10*time.Minute)
// 设置键值对
err := c.Set("key", "value", cache.DefaultExpiration)
if err != nil {
t.Errorf("Failed to set key: %v", err)
}
// 获取值
val, found := c.Get("key")
if !found {
t.Errorf("Key not found")
}
if val != "value" {
t.Errorf("Unexpected value: %v", val)
}
}
分布式缓存
分布式缓存简介
定义:分布式缓存是一种存储在多个网络节点上的数据存储方式,旨在提高数据访问速度和系统整体性能。 作用:减少数据库负载,加快数据读取速度。
常见的分布式缓存系统
- Redis
- Memcached
- Ehcache
选择合适的缓存系统
根据项目需求(如数据持久化需求、数据结构支持等)选择合适的缓存系统。
Go 语言中的缓存库介绍
- go-redis
- memcached
- redigo
实战演练:使用 go-redis 构建一个简单的分布式缓存系统
环境搭建
# 安装 Redis 服务器
sudo apt-get install redis-server
# 安装 go-redis 库
go get -u github.com/go-redis/redis/v8
编写客户端代码
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
// 连接 Redis 服务
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
// 设置键值对
ctx := context.Background()
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
// 获取值
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val) // 输出 "key: value"
}
深入理解 Redis
Redis 数据类型
- String: 简单的键值对
- Hash: 字段和值的映射表
- List: 有序的字符串列表
- Set: 无序的字符串集合
- Sorted Set: 有序的字符串集合,每个成员都关联一个分数
Redis 命令详解
基本命令:
SET key value
GET key
DEL key
高级命令:
HSET key field value
HGET key field
LPUSH key value
LPOP key
SADD key member
SREM key member
ZADD key score member
ZRANGE key start stop
Redis 集群与主从复制
- 集群架构:通过多个 Redis 节点组成集群,实现高可用和负载均衡。
- 主从复制:主节点负责写操作,从节点负责读操作,提高读性能。
使用 go-redis 进行高级操作
连接池
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
// 创建连接池
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
PoolSize: 10, // 设置连接池大小
})
// 设置键值对
ctx := context.Background()
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
// 获取值
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val) // 输出 "key: value"
}
异步操作
package main
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
func main() {
// 创建 Redis 客户端
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
// 设置键值对(异步)
ctx := context.Background()
cmd := rdb.Set(ctx, "key", "value", 0)
go func() {
if err := cmd.Err(); err != nil {
fmt.Println("Error setting key:", err)
} else {
fmt.Println("Key set successfully")
}
}()
// 等待一段时间确保异步操作完成
time.Sleep(1 * time.Second)
// 获取值
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val) // 输出 "key: value"
}
批量操作
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
// 创建 Redis 客户端
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
// 批量设置键值对
ctx := context.Background()
pipeline := rdb.Pipeline()
pipeline.Set(ctx, "key1", "value1", 0)
pipeline.Set(ctx, "key2", "value2", 0)
_, err := pipeline.Exec(ctx)
if err != nil {
panic(err)
}
// 批量获取值
vals, err := rdb.MGet(ctx, "key1", "key2").Result()
if err != nil {
panic(err)
}
fmt.Println("vals:", vals) // 输出 "vals: [value1 value2]"
}
一致性哈希
一致性哈希原理
- 环形空间:将哈希空间映射到一个圆环上。
- 虚拟节点:每个实际节点对应多个虚拟节点。
一致性哈希实现
package main
import (
"fmt"
"hash/crc32"
"sort"
"strconv"
)
type ConsistentHash struct {
nodes []string
circle map[int]string
}
func NewConsistentHash(nodes []string) *ConsistentHash {
ch := &ConsistentHash{
nodes: nodes,
circle: make(map[int]string),
}
for _, node := range nodes {
for i := 0; i < 100; i++ {
hash := crc32.ChecksumIEEE([]byte(fmt.Sprintf("%s:%d", node, i)))
ch.circle[hash] = node
}
}
return ch
}
func (ch *ConsistentHash) GetNode(key string) string {
hash := crc32.ChecksumIEEE([]byte(key))
keys := make([]int, 0, len(ch.circle))
for k := range ch.circle {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
if k >= hash {
return ch.circle[k]
}
}
return ch.circle[keys[0]]
}
func main() {
nodes := []string{"node1", "node2", "node3"}
ch := NewConsistentHash(nodes)
fmt.Println(ch.GetNode("key1")) // 输出 "node1"
fmt.Println(ch.GetNode("key2")) // 输出 "node2"
fmt.Println(ch.GetNode("key3")) // 输出 "node3"
}
集群模式
Redis 集群架构
- 数据分片:将数据分散到多个节点上。
- 故障转移:自动检测并恢复故障节点。
使用 Redis 集群
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
// 创建 Redis 集群客户端
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{
"localhost:7000",
"localhost:7001",
"localhost:7002",
},
})
// 设置键值对
ctx := context.Background()
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
// 获取值
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val) // 输出 "key: value"
}
数据同步策略
主从同步
- 全量同步:首次同步时将所有数据从主节点复制到从节点。
- 增量同步:后续同步只传输主节点上的新更改。
Redis Sentinel
- 故障检测:自动检测主节点故障。
- 自动切换:将从节点提升为主节点。
版权声明
本文仅代表作者观点,不代表区块链技术网立场。
本文系作者授权本站发表,未经许可,不得转载。
上一篇:Go语言常用标准库 下一篇:详解以太坊地址生成实战
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。