从光大证券的软件设计缺陷想到的。

13-08-18 banq
                   

8月16中国股市出现名震历史的乌龙事件,导致该事件的原因今天被证监会调查后,确定是软件系统的设计问题:

光大证券自营的策略交易系统存在程序调用错误、额度控制失效等设计缺陷,并被连锁触发,导致生成巨量市价委托订单,累计申报买入234亿元,实际成交72.7亿元。

根据这条信息我个人瞎胡乱猜测一下出错场景:

交易员发出买单以后,由于锁问题,无论是语言内存锁或数据库锁,导致买入状态没有更新,或者交易员点按“买入”按钮时,没有响应,不由自主多按了几下,结果,这些命令恰恰全部被计算机系统都接受,只是因为锁堵塞,没有及时处理,没有及时更新状态,等系统稍微空闲,这些被锁住的交易全部被执行。

这个问题有点类似“重复提交”问题,打个简单比喻,在本论坛发言,按发布键后,没有反应,或者查询主题列表没有看到,然后就再多按一次,结果同一个贴重复发表了两次。

个人观点:出现这种问题其实真正体现了用计算机提供的锁等ACID机制并不能真正解决业务上的高一致性。

有一篇文章:弱一致性在现实世界中到处存在 http://www.jdon.com/43246

过去,所有的一切都是写在纸上的假象,至少过去几十年一直如此,即时一致性是IT人发明的,他们纸上谈兵地认为:不同组织不同地方的数据如果没有即时一致性(高一致性)几乎不可能的。

其实,现实生活中也并不是不存在高一致性,只是可能比较少,这里我想提出一个新观点:高一致性是个好东西,但是好东西是不是一定是缺省配置呢?比如有钱是个好东西,那么是不是让每个人一生下来都有很多钱呢?实际相反,每个人生下来缺省是没有钱的,赤条条降生。

既然高一致性是好东西,当然不能随便缺省配置,但是实际中却相反,当我们使用关系数据库,或者使用事务机制等ACID时,这些机制都缺省让我们的程序变成高一致性,高一致性不是稀罕物,可以无偿获得,那么无疑结果是,当系统负载增加时,每个无偿得到高一致性的处理程序都要付出代价,那就是堵塞。

相反,如果我们缺省是弱一致性,对于需要高一致性的业务,我们通过业务分析会辨识发现,然后给予充分的业务重视和业务设计,那么也许能够将计算机提供ACID服帖地为我们业务高一致性服务。

在DDD分析需求过程中,这个问题得到高度重视,以DDD书籍中订单为案例,该订单有一个额度限制,也就是所有订单下条目Item总额不能超过订单设定的额度限制,这个限制其实是一种逻辑一致性限制,而且必须是实时高一致性的,因为每增加一个订单条目,都必须检查这一逻辑一致是否被破坏,如果是,立即不能加入。

比如Oder限制是1000元,订购了三件商品,总金额是1200元,超过了1000元限制,那么这个订单是无法生成的,这属于即时一致性检查。

这种检查我们采取什么方式实现呢?传统是采取锁的方法,将Oder这个对象或表锁住,因为在统计总金额时是不允许其他人有操作的,当你锁住这个对象或表时,堵塞的可能性就会到来。

如何突破这种锁方式的限制呢?那就是Actor模型,也就是说,Oder检查自己总金额,不用在被外界访问时被锁住,而是永远无法被外界锁住可能,因为Actor模型无法被外界直接使用方法调用,只能象发QQ消息一样与外界交互。

具体可见这个贴:事件驱动编程:

http://www.jdon.com/45436

所以,对于这起事件,我个人观点是:这次光大事件问题核心直指系统连锁,应该是状态锁问题,不是内存锁,就是数据库锁等出错,依赖计算机提供的acid等锁机制其实不是百分百安全,只有用业务方法解决业务问题才是根本解决之道。

[该贴被banq于2013-08-18 22:05修改过]

                   

4
banq
2013-08-19 07:21

想到再补充一点:

交易员发出了几次买入指令,这是用户的意图,也就是说,用户的意图是分批买入50ETF,而计算机系统在执行这个意图时出错。

这里可能还存在一个潜在思路问题,没有区分清楚用户意图的接受和用户意图的执行这两个动作。

用户意图首先必须由领域模型接受,领域模型必须综合自身的逻辑限制,比如额度限制,对用户意图进行判断是否可执行?如果用户几次买入50ETF的意图会破坏领域中逻辑限制,如额度限制,领域模型有权决定不再执行意图指令。

而我们传统架构实则都没有区分这两种,而是在执行意图过程中再进行逻辑判断,比如额度限制,如果执行过程本身被锁住,自身形成了一个封闭边界,只能对自己内部进行逻辑审核,而用户再发出一个同样的意图,就不能跨两个意图进行综合判断了。

打个比喻,每个省都有自己的监察部门,但是这个监察只能审核自己省内部的,如果两个省范同样错误就没有办法跨省联合监察,这必须更高一层监察部门实现。

而我们区分用户意图接受和执行两个动作,实际就是在跟高层面对用户指令进行监督检查。

参考见:http://www.jdon.com/45385

bingyang
2013-08-20 11:27

分析得很好。

我觉得“锁”这个东西跟人的思维习惯相关——先锁住处理完后其他人再进来,好比火车站检票口平时人少大家一个一个来,到春运时一堆人拥到哪里那个叫悲催,慢慢检票,但我们的系统可等不了那么长的时间“检票”,锁是锁住了但大量数据拥挤而来形成的阻塞问题不可忽视,到底用不用锁,什么时候用锁,一定花时间要推敲,碾磨后再使用。如果这个系统用disuptor来处理,又会怎样呢?

tangxuehua
2013-08-20 13:28

1)买入是插入操作还是更新操作?如果是更新操作,在聚合根上设计乐观锁就搞定了应该,不至于出现重复买入的情况;但是,即便是update操作,要是不是因为并发的问题,用乐观锁还解决不了呢,呵呵

2)如果是插入操作,那服务器端领域模型内如何判断是否重复买入?也就是聚合的不变性是什么?

3)那个系统没有在客户端做基本的防止重复提交的判断?比如按钮灰掉。

我觉得要谈解决方案,首先要搞清楚它的业务到底是什么,买入操作做了什么操作,这个操作有哪些业务不变性规则?这点没搞清楚,没法谈具体解决方案了。

[该贴被tangxuehua于2013-08-20 13:36修改过]

xianghx
2013-08-20 14:46

他们的好像是生成订单,和处理订单是分开的。

现在生成了好多,而处理的少。

金额发生了不同。

这个系统只有10万元,不能处理高频的交易,只能说是上面的决策问题,为了省钱。

2Go 1 2 下一页