区块链 区块链技术 比特币公众号手机端

Go语言高速缓存Caching

liumuhui 2年前 (2024-11-12) 阅读数 330 #区块链
文章标签 Go语言Go

目录

  • 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

  • 故障检测:自动检测主节点故障。
  • 自动切换:将从节点提升为主节点。
版权声明

本文仅代表作者观点,不代表区块链技术网立场。
本文系作者授权本站发表,未经许可,不得转载。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门