我们在在线业务里保证数据一致性的一种思路, 和 MySQL 保证 redolog 和 binlog 一致性的方案很像。下面我先来说一下业务里我们是怎么处理的。
说到最终一致性,绕不开的一种方案就是消息队列了。
消息队列可以保证某个数据变更后,多个下游都完成对应的处理。那么如何保证消息的成功推到消息队列?
强依赖消息队列的写入稳定性
这个做法是比较激进的,就是在在线业务中,设计变更操作时,不写表。而是推消息。
真正写表的服务业放在接消息后的模块去处理。
这样的做法问题很多:
问题1
因为业务一般存在一个主表。如果主表都没写,就去写消息。
这时候用户以为是操作成功了。但是立马查库很有可能还没有完成写库。体验较差。
问题2
另外在我们公司里的实际体验,推消息的稳定性远低于 MySQL 的写入稳定性。如果推消息失败,就返回报错,接口稳定性好像也差点意思。
改进一下?
开事务,先写表,再推消息。如果推消息失败,回滚事务,接口报错。
这么处理后,解决了上面的问题1,只要我接口成功了,再查数据一定是更新了的。
但是还是解决不了问题2,接口的稳定性会下降,用户感知就是,点好几次才能行!
更可怕的是,如果你是高并发场景。这可不得了。长事务是不可取的。
再提升下用户体验
写表,并通过补偿脚本保证推消息成功。
具体的做法:
表中加一个字段 todo,代表是否推了消息。更新逻辑这么处理:
- 写表,todo=1
- 推消息。
- todo=0, 再写表
只要写表成功了,接口就可以返回成功。
所以只要1成功了。就可以告诉用户。接口成功。
如果2失败了,无所谓。我们会起一个补偿脚本。定期扫 todo=1 的消息去推。
如果3失败了。无所谓。用补偿脚本再推一次(不管幂等哈,因为消息队列交互的场景,我们都是提倡消费者接到消息后反查最新数据才落库的)
说说 MySQL 里的设计
写到这,想到 MySQL 里,保证内部事务一致性(两段式提交)的策略设计:
prepare
commit
炫杉:有点事,改日再写!