Go Redis 管道和事务之 go-redis

这篇具有很好参考价值的文章主要介绍了Go Redis 管道和事务之 go-redis。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Go Redis 管道和事务之 go-redis

Go Redis 管道和事务官方文档介绍

Redis pipelines(管道) 允许一次性发送多个命令来提高性能,go-redis支持同样的操作, 你可以使用go-redis一次性发送多个命令到服务器,并一次读取返回结果,而不是一个个命令的操作。

Go Redis 管道和事务: https://redis.uptrace.dev/zh/guide/go-redis-pipelines.html

  • 管道

  • Watch 监听

  • 事务

#管道

通过 go-redis Pipeline 一次执行多个命令并读取返回值:

pipe := rdb.Pipeline()

incr := pipe.Incr(ctx, "pipeline_counter")
pipe.Expire(ctx, "pipeline_counter", time.Hour)

cmds, err := pipe.Exec(ctx)
if err != nil {
	panic(err)
}

// 结果你需要再调用 Exec 后才可以使用
fmt.Println(incr.Val())

或者你也可以使用 Pipelined 方法,它将自动调用 Exec:

var incr *redis.IntCmd

cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
	incr = pipe.Incr(ctx, "pipelined_counter")
	pipe.Expire(ctx, "pipelined_counter", time.Hour)
	return nil
})
if err != nil {
	panic(err)
}

fmt.Println(incr.Val())

同时会返回每个命令的结果,你可以遍历结果集:

cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
	for i := 0; i < 100; i++ {
		pipe.Get(ctx, fmt.Sprintf("key%d", i))
	}
	return nil
})
if err != nil {
	panic(err)
}

for _, cmd := range cmds {
    fmt.Println(cmd.(*redis.StringCmd).Val())
}

#Watch 监听

使用 Redis 事务, 监听key的状态,仅当key未被其他客户端修改才会执行命令, 这种方式也被成为 乐观锁。

Redis 事务:https://redis.io/docs/manual/transactions/

乐观锁:

WATCH mykey

val = GET mykey
val = val + 1

MULTI
SET mykey $val
EXEC

#事务

你可以使用 TxPipelinedTxPipeline 方法,把命令包装在 MULTIEXEC 中, 但这种做法没什么意义:

cmds, err := rdb.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
	for i := 0; i < 100; i++ {
		pipe.Get(ctx, fmt.Sprintf("key%d", i))
	}
	return nil
})
if err != nil {
	panic(err)
}

// MULTI
// GET key0
// GET key1
// ...
// GET key99
// EXEC

你应该正确的使用 Watch + 事务管道, 比如以下示例,我们使用 GET, SETWATCH 命令,来实现 INCR 操作, 注意示例中使用 redis.TxFailedErr 来判断失败:

const maxRetries = 1000

// increment 方法,使用 GET + SET + WATCH 来实现Key递增效果,类似命令 INCR
func increment(key string) error {
	// 事务函数
	txf := func(tx *redis.Tx) error {
   // // 获得当前值或零值 
		n, err := tx.Get(ctx, key).Int()
		if err != nil && err != redis.Nil {
			return err
		}

		n++  // 实际操作

    // 仅在监视的Key保持不变的情况下运行
		_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
			pipe.Set(ctx, key, n, 0)
			return nil
		})
		return err
	}
	
	for i := 0; i < maxRetries; i++ {
		err := rdb.Watch(ctx, txf, key)
		if err == nil {
			// Success.
			return nil
		}
		if err == redis.TxFailedErr {
			// 乐观锁失败
			continue
		}
		return err
	}

	return errors.New("increment reached maximum number of retries")
}

Go Redis 管道和事务 实操

package main

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

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接
func initRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 没有密码,默认值
		DB:       0,  // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20, // 最大套接字连接数。 默认情况下,每个可用CPU有10个连接,由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消,没有值,也没有截止日期。
	// 它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}

// watchDemo 在key值不变的情况下将其值+1
func watchKeyDemo(key string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	// Watch准备一个事务,并标记要监视的密钥,以便有条件执行(如果有密钥的话)。
	// 当fn退出时,事务将自动关闭。
	// func (c *Client) Watch(ctx context.Context, fn func(*Tx) error, keys ...string)
	return rdb.Watch(ctx, func(tx *redis.Tx) error {
		// Get Redis `GET key` command. It returns redis.Nil error when key does not exist.
		// 获取 Key 的值 n
		n, err := tx.Get(ctx, key).Int()
		if err != nil && err != redis.Nil {
			fmt.Printf("redis get failed, err: %v\n", err)
			return err
		}
		// 假设操作耗时5秒
		// 5秒内我们通过其他的客户端修改key,当前事务就会失败
		time.Sleep(5 * time.Second)
		// txpipeline 执行事务中fn队列中的命令。
		// 当使用WATCH时,EXEC只会在被监视的键没有被修改的情况下执行命令,从而允许检查和设置机制。
		// Exec总是返回命令列表。如果事务失败,则返回TxFailedErr。否则Exec返回第一个失败命令的错误或nil
		_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
			// 业务逻辑 如果 Key 没有变化,则在原来的基础上加 1
			pipe.Set(ctx, key, n+1, time.Hour)
			return nil
		})
		return err
	}, key)
}

func main() {
	if err := initRedisClient(); err != nil {
		fmt.Printf("initRedisClient failed: %v\n", err)
		return
	}
	fmt.Println("initRedisClient started successfully")
	defer rdb.Close() // Close 关闭客户端,释放所有打开的资源。关闭客户端是很少见的,因为客户端是长期存在的,并在许多例程之间共享。


	err := watchKeyDemo("watch_key")
	if err != nil {
		fmt.Printf("watchKeyDemo failed: %v\n", err)
		return
	}
	fmt.Printf("watchKeyDemo succeeded!\n")
}

运行

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
initRedisClient started successfully
watchKeyDemo succeeded!

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base took 6.5s 
➜ go run main.go
initRedisClient started successfully
watchKeyDemo failed: redis: transaction failed

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base took 6.2s 
➜ 

Redis 操作文章来源地址https://www.toymoban.com/news/detail-486505.html

27.0.0.1:6379> get watch_key
(nil)
127.0.0.1:6379> set watch_key 9
OK
127.0.0.1:6379> get watch_key
"9"
127.0.0.1:6379> get watch_key
"10"
127.0.0.1:6379> set watch_key 99
OK
127.0.0.1:6379>

到了这里,关于Go Redis 管道和事务之 go-redis的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 4.redis-事务和管道

    目录 一.事务   1.特性   2.命令 二.管道 pipe   1.操作   2.与原生批量命令对比   4.注意事项   1.特性     1) 单独的隔离操作       Redis的事务仅仅是保证事务里的操作会被连续独占得执行,redis命令执行是单线程架构,在执行完事事务内所有指令是不可能再去同时执行其他客

    2023年04月09日
    浏览(31)
  • redis 事务与管道

    1.1 什么是事务: 指可以一次执行多个命令,本质是一组命令的集合。 一个事务中的所有命令都会序列化, 按顺序的串行化执行而不会被其他命令插入,不许加塞 。 即: 一个队列中,一次性、顺序性、排他性的执行一系列命令 。 1.2 与传统关系型数据库的事务相比redis事务

    2024年02月04日
    浏览(43)
  • Redis---事务&管道

    目录 一、Redis的事务是什么? 1.1 Redis和关系型数据库事务的区别  二、怎么玩Redis事务?  2.1  正常执行:  2.2 放弃事务  2.3  全体连坐  2.4  冤头债主  2.5  watch监控 三、管道 3.1  为什么会引入管道这个概念呢?我们首先来看一道面试题 3.2   管道是什么?  3.3  管道的

    2024年02月03日
    浏览(31)
  • Redis 7 第五讲 事务、管道、发布订阅 过渡篇

             可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞          一个队列中,一次性、顺序性、排他性的执行一系列命令 单独的隔离操作 Redis的事务仅仅是保证事务里的操

    2024年02月10日
    浏览(37)
  • go的协程和管道运用案例

    2024年01月19日
    浏览(37)
  • Go学习第十一章——协程goroutine与管道channel

    1 协程goroutine 1.1 基本介绍 前置知识:“进程和线程”,“并发与并行” 协程的概念 协程(Coroutine)是一种用户态的轻量级线程,不同于操作系统线程,协程能够在单个线程中实现多任务并发,使用更少的系统资源。协程的运行由程序控制,不需要操作系统介入,因此协程之

    2024年02月08日
    浏览(40)
  • Go语言-无限可能的管道协程:解锁并发编程的新境界

    在Go语言中,协程(Goroutine)是一种轻量级的并发执行单位,它可以与其他协程并发执行,但不同于操作系统级别的线程。Go语言的协程由Go运行时(Go runtime)来调度,可以在相同的地址空间中并发执行,并且具有非常小的切换开销。 以下是一些关于Go协程的重要特点和用法:

    2024年01月24日
    浏览(43)
  • Go 语言实现 MySQL 数据库事务

    MySQL事务是指一组数据库操作,它们被视为一个逻辑单元,并且要么全部成功执行,要么全部回滚(撤销)。事务是数据库管理系统提供的一种机制,用于确保数据的一致性和完整性。 事务具有以下特性(通常由ACID原则定义): 原子性(Atomicity):事务中的所有操作要么全

    2024年02月08日
    浏览(46)
  • 使用go语言构建区块链 Part4.事务1

    英文源地址 事务是比特币的核心, 区块链的唯一目的是以安全可靠的方式存储交易, 因此在交易创建后没有人可以修改. 今天我们开始实现事务, 但由于这是一个相当大的主题, 我将它分成两部分: 在这一部分中, 我们将实现事务的通用机制, 在第二部分中, 我们将研究细节. 此外

    2024年02月06日
    浏览(50)
  • Go重写Redis中间件 - Go实现Redis集群

    这章的内容是将我们之前实现的单机版的Redis扩充成集群版,给Redis增加集群功能,在增加集群功能之前,我们先学习一下在分布式系统中引用非常广泛的技术一致性哈希,一致性哈希在我们项目里就应用在我们Redis集群的搭建这块 详解一致性哈希 Redis集群需求背景 单台服务

    2024年02月13日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包