Redis备忘录

基础知识

缓存设计思想

缓存的主要目的是提高数据访问速度,减少后端数据库的压力。设计时需要考虑:

  • 数据一致性:缓存与数据库中的数据需保持一致。
  • 缓存失效策略:如LRU(最近最少使用)等,以便有效管理缓存中的数据。
  • 数据过期:设置合理的过期时间,避免不必要的数据占用缓存空间。

缓存开发规范

在使用Redis时,应遵循一些开发规范:

  • 键命名规则:应清晰且具有描述性,例如使用user:1001:session这样的格式。
  • 异常处理:在操作Redis时应处理可能的异常情况,确保系统的健壮性。
  • 性能监控:定期监控Redis的性能指标,如命中率、内存使用情况等。

缓存架构模式

常见的缓存架构模式包括:

  • 本地缓存:将数据缓存到应用服务器内存中,适合小型应用。
  • 集中式缓存:使用Redis作为集中式缓存,适合分布式系统。
  • CDN缓存:在内容分发网络(CDN)中缓存静态内容,提高访问速度。

Redis概述

Redis是一个高性能的键值存储系统,支持多种数据结构(如字符串、哈希、列表、集合等)。它的特点包括:

  • 内存存储:数据存储在内存中,访问速度极快。
  • 持久化:支持RDB和AOF持久化机制,确保数据安全。

Redis性能原理

Redis的高性能来源于:

  • 单线程模型:使用单线程处理请求,避免了上下文切换的开销。
  • 非阻塞I/O:使用事件驱动架构,能够快速响应大量请求。

Redis安装和访问

Redis的安装相对简单,可以通过源码编译或包管理工具进行安装。访问Redis一般使用客户端命令行工具(redis-cli)或通过编程语言的Redis客户端库。

Redis应用场景

Redis广泛应用于各种场景,包括但不限于:

  • 缓存:减少数据库负担,提高查询速度。
  • 会话存储:存储用户会话信息。
  • 排行榜/计数器:利用Redis的有序集合实现实时排行榜。

实例

# 使用 Docker 启动 Redis
docker run --name redis -d -p 6379:6379 redis

使用 Shell 脚本与 Redis 交互

创建一个简单的 Shell 脚本来与 Redis 进行交互。
shell_script.sh

#!/bin/bash

# 设置 Redis 服务器地址
REDIS_HOST="localhost"
REDIS_PORT=6379

# 向 Redis 写入数据
echo "SET mykey 'Hello, Redis!'" | redis-cli -h $REDIS_HOST -p $REDIS_PORT

# 从 Redis 读取数据
echo "GET mykey" | redis-cli -h $REDIS_HOST -p $REDIS_PORT

运行 Shell 脚本

chmod +x shell_script.sh
./shell_script.sh

使用 Golang 访问 Redis

创建一个简单的 Go 应用程序,使用 go-redis 库连接 Redis 并进行操作。
main.go

package main

import (
    "fmt"
    "log"
    "github.com/go-redis/redis/v8"
    "context"
)

func main() {
    ctx := context.Background()

    // 创建 Redis 客户端
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    // 向 Redis 写入数据
    err := rdb.Set(ctx, "mykey", "Hello, Redis!", 0).Err()
    if err != nil {
        log.Fatalf("Could not set key: %v", err)
    }

    // 从 Redis 读取数据
    val, err := rdb.Get(ctx, "mykey").Result()
    if err != nil {
        log.Fatalf("Could not get key: %v", err)
    }

    fmt.Printf("mykey: %s\n", val)
}

运行 Golang 程序

go get github.com/go-redis/redis/v8

然后运行 Go 程序:

go run main.go

Redis 数据结构分析

Redis 数据结构简介

SDS 字符串

  • SDS(Simple Dynamic Strings)是 Redis 中的字符串实现,处理字符串时避免了传统字符串的缺陷。

跳跃表

  • 跳跃表用于实现有序集合,提供高效的插入、删除和查找操作。

压缩列表

  • 压缩列表用于节省内存空间,适合存储小数据量的列表和哈希。

整数集合

  • 整数集合是一种集合,实现了对整数的高效存储和操作。

字典

  • Redis 使用字典(哈希表)来实现键值对存储。

quicklist

  • quicklist 是一个混合数据结构,用于列表,结合了链表和压缩列表的优点。

Stream

  • Stream 是 Redis 5.0 引入的一种新的数据结构,适合处理实时数据流。

HyperLogLog

  • HyperLogLog 用于估算唯一元素的基数,节省内存。

RedisObject 结构

  • RedisObject 结构用于表示 Redis 中的所有数据类型,包含元数据和实际数据。

使用 Shell 脚本与 Redis 交互

我们将创建一个 Shell 脚本,展示如何使用 Redis 不同的数据结构。
shell_script.sh

#!/bin/bash

# 设置 Redis 服务器地址
REDIS_HOST="localhost"
REDIS_PORT=6379

# 使用 SDS 存储字符串
echo "SET mystring 'Hello, Redis!'" | redis-cli -h $REDIS_HOST -p $REDIS_PORT

# 使用集合存储数据
echo "SADD myset 'value1' 'value2' 'value3'" | redis-cli -h $REDIS_HOST -p $REDIS_PORT

# 使用哈希存储数据
echo "HSET myhash field1 'Hello' field2 'World'" | redis-cli -h $REDIS_HOST -p $REDIS_PORT

# 使用列表存储数据
echo "LPUSH mylist 'item1' 'item2' 'item3'" | redis-cli -h $REDIS_HOST -p $REDIS_PORT

# 输出所有数据
echo "GET mystring" | redis-cli -h $REDIS_HOST -p $REDIS_PORT
echo "SMEMBERS myset" | redis-cli -h $REDIS_HOST -p $REDIS_PORT
echo "HGETALL myhash" | redis-cli -h $REDIS_HOST -p $REDIS_PORT
echo "LRANGE mylist 0 -1" | redis-cli -h $REDIS_HOST -p $REDIS_PORT

运行 Shell 脚本

chmod +x shell_script.sh
./shell_script.sh

使用 Golang 访问 Redis 数据结构

接下来,我们将创建一个 Go 程序,演示如何使用 Redis 的不同数据结构。
main.go

package main

import (
    "context"
    "fmt"
    "log"
    "github.com/go-redis/redis/v8"
)

func main() {
    ctx := context.Background()

    // 创建 Redis 客户端
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    // 使用 SDS 存储字符串
    err := rdb.Set(ctx, "mystring", "Hello, Redis!", 0).Err()
    if err != nil {
        log.Fatalf("Could not set string: %v", err)
    }

    // 使用集合存储数据
    err = rdb.SAdd(ctx, "myset", "value1", "value2", "value3").Err()
    if err != nil {
        log.Fatalf("Could not add to set: %v", err)
    }

    // 使用哈希存储数据
    err = rdb.HSet(ctx, "myhash", "field1", "Hello", "field2", "World").Err()
    if err != nil {
        log.Fatalf("Could not set hash: %v", err)
    }

    // 使用列表存储数据
    err = rdb.LPush(ctx, "mylist", "item1", "item2", "item3").Err()
    if err != nil {
        log.Fatalf("Could not push to list: %v", err)
    }

    // 输出所有数据
    val, err := rdb.Get(ctx, "mystring").Result()
    if err != nil {
        log.Fatalf("Could not get string: %v", err)
    }
    fmt.Printf("mystring: %s\n", val)

    members, err := rdb.SMembers(ctx, "myset").Result()
    if err != nil {
        log.Fatalf("Could not get set members: %v", err)
    }
    fmt.Printf("myset: %v\n", members)

    hash, err := rdb.HGetAll(ctx, "myhash").Result()
    if err != nil {
        log.Fatalf("Could not get hash: %v", err)
    }
    fmt.Printf("myhash: %v\n", hash)

    list, err := rdb.LRange(ctx, "mylist", 0, -1).Result()
    if err != nil {
        log.Fatalf("Could not get list: %v", err)
    }
    fmt.Printf("mylist: %v\n", list)
}

运行 Golang 程序
然后运行 Go 程序:

go run main.go

Redis 工作机制分析

Redis 存储结构
Redis 使用一种基于字典的哈希表实现键值存储。键和值的存储结构都经过优化,以提高性能和响应速度。

键管理机制
Redis 通过哈希表来管理键,具有 O(1) 的平均时间复杂度进行插入、查找和删除操作。键的过期管理通过维护一个过期时间戳的跳表来实现。

持久化机制
Redis 提供两种主要的持久化机制:

  • RDB(Redis Database Backup):周期性地将内存数据快照保存到磁盘。
  • AOF(Append Only File):记录每一个写操作的日志,确保数据不丢失。

客户端创建和关闭机制
Redis 提供简单的连接机制,客户端可以通过 TCP 连接 Redis 服务器。连接关闭时,Redis 会释放相关资源。

命令请求处理流程
当 Redis 接收到命令请求时,首先会将其解析为命令类型,然后执行相应的操作。

服务器启动流程
Redis 服务器启动时,会加载持久化的数据,并进行必要的初始化工作。

事件处理
Redis 通过事件驱动机制处理客户端请求,使用 I/O 多路复用提高并发能力。


使用 Shell 脚本进行 Redis 操作

shell_script.sh

#!/bin/bash

# 设置 Redis 服务器地址
REDIS_HOST="localhost"
REDIS_PORT=6379

# 启动 Redis 服务器并设置持久化策略
echo "Starting Redis server..."
docker run --name redis -d -p 6379:6379 redis --appendonly yes

# 等待 Redis 服务器启动
sleep 5

# 添加一些数据
echo "Adding data to Redis..."
echo "SET mykey 'Hello, Redis!'" | redis-cli -h $REDIS_HOST -p $REDIS_PORT
echo "SET anotherkey 'Persistent Data'" | redis-cli -h $REDIS_HOST -p $REDIS_PORT

# 检查 AOF 文件
echo "Checking AOF file..."
docker exec redis ls /data

# 关闭 Redis 服务器
echo "Stopping Redis server..."
docker stop redis
docker rm redis

运行 Shell 脚本

chmod +x shell_script.sh
./shell_script.sh

使用 Golang 访问 Redis

main.go

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "github.com/go-redis/redis/v8"
)

func main() {
    ctx := context.Background()

    // 创建 Redis 客户端
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    // 添加数据到 Redis
    err := rdb.Set(ctx, "mykey", "Hello, Redis!", 0).Err()
    if err != nil {
        log.Fatalf("Could not set key: %v", err)
    }

    err = rdb.Set(ctx, "anotherkey", "Persistent Data", 0).Err()
    if err != nil {
        log.Fatalf("Could not set key: %v", err)
    }

    // 读取数据
    val, err := rdb.Get(ctx, "mykey").Result()
    if err != nil {
        log.Fatalf("Could not get key: %v", err)
    }

    fmt.Printf("mykey: %s\n", val)

    // 检查 AOF 文件是否存在
    aofFile := "/data/appendonly.aof"
    if _, err := os.Stat(aofFile); os.IsNotExist(err) {
        fmt.Printf("AOF file does not exist.\n")
    } else {
        fmt.Printf("AOF file exists: %s\n", aofFile)
    }

    // 关闭 Redis 客户端
    rdb.Close()
}

运行 Golang 程序

go run main.go

Redis 高级特性原理

发布与订阅原理
Redis 的发布/订阅(Pub/Sub)模式允许消息的发送者(发布者)向多个接收者(订阅者)发送消息,适用于实时应用。

事务实现机制
Redis 支持基本的事务机制,通过 MULTI、EXEC、DISCARD 命令实现原子性操作。

慢查询日志
Redis 提供慢查询日志功能,可以帮助开发者识别性能瓶颈,优化 SQL 查询。

Lua 脚本编程
Redis 支持 Lua 脚本编程,允许用户在 Redis 服务器端执行复杂的操作,避免多个网络往返。


使用 Shell 脚本进行发布与订阅

我们将创建一个 Shell 脚本来演示 Redis 的发布与订阅功能。
pubsub_script.sh

#!/bin/bash

# 设置 Redis 服务器地址
REDIS_HOST="localhost"
REDIS_PORT=6379

# 启动 Redis 服务器
docker run --name redis -d -p 6379:6379 redis

# 启动发布者
echo "Starting publisher..."
(
    sleep 1
    for i in {1..5}
    do
        echo "PUBLISH mychannel "Message $i"" | redis-cli -h $REDIS_HOST -p $REDIS_PORT
        sleep 1
    done
) &

# 启动订阅者
echo "Starting subscriber..."
redis-cli -h $REDIS_HOST -p $REDIS_PORT SUBSCRIBE mychannel

运行 Shell 脚本

chmod +x pubsub_script.sh
./pubsub_script.sh

使用 Golang 实现事务和慢查询日志

接下来,我们将创建一个 Go 程序,演示如何使用 Redis 的事务和慢查询日志功能。
main.go

package main

import (
    "context"
    "fmt"
    "log"
    "time"
    "github.com/go-redis/redis/v8"
)

func main() {
    ctx := context.Background()

    // 创建 Redis 客户端
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    // 开始事务
    pipe := rdb.TxPipeline()

    // 向事务中添加命令
    pipe.Set(ctx, "key1", "value1", 0)
    pipe.Set(ctx, "key2", "value2", 0)

    // 执行事务
    _, err := pipe.Exec(ctx)
    if err != nil {
        log.Fatalf("Transaction failed: %v", err)
    }

    fmt.Println("Transaction executed successfully.")

    // 模拟慢查询
    time.Sleep(2 * time.Second) // 模拟慢查询

    // 记录慢查询
    fmt.Println("Simulating slow query... (This is just a log, actual implementation requires configuration)")

    // 读取数据
    val1, err := rdb.Get(ctx, "key1").Result()
    if err != nil {
        log.Fatalf("Could not get key1: %v", err)
    }
    val2, err := rdb.Get(ctx, "key2").Result()
    if err != nil {
        log.Fatalf("Could not get key2: %v", err)
    }

    fmt.Printf("key1: %s, key2: %s\n", val1, val2)

    // 关闭 Redis 客户端
    rdb.Close()
}

运行 Golang 程序

go run main.go

使用 Lua 脚本进行操作

接下来,我们将展示如何使用 Lua 脚本在 Redis 中执行操作。
lua_script.lua

-- Lua 脚本:将两个键的值相加
local key1 = KEYS[1]
local key2 = KEYS[2]
local sum_key = KEYS[3]

local value1 = tonumber(redis.call('GET', key1))
local value2 = tonumber(redis.call('GET', key2))

if value1 and value2 then
    redis.call('SET', sum_key, value1 + value2)
    return value1 + value2
else
    return nil
end

运行 Lua 脚本
我们可以通过 Redis CLI 运行 Lua 脚本:

redis-cli --eval lua_script.lua key1 key2 sum_key

Redis 集群高可用实践

主从复制
Redis 主从复制是通过将主节点的数据复制到从节点来实现的。这有助于数据冗余和负载均衡。

哨兵模式
Redis 哨兵模式用于监控主从节点,自动故障转移,并提供高可用性的服务。

Redis Cluster
Redis Cluster 是 Redis 官方提供的集群解决方案,支持数据分片和自动故障转移。

Codis 集群
Codis 是一个基于 Redis 的分布式框架,提供了更高层次的抽象,支持数据分片和高可用性。


使用 Shell 脚本搭建主从复制

master_slave_script.sh

#!/bin/bash

# 启动 Redis 主节点
echo "Starting Redis master..."
docker run --name redis-master -d -p 6379:6379 redis

# 启动 Redis 从节点
echo "Starting Redis slave..."
docker run --name redis-slave --link redis-master -d redis redis-server --slaveof redis-master 6379

# 等待主从节点启动
sleep 5

# 测试主从复制
echo "Testing master-slave replication..."
echo "SET key1 'Hello, Redis!'" | redis-cli -h localhost -p 6379
echo "GET key1 from master: $(redis-cli -h localhost -p 6379 GET key1)"
echo "GET key1 from slave: $(redis-cli -h redis-slave -p 6379 GET key1)"

# 停止并删除容器
echo "Stopping and removing containers..."
docker stop redis-master redis-slave
docker rm redis-master redis-slave

运行 Shell 脚本

chmod +x master_slave_script.sh
./master_slave_script.sh

使用 Golang 实现 Redis 哨兵模式

sentinel_example.go

package main

import (
    "context"
    "fmt"
    "log"
    "github.com/go-redis/redis/v8"
)

func main() {
    ctx := context.Background()

    // 创建 Redis 哨兵客户端
    rdb := redis.NewFailoverClient(&redis.FailoverOptions{
        MasterName:    "mymaster",
        SentinelAddrs: []string{"localhost:26379"}, // 替换为您的哨兵地址
    })

    // 设置数据
    err := rdb.Set(ctx, "key1", "Hello, Sentinel!", 0).Err()
    if err != nil {
        log.Fatalf("Could not set key: %v", err)
    }

    // 获取数据
    val, err := rdb.Get(ctx, "key1").Result()
    if err != nil {
        log.Fatalf("Could not get key: %v", err)
    }

    fmt.Printf("key1: %s\n", val)

    // 关闭 Redis 客户端
    rdb.Close()
}

运行 Golang 程序

go run sentinel_example.go

使用 Redis Cluster

cluster_example.go

package main

import (
    "context"
    "fmt"
    "log"
    "github.com/go-redis/redis/v8"
)

func main() {
    ctx := context.Background()

    // 创建 Redis Cluster 客户端
    rdb := redis.NewClusterClient(&redis.ClusterOptions{
        Addrs: []string{"localhost:7000", "localhost:7001", "localhost:7002"}, // 替换为您的集群节点地址
    })

    // 设置数据
    err := rdb.Set(ctx, "key1", "Hello, Redis Cluster!", 0).Err()
    if err != nil {
        log.Fatalf("Could not set key: %v", err)
    }

    // 获取数据
    val, err := rdb.Get(ctx, "key1").Result()
    if err != nil {
        log.Fatalf("Could not get key: %v", err)
    }

    fmt.Printf("key1: %s\n", val)

    // 关闭 Redis Cluster 客户端
    rdb.Close()
}

使用 Codis 集群

环境准备
确保您的环境中安装了以下工具:

  • Docker
  • Go(用于编写 Golang 示例)

安装 Codis

Codis 提供了 Docker 镜像,您可以使用 Docker 来快速启动 Codis 集群。

启动 Codis 集群

以下是一个简单的 Docker Compose 配置文件,用于启动 Codis 集群。
docker-compose.yml

version: '3'

services:
  zookeeper:
    image: zookeeper:3.4.6
    ports:
      - "2181:2181"

  codis-dashboard:
    image: codis-dashboard:latest
    ports:
      - "18087:18087"
    environment:
      - ZK_HOST=zookeeper:2181

  codis-proxy:
    image: codis-proxy:latest
    ports:
      - "19000:19000"
    environment:
      - ZK_HOST=zookeeper:2181

  codis-server:
    image: redis:latest
    ports:
      - "6379:6379"

启动服务

在同一目录下运行以下命令启动 Codis 集群:

docker-compose up -d

配置 Codis

访问 Codis Dashboard,通过浏览器打开 http://localhost:18087,您可以在此配置 Codis 集群。

  1. 添加 Redis 实例;
  2. 配置分片;
  3. 启动 Proxy。

使用 Golang 连接 Codis 集群

main.go

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/go-redis/redis/v8"
)

func main() {
    ctx := context.Background()

    // 创建 Codis Redis 客户端
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:19000", // Codis Proxy 地址
    })

    // 设置数据
    err := rdb.Set(ctx, "key1", "Hello, Codis!", 0).Err()
    if err != nil {
        log.Fatalf("Could not set key: %v", err)
    }

    // 获取数据
    val, err := rdb.Get(ctx, "key1").Result()
    if err != nil {
        log.Fatalf("Could not get key: %v", err)
    }

    fmt.Printf("key1: %s\n", val)

    // 关闭 Redis 客户端
    rdb.Close()
}

运行 Golang 示例

go run main.go

Redis 企业解决方案分析

缓存命中率
缓存命中率是衡量缓存性能的重要指标。提高缓存命中率可以显著提升系统性能。

缓存预热
缓存预热是指在系统启动时,提前将数据加载到缓存中,以提高缓存命中率。

缓存穿透
缓存穿透是指请求的数据在缓存和数据库中都不存在,导致每次请求都直接查询数据库,增加数据库压力。

缓存雪崩
缓存雪崩是指缓存中的大量数据在同一时间失效,导致大量请求直接打到数据库。

缓存击穿
缓存击穿是指某个热点数据在缓存中失效,导致大量请求同时打到数据库。

Hot Key 和 Big Key

  • Hot Key:访问频率极高的键,会导致性能瓶颈。
  • Big Key:存储大量数据的键,可能导致内存使用不当。

缓存更新策略
缓存更新策略涉及如何在数据变更时更新缓存,以保持数据一致性。

缓存与数据库一致性
确保缓存和数据库之间的数据一致性是关键问题之一。

分布式锁
分布式锁用于在分布式系统中控制资源访问,确保数据一致性。


缓存预热示例

我们将使用 Shell 脚本来预热缓存。
warm_cache.sh

#!/bin/bash

# 设置 Redis 服务器地址
REDIS_HOST="localhost"
REDIS_PORT=6379

# 预热缓存
echo "Prewarming cache..."
for i in {1..10}
do
    echo "SET key$i 'Preheated Value $i'" | redis-cli -h $REDIS_HOST -p $REDIS_PORT
done

echo "Cache prewarming completed."

运行 Shell 脚本

chmod +x warm_cache.sh
./warm_cache.sh

缓存穿透示例

cache_penetration.go

package main

import (
    "context"
    "fmt"
    "log"
    "github.com/go-redis/redis/v8"
)

var ctx = context.Background()

func main() {
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    // 模拟查询数据库
    key := "nonexistent_key"
    val, err := rdb.Get(ctx, key).Result()

    if err == redis.Nil {
        // 缓存穿透,直接查询数据库
        fmt.Printf("Cache miss for key: %s. Querying database...\n", key)
        // 这里可以模拟数据库查询
        val = queryDatabase(key)
    } else if err != nil {
        log.Fatalf("Could not get key: %v", err)
    } else {
        fmt.Printf("Cache hit for key: %s, value: %s\n", key, val)
    }
}

func queryDatabase(key string) string {
    // 模拟数据库查询
    return "Database Value"
}

运行 Golang 程序

go run cache_penetration.go

缓存雪崩及击穿示例

我们将使用 Golang 模拟缓存雪崩和击穿。
avalanche_and_thunder.go

package main

import (
    "context"
    "fmt"
    "log"
    "sync"
    "time"
    "github.com/go-redis/redis/v8"
)

var ctx = context.Background()
var rdb *redis.Client

func init() {
    rdb = redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })
}

func getData(key string) string {
    val, err := rdb.Get(ctx, key).Result()

    if err == redis.Nil {
        fmt.Printf("Cache miss for key: %s\n", key)
        // 模拟缓存击穿
        val = queryDatabase(key)
        go func() {
            time.Sleep(1 * time.Second) // 假设数据在缓存中1秒后失效
            rdb.Set(ctx, key, val, 0)
        }()
    }
    return val
}

func queryDatabase(key string) string {
    // 模拟数据库延迟
    time.Sleep(2 * time.Second)
    return "Database Value"
}

func main() {
    var wg sync.WaitGroup

    keys := []string{"key1", "key2", "key3"}

    for _, key := range keys {
        wg.Add(1)
        go func(k string) {
            defer wg.Done()
            fmt.Println(getData(k))
        }(key)
    }

    wg.Wait()
}

运行 Golang 程序

go run avalanche_and_thunder.go

分布式锁示例

distributed_lock.go

package main

import (
    "context"
    "fmt"
    "log"
    "time"
    "github.com/go-redis/redis/v8"
)

var ctx = context.Background()

func main() {
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    lockKey := "my_lock"
    lockValue := "locked"

    // 尝试获取锁
    ok, err := rdb.SetNX(ctx, lockKey, lockValue, 5*time.Second).Result()
    if err != nil {
        log.Fatalf("Could not acquire lock: %v", err)
    }

    if !ok {
        fmt.Println("Could not acquire lock, another process holds it.")
        return
    }

    fmt.Println("Lock acquired. Performing some operations...")

    // 模拟操作
    time.Sleep(3 * time.Second)

    // 释放锁
    rdb.Del(ctx, lockKey)
    fmt.Println("Lock released.")
}

运行 Golang 程序

go run distributed_lock.go
posted @ 2024-09-03 18:52  Mugetsukun  阅读(13)  评论(0编辑  收藏  举报