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("事务操作已成功") }