go中redis使用的示例代码

 更新时间:2025年05月12日 10:38:30   作者:草海桐  
本文主要介绍了在Go语言中使用Redis进行数据存储和管理,包括单机模式、哨兵模式和集群模式的部署及使用,具有一定的参考价值,感兴趣的可以了解一下

在Go语言中使用Redis进行数据存储和管理可以带来高效和灵活的优势。下面的讲解包括单机模式、哨兵模式和集群模式的部署及使用。

一、Redis 简介

Redis是一个高性能的内存数据库,支持多种数据结构,如字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(ZSet)等。它还支持事务、批量操作和流式数据处理。Redis常用于缓存、实时数据处理、计数器、消息队列等场景。

二、Go中Redis的使用

1. 安装Go Redis包

推荐使用github.com/go-redis/redis/v9,它支持单机、哨兵和集群模式。

安装命令:

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

2. 单机模式

特点:单一Redis实例,简单易用,适合开发和测试环境。

连接示例

package main

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

func main() {
	ctx := context.Background()
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})

	pong, err := client.Ping(ctx).Result()
	if err != nil {
		fmt.Printf("连接失败:%v\n", err)
		return
	}
	fmt.Println(pong)  // 输出:PONG

	// 示例:设置和获取值
	if err := client.Set(ctx, "key", "value", 0).Err(); err != nil {
		fmt.Printf("设置失败:%v\n", err)
		return
	}
	val, err := client.Get(ctx, "key").Result()
	if err != nil {
		fmt.Printf("获取失败:%v\n", err)
		return
	}
	fmt.Printf("值:%v\n", val)
}

说明

  • 使用redis.NewClient创建客户端。
  • 使用Ping测试连接。
  • Set设置键值,Get读取值。

3. 哨兵模式

特点:提供高可用性,当主库故障时,自动故障转移。

依赖

  • 必须安装Redis哨兵服务。

连接示例

package main

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

func main() {
	ctx := context.Background()
	client := redis.NewFailoverClient(&redis.FailoverOptions{
		MasterName:    "mymaster",
		SentinelAddrs: []string{"localhost:26379", "localhost:26380", "localhost:26381"},
	})

	pong, err := client.Ping(ctx).Result()
	if err != nil {
		fmt.Printf("连接失败:%v\n", err)
		return
	}
	fmt.Println(pong)

	// 示例:设置和获取值
	if err := client.Set(ctx, "key", "value", 0).Err(); err != nil {
		fmt.Printf("设置失败:%v\n", err)
		return
	}
	val, err := client.Get(ctx, "key").Result()
	if err != nil {
		fmt.Printf("获取失败:%v\n", err)
		return
	}
	fmt.Printf("值:%v\n", val)
}

说明

  • 使用redis.NewFailoverClient创建客户端。
  • 指定主库名称MasterName和哨兵地址。
  • 当主库故障时,哨兵会自动将从库提升为主库。

三、Redis集群

1. 集群模式

特点:通过分片实现水平扩展,每个节点处理一部分数据,适合高并发场景。

集群部署

部署结构

  • 6节点:3主3从,每个主节点负责一个分片。
  • 每个分片有1主1从。

使用redis-cli创建集群

  • 启动6个Redis实例,分别指定不同端口。
  • 使用redis-cli命令创建集群。

创建集局脚本:例如 start_cluster.sh

# 启动6个节点,端口分别为30001到30006
for port in {30001..30006}; do
    redis-server --cluster-enabled yes --cluster-config-file node-${port}.conf --port ${port} --daemonize yes
done

# 创建集群
redis-cli --cluster create 127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 --cluster-replicas 1

在上面的脚本中,node-${port}.conf 是 Redis 集群模式下每个节点的配置文件,用于指定节点的运行参数。如果这些配置文件不存在,Redis 会自动生成一个默认的配置文件,但为了确保集群部署的正确性,最好手动创建这些配置文件。

例如:

# node-30001.conf
cluster-enabled yes
port 30001
bind 127.0.0.1
daemonize yes
logfile /var/log/redis/redis_30001.log
dir ./data/30001
save 60 1
appendonly yes

解释:

  • cluster-enabled yes: 启用集群模式。
  • port: 指定当前节点的端口。
  • bind: 绑定主机地址。
  • daemonize yes: 后台运行。
  • logfile: 指定日志文件路径。
  • dir: 指定数据文件存储路径。
  • save: 配置数据持久化策略。
  • appendonly: 启用 AOF 持久化。

然后给脚本赋予执行权限并运行:

chmod +x start_cluster.sh
./start_cluster.sh

连接示例

package main

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

func main() {
	// 集群节点地址
	addresses := []string{
		"localhost:30001",
		"localhost:30002",
		"localhost:30003",
		"localhost:30004",
		"localhost:30005",
		"localhost:30006",
	}

	clusterClient := redis.NewClusterClient(&redis.ClusterOptions{
		Addrs: addresses,
	})

	pong, err := clusterClient.Ping(context.Background()).Result()
	if err != nil {
		fmt.Printf("连接失败:%v\n", err)
		return
	}
	fmt.Println(pong)

	// 设置键值对
	if err := clusterClient.Set(context.Background(), "key", "value", 0).Err(); err != nil {
		fmt.Printf("设置失败:%v\n", err)
		return
	}

	// 获取值
	val, err := clusterClient.Get(context.Background(), "key").Result()
	if err != nil {
		fmt.Printf("获取失败:%v\n", err)
		return
	}
	fmt.Printf("值:%v\n", val)
}

说明

  • 使用redis.NewClusterClient创建集群客户端。
  • 初始化时提供所有节点地址。
  • 集群模式下,Redis自动处理数据分片和请求路由。

四、常用数据结构与操作

1. 字符串(String)

// 设置过期时间
err = client.Set(context.Background(), "key", "value", 10*time.Second).Err()

// 递增计数器
num, err := client.Incr(context.Background(), "counter").Result()

// 递减计数器
num, err := client.Decr(context.Background(), "counter").Result()

2. 哈希(Hash)

// 设置字段值
err = client.HSet(context.Background(), "hashKey", "field", "value").Err()

// 获取字段值
value, err := client.HGet(context.Background(), "hashKey", "field").Result()

3. 列表(List)

// 从左边推入元素
err = client.LPush(context.Background(), "listKey", "value").Err()

// 弹出左边第一个元素
value, err := client.LPop(context.Background(), "listKey").Result()

4. 集合(Set)

// 添加元素
err = client.SAdd(context.Background(), "setKey", "element").Err()

// 移除元素
err = client.SRem(context.Background(), "setKey", "element").Err()

// 获取所有元素
members, err := client.SMembers(context.Background(), "setKey").Result()

5. 有序集合(ZSet)

// 添加元素并设置分数
err = client.ZAdd(context.Background(), "zsetKey", &redis.Z{Member: "element", Score: 100}).Err()

// 获取元素的分数
score, err := client.ZScore(context.Background(), "zsetKey", "element").Result()

// 获取排名
rank, err := client.ZRank(context.Background(), "zsetKey", "element").Result()

五、事务与批量操作

1. 事务

// 开始事务
ctx := context.Background()
tx, err := client.Tx(ctx)

// 执行事务中的操作
_, err = tx.Pipeline()(
	function(ctx context.Context) (_redis.CMDCb, error) {
		_, err := tx.Get(ctx, "balance").Result()
		if err != nil {
			return nil, err
		}
		_, err := tx.Incr(ctx, "balance").Result()
		if err != nil {
			return nil, err
		}
		return nil, nil
	},
)

if err != nil {
	fmt.Printf("事务执行失败:%v\n", err)
}

2. 管道技术

// 创建管道
pipe := client.Pipeline()

// 执行多个命令
cmds, err := pipe.Set(context.Background(), "key1", "value1", 0).
	Set(context.Background(), "key2", "value2", 0).
	Exec(context.Background())

if err != nil {
	fmt.Printf("管道执行失败:%v\n", err)
	return
}

// 打印结果
for _, cmd := range cmds {
	fmt.Printf("%v\n", cmd)
}

六、高可用性

1. 复制(主从)

  • 设置主从复制,确保数据安全。
  • 主库写入,数据同步到从库。
  • 从库可用于读分离,提高读性能。

2. 故障转移

  • 使用哨兵模式实现自动故障转移。
  • 集群模式下,节点故障自动迁移。

3. 连接池

// 配置连接池
pool := &redis.Pool{
	Dial: func(context.Context) (redis.Conn, error) {
		return client.DialContext(context.Background())
	},
	MaxActive:   10,  // 最大活跃连接数
	MaxIdle:      5,   // 最大空闲连接数
	IdleTimeout: 5 * time.Minute,
}

// 使用连接池
conn := pool.Get(context.Background())
defer conn.Close()

七、监控与性能调优

1. 内置工具

  • redis-cli: 命令行工具,执行各种Redis命令。
  • redis-benchmark: 性能基准测试工具。
# 基准测试
redis-benchmark -h 127.0.0.1 -p 6379 -t set,lpush -n 10000 -q

2. 性能指标

  • 内存使用: 使用info memory查看内存状态。
  • 拒绝策略: 配置maxmemory-policy避免内存溢出。
  • 过期时间: 设置合理的expire,控制键生命周期。

3. 调试

  • 使用slowlog记录慢查询。
  • 监控blocked clientsmaster_repl_offset

八、实际案例

1. 高并发秒杀系统

需求:在高并发下,确保商品库存正确。

解决方案

  • 使用Redis的事务和分布锁。
  • 数据结构:Hash存储商品库存。
package main

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

// 秒杀函数
func doSecKill(ctx context.Context, client *redis.Client, productId string, userId string) (bool, error) {
	// 锁名称:秒杀锁
	lockKey := "lock:sec:kill"
	// 商品库存Key
	stockKey := "stock:" + productId

	// 尝试获取锁,防止超卖
	lock, err := client.LockNew(lockKey, 100*time.Millisecond).Acquire(ctx, time.Second*5).Result()
	if err != nil {
		return false, err
	}
	defer lock.Release(ctx)

	// 检查库存是否充足
	currentStock, err := client.HGet(ctx, stockKey, "quantity").Int64()
	if err != nil || currentStock <= 0 {
		return false, fmt.Errorf("库存不足")
	}

	// 减少库存
	_, err = client.HIncrBy(ctx, stockKey, "quantity", -1).Result()
	if err != nil {
		return false, fmt.Errorf("秒杀失败:%v", err)
	}

	return true, nil
}

func main() {
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})

	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(userId int) {
			defer wg.Done()
			ctx := context.Background()
			success, err := doSecKill(ctx, client, "product001", fmt.Sprintf("user%d", userId))
			if success {
				fmt.Printf("用户%d秒杀成功\n", userId)
			} else {
				fmt.Printf("用户%d秒杀失败:%v\n", userId, err)
			}
		}(i)
	}
	wg.Wait()
}

说明

  • 使用Redis的分布锁确保秒杀过程的原子性。
  • 使用Hash结构存储库存信息,实现并发安全的扣减操作。

九、最佳实践

1. 数据过期时间

  • 为键设置合理的TTL,避免内存膨胀。

2. 内存管理

  • 监控used_memory,确保内存使用在可控范围内。
  • 配置maxmemorymaxmemory-policy

3. 日志配置

  • 开启Redis日志,记录操作和错误信息。
  • 使用slowlog跟踪慢查询。

4. 安全性

  • 设置强密码。
  • 配置防火墙,限制访问来源。

5. 监控

  • 使用Prometheus、Grafana监控Redis性能。
  • AlertManager配置告警规则。

6. 备份恢复

  • 定期备份RDB或AOF文件。
  • 配置主从复制,确保数据安全。

7. 连接池管理

  • 合理配置连接池参数,避免连接耗尽。

8. 数据持久化

  • 选择RDB或AOF,根据需求配置持久化策略。

十、总结

在Go语言中使用Redis,特别是结合哨兵和集群模式,可以实现高可用和高扩展性的系统。合理选择数据结构,使用事务和管道技术,可以提升性能。同时,注重监控和维护,确保Redis的稳定运行。

到此这篇关于go中redis使用的示例代码的文章就介绍到这了,更多相关go redis使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang中的Slice与数组及区别详解

    Golang中的Slice与数组及区别详解

    数组是一种具有固定长度的基本数据结构,在golang中与C语言一样数组一旦创建了它的长度就不允许改变,数组的空余位置用0填补,不允许数组越界。今天小编通过实例代码操作给大家详细介绍lang中的Slice与数组的相关知识,一起看看吧
    2020-02-02
  • Go语言实现生产者-消费者模式的方法总结

    Go语言实现生产者-消费者模式的方法总结

    这篇文章主要介绍了在 Go 语言中实现生产者消费者模式的多种方法,并重点探讨了通道、条件变量的适用场景和优缺点,需要的可参考一下
    2023-05-05
  • golang程序使用alpine编译出最小arm镜像实现

    golang程序使用alpine编译出最小arm镜像实现

    这篇文章主要为大家介绍了golang程序使用alpine编译出最小arm镜像,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • 详解Go语言中的作用域和变量隐藏

    详解Go语言中的作用域和变量隐藏

    这篇文章主要为大家介绍了Go语言中的作用域和变量隐藏,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助,感兴趣的小伙伴可以了解一下
    2022-04-04
  • 源码分析Golang log是如何实现的

    源码分析Golang log是如何实现的

    go语言的log包提供了简单的日志记录功能,允许开发者在应用程序中记录重要的信息、错误、警告等,log包是Go标准库的一部分,因此,使用它不需要安装额外的第三方库,本文给大家源码分析了Golang log是如何实现的,需要的朋友可以参考下
    2024-03-03
  • 详解Go flag实现二级子命令的方法

    详解Go flag实现二级子命令的方法

    这篇文章主要介绍了Go flag 详解,实现二级子命令,本文就探讨一下 Go 语言中如何写一个拥有类似特性的命令行程序,需要的朋友可以参考下
    2022-07-07
  • Golang连接池的几种实现案例小结

    Golang连接池的几种实现案例小结

    这篇文章主要介绍了Golang连接池的几种实现案例小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Golang合并yaml文件过程逐步讲解

    Golang合并yaml文件过程逐步讲解

    之前一直从事java开发,习惯了使用yaml文件的格式,尤其是清晰的层次结构、注释,下面这篇文章主要给大家介绍了关于Golang合并yaml文件的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • Golang反射模块reflect使用方式示例详解

    Golang反射模块reflect使用方式示例详解

    Golang的反射功能,在很多场景都会用到,最基础的莫过于rpc、orm跟json的编解码,更复杂的可能会到做另外一门语言的虚拟机,这篇文章主要介绍了Golang反射模块reflect使用方式探索,需要的朋友可以参考下
    2023-01-01
  • Golang error使用场景介绍

    Golang error使用场景介绍

    我们在使用Golang时,不可避免会遇到异常情况的处理,与Java、Python等语言不同的是,Go中并没有try...catch...这样的语句块,这个时候我们如何才能更好的处理异常呢?本文来教你正确方法
    2023-03-03

最新评论