Dynamo模式支持并发写入同一个键,这可能会导致写冲突的发生。在多主架构中,写冲突一般在写操作同步的时候;在无主架构中则是在读修复或者是数据回传的时候。
问题主要在于,对于不同的节点,由于网络等原因,经常看到不同的事件发生顺序。
如果每个节点都采用简单的最后写入获胜的策略,就有可能出现上面图示的情况:不同节点对于事件发生的顺序无法达成一致。对此,有以下应对策略:
最后写入原则
对于节点本身,保留最后写入的数据去覆盖相对比较旧的数据,关键点在于对于可能冲突的写入操作,如何确定他们的发生顺序。事件顺序有时真的很难确定,甚至在很多情况下是并发的请求。这时需要一些机制来确定事件顺序,比如为每次写操作加上时间戳,对于冲突操作只保留最大时间戳的数据(这就是最后写入原则)。
由于在读修复或者数据回传的时候,冲突操作才能被检测到,这会出现一种情况:写操作时返回操作成功,过一段时间(发生了读修复或者数据回传)这个写操作被覆盖了。
happens-before和并发
如何确定两个时间是否是并发的?
有些场景下两个请求必然存在先后顺序,比如创建和更新,创建和删除等等。有些场景则没有逻辑上的先后顺序,比如不同客户端对于同一个键的修改。前者中,有happens-before关系,逻辑上的依赖;后者则没有逻辑上的先后顺序,叫做并发请求。
下面以一个购物车为例来说明并发与happens-before请求的确定:
对于这个图示,下图说明了happens-before和并发的关系:
- 数据库保留版本号概念,为每次写入操作进行版本号递增。
- 读操作时返回所有没有被覆盖的数据和版本号,规定写操作进行之前必须先进行读操作,并针对最新的版本数据进行写操作。
- 写操作进行时必须说明是基于某一版本的数据进行更新写入。
这里有冲突自动解决策略的介绍。
Vector clock
Happens-before