在 MongoDB 上模拟事务操作来实现支付

Posted 天师符

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在 MongoDB 上模拟事务操作来实现支付相关的知识,希望对你有一定的参考价值。

我们的产品叫「学海密探」,属于在线教育行业,产品需要有支付功能,然而支付最蛋疼是什么?有人会说是支付宝和微信等支付接口的接入开发!没错,但支付接口的开发算是比较简单的了,我觉得凡是跟钱有关系的操作最重要的是事务问题,这一点很重要,很重要,真的很重要!LeanCloud 官方文档中有提到 MongoDB 不支持事务,并建议对事务有强烈需求的开发者使用其他折中方式来实现。我们的支付必须用事务,我们经过 N 轮讨后论设计了一套基于 LeanCloud 的支付场景的通用解决方案,也许其他团队比我们做得好,在这里还是分享下我们的一些见解吧。

我们在用户表 _User 中加入了一个 paylogs 字段,类型是 Array,主要用于存放着每一次支付日志的 id,用它来判断用户的账户 account 的值,比如钱扣没扣,加没加等情况。因为在支付过程中不能保证每一次操作都成功,所以还要引入一个日志表 log 来做数据的一致性,保证用户资金变动与实际相符。log 表里有一个「状态」字段 state,它的取值为 0 ~ 5,主要是从发起支付开始,生成 order 表,生成 log 表等,并记录 log 的状态,后续所有的业务都要根据 state 的状态执行不同的业务操作,不管在哪个状态出问题都可以根据 log表的 state 状态重复执行相应的业务操作,从而保证支付过程的数据一致性!

比如在充值的时候,用户扣费成功后,修改用户的 account 值成功了,但在修改 state 字段时失败了,下一次再执行时又怎么判断用户的 account 值已经改变了呢?这个问题真是太重要了,这就要用到那个 paylogs 字段,判断 paylogs 字段是否有值,八戒影院有值说明刚才用户的 account 值已经操作过了,此时可以不用操作,如果没有再进行操作。这里利用了 LeanCloud 数据存储接口的按条件更新数据的功能(使用 query 参数)来保证操作的原子性,从而也解决了并发问题!这个 query 参数真得要夸一下,在 LeanCloud 还没提供这个功能之前,我们只能采用 log 表中的新旧值比对来解决数据一致问题,但无法避免并发问题,这样当用户数据错了,我们只能认为支付失败,然后进入人工干预环节,很是麻烦。

最后再说说我们和 LeanCloud 的相遇。最初是通过朋友圈知道了 LeanCloud,后来就一直关注着。也试过一些项目,发现开发应用真是方便许多。原本大量的后端需求都要自己研发,现在都省了,只需要专注于写应用端的代码,效率自然会提高一倍以上,开发迭代速度也上去了。当然也有过顾虑,生怕遇到一些特殊场景 LeanCloud 满足不了那我们就悲剧了!但后来看到一些知名的应用都在用 LeanCloud,心里也就安稳了,有前辈们踩过坑,有 LeanCloud 的技术支持做后盾,我们就这样选定了 LeanCloud。

随着项目的进行,我们从传统的后端服务接口开发变成了面向「Document」开发,服务端工作减轻了不少。虽然在数据设计中遇到一些复杂结构的问题,比如 Pointer 和 Relations 的问题。记得当时 Array 不支持 Pointer 的 include,所以考虑用 Relations,但在 LeanCloud 社区中又看到几个相关问题都推荐使用 Array 或者 Pointer,后来通读了相关文档对数据模型有了更深的理解,问题也就少了,所以说全面了解文档是至关重要的。

以上是关于在 MongoDB 上模拟事务操作来实现支付的主要内容,如果未能解决你的问题,请参考以下文章

翻译MongoDB指南/CRUD操作

带有 MongoDB 和事务的 Spring 批处理

mongodb 单事务内使用多线程加速的简单例子

mongodb 单事务内使用多线程加速的简单例子

mongodb 单事务内使用多线程加速的简单例子

mongodb 单事务内使用多线程加速的简单例子