上次讨论, 大家对这几个概念在分布式系统中的关系似乎有些混乱. 在这里整理一下, 如果有错误请指出.
首先看两阶段提交. 这是为了解决在不同的数据库中数据一致性的问题. 举一个例子, 在
一个 transaction 中, 需要更新A数据库的一张表, 还需要更新B数据库的一张表. 那么如果我们先在A数据库更新, 然后提交,
再在B数据库更新, 这个时候更新不成功(比如,违反某种约束条件), 这个时候A数据库无法回滚, 一致性被破坏. 为了解决这个问题,
有些数据库支持两阶段提交, 第一个阶段是预提交阶段,第二个阶段是正式提交. 预提交的数据是可以回滚的, 而且数据库保证预提交成功后提交一定成功.
有了两阶段提交, 上面问题的解决办法是, 先在两个数据库更新, 然后分别在两个库预提交, 都成功再正式提交.
预提交会检查数据的合法性(是否有违反约束条件等), 然后会通过锁来保证正式提交一定成功. 但是如果出现类似下面的状况, A
B两个库预提交都成功了, 进入正式提交阶段, A成功, B失败(比如B宕机,断电什么的). 这个时候两阶段提交失败,我记得是需要人介入的,
要dba手工处理.
那么回过头来看我们的问题, 一个节点要写两个备份,两个备份都成功才认为是成功,否则失败. 因为两个备份不存在什么违反约束等情况,
所以我们的问题跟两阶段提交不一样, 不需要预提交过程. 实际山相当于我们的预提交总是成功的. 当正式提交失败时, 比如 A写两个备份到B和C,
C返回成功, B没有返回. 这个时候如何处理就成了问题. 按照约定, 这次写入应该是失败, 假设我们这个时候开始回滚,
但是因为这个相当于正式提交阶段,B返回成功,则B上的数据可以被读取, 相当于B上已经提交了. 再对B做一次反向的操作,
仍旧存在有人读取了新数据, 然后B才被反向操作. 这样那个人就读取到了脏数据. 读到脏数据是很严重的问题, 等于是无中生有的数据.
而如果采用回滚策略, 这个问题无法解决(我没想到什么办法).
我们看一下dynamo是如何处理的. 这个问题相当于dynamo中 N=W R=1这种情况. 为了简单起见, 我们还是让 N=W=3.
dynamo有一层叫做coordinator, 是可以放到任意位置的, 我们假设放到了客户端. 那么一个写操作, coordinator
会选择三个节点(不妨设为 A B C), 同时发三个写操作的包, 然后等候回应. 假设规定时间内, 只有两个节点返回成功(不妨设为A B),
也就是说, 第三个节点没成功, 这个时候coordinator会选择第四个节点, 发出写操作,
如果写不成功就选择第五个…直到写成功为止(不妨设这个节点是X). 这样保证了备份数. 额外被选择的节点(X)存储这个数据信息到一个单独的目录中,
X节点负责不断的尝试把这部分数据写回到C节点. 这个过程叫做 hinted handoff. 在这样的策略下, 因为R=1,
读任何一个节点都是有效的, 假设客户读节点 A B, 那么读到的都是最新数据, 如果读节点C, 可能读到老的数据. 但是无论如何读不到脏数据.
因为在可期望的时间内, 所有的节点上的数据可以达到一致, 所以这种一致性叫做最终一致性. 最终一致性中也是没有脏数据的.
阅读(1329) | 评论(0) | 转发(0) |