etcd 事务操作

etcd是分布式的、可靠的、分布式存储K-V系统,用于存储分布式系统中的关键数据。

ETCD 事务

基于 CAS(Compare and Swap,即比较再交换) 方式

etcd中事务时一组原子性操作,可以确保多操作之间的原子性,并且可以保证一组操作在执行期间不会被其他操作中断

什么是事务?

事务通常就是指数据库事务。事务具有 ACID 特性,即原子性、一致性、隔离性和持久性。

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • 持久性(Durability):一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。
 

GET ETCD 的包

go get github.com/coreos/etcd/clientv3
go mod edit -replace github.com/coreos/bbolt@v1.3.4=go.etcd.io/bbolt@v1.3.4
go mod edit -replace google.golang.org/grpc@v1.60.1=google.golang.org/grpc@v1.26.0

 

etcd创建数据

etcdctl.exe put userA 1000
etcdctl.exe put userB 100

etcd查询key/value

C:\Users\EDY\Downloads\etcd-v3.5.11-windows-amd64>etcdctl.exe get --prefix user
userA
990
userB
110

 

事务操作

package main

//etcd 事务操作使用txn 方式

import (
    "context"
    "fmt"
    "log"
    "strconv"
    "time"
    "github.com/coreos/etcd/clientv3"
)
type user struct{
    Key string
    Account int
    ModRevision int64
}
//函数通过name 获取etcd key中的值 
func getEtcd(cli *clientv3.Client,name string )(u user, err error){
    account := user{Key:name}
    ctx , cancel := context.WithTimeout(context.Background(), 10* time.Second)
    resp , err := cli.Get(ctx , account.Key)
    cancel()
    if err !=nil{
        err = fmt.Errorf("etcd get err")
        
    }
    if len(resp.Kvs) == 1 {
        account.ModRevision = resp.Kvs[0].ModRevision
        account.Account,_ = strconv.Atoi(string(resp.Kvs[0].Value))
    }
    
    return account , err
}
//运算操作,修改user 结构体的值。
func accountOperation(src ,dest *user,sum int )(err error){
    if src.Account <= 0{
        err = fmt.Errorf("金额不足")
        return
    }
    src.Account -= sum
    dest.Account += sum
    return err
}
//事务操作,两个key同时执行如果其中一个key 操作失败事务执行失败,反之执行成功
func work(cli *clientv3.Client , src,dest user)(err error){ txnReps, err := cli.Txn(context.TODO()).If( clientv3.Compare(clientv3.ModRevision(src.Key), "=", src.ModRevision), clientv3.Compare(clientv3.ModRevision(dest.Key), "=", dest.ModRevision), ).Then( clientv3.OpPut(src.Key,strconv.Itoa(src.Account)), clientv3.OpPut(dest.Key,strconv.Itoa(dest.Account)), ).Else( ).Commit() if err != nil{ err = fmt.Errorf("事务操作失败") return err } if !txnReps.Succeeded { err = fmt.Errorf("事务操作失败 %v %v\n",src,dest) return err } return err } func main(){
  //连接etcd cli ,err :
= clientv3.New(clientv3.Config{ Endpoints: []string{"127.0.0.1:2379"}, DialTimeout: time.Second *5, }) if err != nil { // log.Fatal(err) fmt.Printf("etcd connection tailed err:%v\n",err) return } defer cli.Close()
// 获取userA 的数据放到user 的结构体中 src,err :
= getEtcd(cli,"userA") if err != nil { fmt.Printf("etcd get %v failed err%v",src,err) return }
  // 获取userB 的数据放到user的结构体中 dest,err :
=getEtcd(cli,"userB") if err != nil { fmt.Printf("etcd get %v failed err%v",dest,err) return }
// 对user 的结构体运算,如果src的值小于等于0时抛出异常 err
= accountOperation(&src,&dest,10) if err !=nil{ log.Fatalf("账号: %v金额不足 当前余额:%v\n",src.Key,src.Account) return } fmt.Println(src,dest)
// 事务操作 err
= work(cli ,src,dest) if err !=nil{ log.Fatalf("事务操作失败请重试 源:%v 目标:%v",src,dest) return } fmt.Println("事务操作已成功") }

 

posted @ 2023-12-28 16:49  扛把子修BUG  阅读(39)  评论(0编辑  收藏  举报