美文网首页
如何安全地实现多Key操作?

如何安全地实现多Key操作?

作者: 一生逍遥一生 | 来源:发表于2021-03-29 18:12 被阅读0次

    etcd v3 为了解决多key的原子操作问题,提供了全新迷你事务API,同时基于MVCC版本号,它可以实现各种隔离级别的事务。
    它的基本结构为:client.Txn(ctx).If(cmp1,cmp2,...).Then(op1,op2,...,).Else(op1,op2,..)
    If语句支持那些检查项:

    • key的最近一次修改版本号mod_version,判断是否为预期版本。
    • key的创建版本号create_version,检查key是否过期。
    • key的修改次数version,判断修改次数是否为符合预期。
    • key的value值,判断值是否符合预期。

    ACID是衡量事务的四个特性,有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

    原子性与持久性

    事务的原子性是指一个事务中,所有请求要么同时成功,要么同时失败。

    持久性是指事务一旦提交,其所做的修改会永久保存在数据库。

    在使用etcd实现转账的情况下(从A转到B):刚扣完A的钱(扣款成功),发生crash:
    此时MVCC写事务持有boltdb写锁,仅是将修改提交到内存中,保证幂等性、防止日志重复执行的一致性索引consistent index也并未更新。同时,负责
    boltdb事务提交的goroutine因无法持有写锁, 也并未将事务提交到持久化存储中。在节点重启时,etcd server会重放WAL中的已提交日志条目,再次执行
    事务。

    A扣款成功,B也增加完成,MVCC事务完成转账,server返回给client转账成功后,boltdb的事务提交goroutine,批量将事务持久化到磁盘中时发生了crash,
    如何保证原子性和持久性?
    一致性consistent index字段值是和key-value数据在一个boltdb事务里同时持久化到磁盘中。若在boltdb事务提交过程中发生crash了,简单情况是
    consistent index和key-value数据都更新失败。节点重启后, etcd server重放WAL中已提交日志条目时,同样会再次应用转账事务到状态机中,因此事务
    的原子性和持久性依然能得到保证。

    一致性

    在不同场景中,一致性的含义不同:

    • 分布式系统中多副本数据一致性:各个副本之间的数据是否一致
    • CAP原理中的一致性是指可线性化(多副本组成)。
    • 事务的一致性:由数据库和业务沉痼两方面组成。

    为了保证转账的一致性:

    • 检查转账者资源大于等于转账资金,在提交事务时,通过账号资产的版本号,确保双方账号资产未被其他事务修改。若双方资源被前台事务修改,账号资源版本号
      检查失败,这时业务可以通过获取最新的资产和版本号,发起新的转账事务流程解决。
    • etcd会通过WAL日志和consistent index、boltdb事务特性,去确定事务的原子性,因此不会有部分成功部分失败的操作,导致资源凭空消失、新增。

    隔离性

    常见的事务隔离级别有一下四种:

    • 未提交读:一个client能读取到未提交的事务。A扣除后,B的资金还没有增加,这时client读取中间状态数据,发现总资金数额减少,破坏了事务一致性的约束。
    • 已提交读:只能读取到已提交的事务数据,但存在不可重复读的问题。
    • 可重复读:每次读取的数据都是一样的。
    • 串行化:隔离级别最高,读写相互阻塞,通过牺牲并发能力、串行化来解决事务并发更新过程中的隔离问题。为了优化性能,在基于MVCC机制实现的各个数据库
      系统中,提供了一个名为"可串行化的快照隔离"级别,相对于悲观锁而言,通过快照技术实现的类似串行化的效果、事务提交时能检查是否冲突。

    未提交读

    etcd是基于boltdb实现读写操作的,读请求由boltdb的读事务处理,可以理解为快照读。写请求由boltdb写事务处理,etcd定时将一批写操作提交到boltdb
    并清空buffer。

    由于etcd是批量提交写事务的,读事务又是快照读,因此当MVCC写事务完成时,它需要更新buffer,这样下一个读请求到达时,才能从buffer中获取到最新数据。

    etcd并未使用悲观锁来解决脏读的问题,而是通过MVCC机制来实现读写不阻塞,并解决脏读的问题。

    可重复读

    如何实现可重复读:
    通过MVCC快照读,或者参考etcd的事务框架STM实现,在事务中维护一个读缓存,优先从读缓存中查找,不存在则从etcd查询并更新到缓存中,这样事务中后续
    读请求都可从缓存中查找,确保了可重复读。

    串行化快照读

    在事务刚开始时,首先获取etcd当前的版本号rev,事务中后续发出的读请求都带上这个版本号rev,告诉etcd需要获取那个时间点的快照数据, etcd的MVCC
    机制就能确保事务中能读取到同一时刻的数据。

    相关文章

      网友评论

          本文标题:如何安全地实现多Key操作?

          本文链接:https://www.haomeiwen.com/subject/wbobhltx.html