聊聊「ZooKeeper」(上)

今天中午遛弯的时候,思考了一下分布式环境下数据一致性的问题,在此记录一下。


假设我们有一个数据存储的服务(假设用 MySQL 存储),为了数据的安全性,我们进行异地备份,在北京、郑州、深圳这三个一线城市各有一台服务器,每台服务器上都部署了我们的服务,每台服务器的 MySQL 上各存有一份数据。

那么,问题来了。

我们想将 id=1 数据项中的 num = 100 修改为了 200,那么怎么实现呢?

最容易想到的,可以这样实现:

我们调用这三台机器的修改数据项的服务接口(即调用三次),将 “id=1 数据项中的 num = 100” 修改为 200。我们可以设置规则,如果三次调用都成功,则数据更新成功;或者设置规则只要有大于等于2台服务器接口调用成功,就认为数据更新成功。

上面这种方案,是否合理?问题在哪里

上面这种方案将判断数据是否更新成功的操作放到了客户端进行,这当然是不合理的。数据是否更新成功,应该是提供数据更新的服务端告诉客户端的,不应该由客户端自己实现判断逻辑。

继续思考,是不是可以这样

我们在服务端再弄一个服务 A,主要工作就是调用三个服务器更新数据的接口,然后该服务 A 判断如果三次调用都成功,则数据更新成功,客户端只用调用这个项目A,项目A 会告诉客服端数据更新成功或失败。

听起来是可以的。那下一步我们要思考服务 A 具体做哪些工作。

比如客户端告诉服务 A 说“将 id=1 的数据项中的 num 字段值从 100 修改为了 200”,然后服务 A 对三台机器上的服务说“开始更新吧,将 id=1 的数据项中的 num 字段值从 100 修改为了 200,更新之后告诉我结果”,然后三个机器上的服务开始更新并将结果告诉服务 A,如果三台都更新成功了,则整个流程成功;如果其中1台失败了,则其余两台也要回滚数据。

但这里就有另一个问题了:

三台机器中某两台更新数据成功,有一台更新失败,则更新成功的两台机器上的数据要回滚。但数据已更新到数据库,事务也提交了,你要“回滚”数据,怎么回滚?如果在你回滚之前,有另一个线程也更改了“id=1 的数据项中的 num 字段值”,岂不产生了脏数据?

那应该怎么办呢?

最简单的解决办法,对同一条数据的更改,必须串行进行。即三台机器里每台机器都需要获取到另外两台机器的更新状态后,才能决定提交或回滚数据,否则就一直等待,其他线程也不能修改这一条数据。(是不是感觉逻辑开始复杂了..)

问题是:串行更新必然会导致性能问题。一般来说,软件开发中『最容易想到的解决方案,效率都不是最优的』。

针对这种场景,我们看看 ZooKeeper 是怎么实现的:聊聊「ZooKeeper」(下)

完。

码先生
Author: 码先生

1 thought on “聊聊「ZooKeeper」(上)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注