亲爱的朋友们: 首先请允许火币君说一声:“抱歉,让大家担心了”。众所周知,在5月22日火币网上线新功能“市价单”后,由于新功能市价单和主动单相撞,火币网在5月26日至6月2日期间一共出现了5次非正常价格成交单的情况,虽然不影响用户正常价格交易,但给大家带来的困扰,我们深感抱歉。我们认为有必要将整个过程通过技术的角度给大家详细说明,以免大家担心和误解。
火币网的撮合引擎是基于mysql数据库和golang编写,采用了github.com/go-sql-driver/mysql的访问驱动,挂单撤单和撮合都采用最严格的表锁,采用了最保守的串行化事务。这次遇到问题的市价单功能,是在4月中旬左右开始的一个项目,该项目从开发测试到上线前后经历了一个月,在上线前两周多内时间内经过了足够的测试,在测试期间内没有发生过一起此类问题bug。 新上线的市价单撮合功能的逻辑也比较简单,它对于市价单的主要处理过程是在撮合中每遇到一张市价单即锁住订单表并以”id<{市价单id}”为条件查找之前未成交的被动限价单执行撮合过程,撮合产生的成交价格以被动单的报价为准。 按照该撮合逻辑,主动单在行情深度足够的情况下是不会有机会碰到市价单的,因为小于市价单id的主动单已经在之前的普通单撮合中成交全部完成。但根据实际跟踪分析后发现在未知原因的极端随机情况下,上述的查询结果中包含了明显应该在限价单撮合中已经完成撮合但是没有被撮合过的主动单,该主动单没有经历过之前的普通单撮合过程,按照价格优先的撮合原则被优先成交,导致直接撞上了市价单成交由此形成了异常成交记录,比如万元单就是市价卖单id=18829359的订单与报价万元的主动买单 id=18829357成交(其中不连续的订单id18829358是被用户取消的订单)。 这个问题在一周时间内分3天总共发生了5次,每次发生后工程师都加强了对于数据库事务一致性和撮合相关的代码的检查更改,但最终没有完全解决到问题,火币运营部门于6月2日凌晨00:40左右停止了市价单功能 ,并决定在未完全解决这个问题之前暂时不再开放市价单功能. 随后,火币工程师通过历史数据,对已经在测试中的新版撮合引擎进行了大量的压力测试,到目前为止没有发现存在类似的问题,目前暂时怀疑是go的mysql驱动本身或使用方面存在问题,但因该项目缺乏详细的文档和社区帮助无法进一步确认具体原因,我们已经决定上报该bug到github社区,并放弃使用go语言继续升级当前版本的功能。
解决方案: 1、火币网于6月2日凌晨00:40左右停止了“市价单”功能 ,并决定在未完全解决这个问题之前暂时不再开放市价单功能。 2、新版撮合引擎完成测试上线后再重新开启“市价单”功能,同时为了更加安全和稳定,新版撮合引擎改用可靠消息队列实现报价队列和撮合,在提高性能和稳定性的同时避免这类由数据库撮合带来的问题。目前新版撮合引擎已经进入测试阶段,本月内将与大家见面。 3、新版撮合引擎上线后,我们会把旧版的撮合引擎在github上开源,懂技术的朋友一方面可以探讨交流,另外一方面做交易平台的同行们也可以参考(经过实测,此撮合系统虽然不支持市价单,但至少支持每秒500笔的交易处理,可以满足大部分交易所的性能需求)。
最后感谢大家的理解和支持!
原文来自:http://blog.sina.com.cn/s/blog_d05911970101iyib.html |