go中redis使用的示例代码

作者:

文章目录
  • Redis是一个高性能的内存数据库,支持多种数据结构,如字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(ZSet)等。它还支持事务、批量操作和流式数据处理。Redis常用于缓存、实时数据处理、计数器、消息队列等场景。
  • 在Go语言中使用Redis,特别是结合哨兵和集群模式,可以实现高可用和高扩展性的系统。合理选择数据结构,使用事务和管道技术,可以提升性能。同时,注重监控和维护,确保Redis的稳定运行。 到此这篇关于go中redis使用的示例代码的文章就介绍到这了,更多相关go redis使用内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: golang使用redis实现全文搜索功能详解 使用Go语言启动Redis的实例详解 如何在Go语言中高效使用Redis的Pipeline Go使用Redis实现分布式锁的常见方法 使用go语言实现Redis持久化的示例代码 Go语言使用Redis和Etcd实现高性能分布式锁 重学Go语言之如何使用Redis Go Redis客户端使用的两种对比 golang redis中Pipeline通道的使用详解
  • 目录
    • 一、Redis 简介
    • 二、Go中Redis的使用
      • 1. 安装Go Redis包
      • 2. 单机模式
      • 3. 哨兵模式
    • 三、Redis集群
      • 1. 集群模式
    • 四、常用数据结构与操作
      • 1. 字符串(String)
      • 2. 哈希(Hash)
      • 3. 列表(List)
      • 4. 集合(Set)
      • 5. 有序集合(ZSet)
    • 五、事务与批量操作
      • 1. 事务
      • 2. 管道技术
    • 六、高可用性
      • 1. 复制(主从)
      • 2. 故障转移
      • 3. 连接池
    • 七、监控与性能调优
      • 1. 内置工具
      • 2. 性能指标
      • 3. 调试
    • 八、实际案例
      • 1. 高并发秒杀系统
      • 说明
    • 九、最佳实践
      • 1. 数据过期时间
      • 2. 内存管理
      • 3. 日志配置
      • 4. 安全性
      • 5. 监控
      • 6. 备份恢复
      • 7. 连接池管理
      • 8. 数据持久化
    • 十、总结

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

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

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

      安装命令:

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

      特点:单一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("连接失败:%vn", err)
      		return
      	}
      	fmt.Println(pong)  // 输出:PONG
      
      	// 示例:设置和获取值
      	if err := client.Set(ctx, "key", "value", 0).Err(); err != nil {
      		fmt.Printf("设置失败:%vn", err)
      		return
      	}
      	val, err := client.Get(ctx, "key").Result()
      	if err != nil {
      		fmt.Printf("获取失败:%vn", err)
      		return
      	}
      	fmt.Printf("值:%vn", val)
      }
      

      说明

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

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

      依赖

      • 必须安装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("连接失败:%vn", err)
      		return
      	}
      	fmt.Println(pong)
      
      	// 示例:设置和获取值
      	if err := client.Set(ctx, "key", "value", 0).Err(); err != nil {
      		fmt.Printf("设置失败:%vn", err)
      		return
      	}
      	val, err := client.Get(ctx, "key").Result()
      	if err != nil {
      		fmt.Printf("获取失败:%vn", err)
      		return
      	}
      	fmt.Printf("值:%vn", val)
      }
      

      说明

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

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

      集群部署

      部署结构

      • 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("连接失败:%vn", err)
      		return
      	}
      	fmt.Println(pong)
      
      	// 设置键值对
      	if err := clusterClient.Set(context.Background(), "key", "value", 0).Err(); err != nil {
      		fmt.Printf("设置失败:%vn", err)
      		return
      	}
      
      	// 获取值
      	val, err := clusterClient.Get(context.Background(), "key").Result()
      	if err != nil {
      		fmt.Printf("获取失败:%vn", err)
      		return
      	}
      	fmt.Printf("值:%vn", val)
      }
      

      说明

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

      // 设置过期时间
      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()
      

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

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

      // 添加元素
      err = client.SAdd(context.Background(), "setKey", "element").Err()
      
      // 移除元素
      err = client.SRem(context.Background(), "setKey", "element").Err()
      
      // 获取所有元素
      members, err := client.SMembers(context.Background(), "setKey").Result()
      

      // 添加元素并设置分数
      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()
      

      // 开始事务
      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("事务执行失败:%vn", err)
      }
      

      // 创建管道
      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("管道执行失败:%vn", err)
      	return
      }
      
      // 打印结果
      for _, cmd := range cmds {
      	fmt.Printf("%vn", cmd)
      }
      

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

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

      // 配置连接池
      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()
      

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

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

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

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

      解决方案

      • 使用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秒杀失败:%vn", userId, err)
      			}
      		}(i)
      	}
      	wg.Wait()
      }
      

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

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

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

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

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

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

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

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

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

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

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

      您可能感兴趣的文章:

      • golang使用redis实现全文搜索功能详解
      • 使用Go语言启动Redis的实例详解
      • 如何在Go语言中高效使用Redis的Pipeline
      • Go使用Redis实现分布式锁的常见方法
      • 使用go语言实现Redis持久化的示例代码
      • Go语言使用Redis和Etcd实现高性能分布式锁
      • 重学Go语言之如何使用Redis
      • Go Redis客户端使用的两种对比
      • golang redis中Pipeline通道的使用详解

      站内搜索