Go语言高速缓存Caching

目录GCache分布式缓存GCacheGCache简介定义:GCache是一个高性能的内存缓存库,用于在Go语言中实现本地缓存功能。特点:高性能支持多种缓存策略简单易用安装GCachegoget-ugithub.com/patrickmn/go-cac

目录


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

  • 故障检测:自动检测主节点故障。
  • 自动切换:将从节点提升为主节点。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
天涯学馆
天涯学馆
0x9d6d...50d5
资深大厂程序员,12年开发经验,致力于探索前沿技术!