Home | 简体中文 | 繁体中文 | 杂文 | 知乎专栏 | Github | OSChina 博客 | 云社区 | 云栖社区 | Facebook | Linkedin | 视频教程 | 打赏(Donations) | About
知乎专栏多维度架构 微信号 netkiller-ebook | QQ群:128659835 请注明“读者”

14.5. 软件开发

软件的设计都要围绕着灾备进行,不能分布式运行,就不合格。

14.5.1. 交易软件分布式交易

交易软件通常使用TCP私有协议,与我们常见的HTTP协议不同,HTTP是无状态,即用户请求-处理数据-渲染HTML页面-销毁并释放内存,我们很容易开发基于负载均衡的横向水平扩展的应用程序。 而交易软件采用的是持久TCP连接,实时传输用户请求,并将结果会送给用户,中间过程不允许中款,且用户状态数据存储在该交易服务器的内存中,如果我们采用常规手段做负载均衡,就会出现用户被分配到另一台服务器上,而无法继续交易。

我们首先要做的就是将用户状态持久化,有点类似我们在HTTP应用中将Session持久化方法,将这些数据存储到公共的共享服务器上。同时该服务器也需要做高可用等等,保证无单点故障。

第二步,我们解决分布式交易,分布式交易是指可以在交易服务器上任意一个节点上均可交易。方法有很多,复杂程度也不同。

通常解决分布式运行的方法,无非是同步|互斥排他锁|共享存储等等,其实就是两多线程的很多技术扩展到了互联网上。使用节点同步解决共享内存访问问题,使用远程锁解决解决多节点访问问题等等。

14.5.1.1. 分布式交易解决方案一

解决挂单问题,通常交易是即时完成的,较难处理的就是挂单。

下面的方案是最简单的,开发难度最低。

数据库表中增加一个字段例如

-----------------------------------------
Id  | node | status | xxxxx
-----------------------------------------
1 | trader1 | N | xxxxx
2 | trader2 | Y | xxxxx
-----------------------------------------
				

trader1 表示该记录产生在trader1 必须在Trader 1上完成成交

trader2 表示该记录产生在trader2 必须在Trader 2上完成成交

测试用例

  1. Trader1 上登陆,下单,然后退出
  2. 在Trader2 上登陆如果1 | trader1 | xxxxx 没有成交,将node 改为 trader2 同时将挂单数据载入到Trader2服务器的内存中。
  3. Trader1 的中的挂单将失效
  4. 如果登陆Trader2前1 | trader1 | xxxxx 已经成交,就放弃载入内存。
[提示]提示
更新记录状态必须增加node条件
				
Update tab set status=Y where id=1 and node =<my node> …
				
				

14.5.1.2. 分布式交易解决方案一

双向通知的含义是Trader 1状态发生改变会通知Trader 2, 反之Trader 2状态发生改变会通知 Trader 1。

图 14.7. 双向通知解决方案

双向通知解决方案

上图可以看出通知与数据库持久关系图,在交易周期内实时接收通知,一旦收到通知,便根据通知中的指令做出反应,改变当前交易服务器的状态。

这种设计可以采用日志复制,然后重做日志的方式,非常累西数据库同步技术,缺点是可能有延迟。设计需要考虑进去。如果超过三个节点就要采用环形复制

我曾经想过通过IP组播,广播技术实现通知发布,这样就可以不设置目的地地址,同时增加了部署的灵活性,而且对于广播节点数量没有限制。比较适合三个以上的机房部署。

14.5.1.3. 分布式交易解决方案一

消息对列,即将所有交易服务器接入到消息队列中,所有通知均推送进入消息队列,由消息队列服务器统一管理通知消息,同时消息具备持久化。

图 14.8. 消息对列解决方案

消息对列解决方案

这种方法将消息交换MQ去处理,免去了自行开发消息中间件,目前MQ技术也比较成熟。

14.5.2. 交易终端

14.5.2.1. 用户分流

用户分流是指,不同用户登录到不同的服务器上进行交易。因为我们的交易系统是可以水平扩展的,可以有很多台交易服务器并且都是Active状态,任意一台服务器都能登陆交易,我们可以使用哈希算法基于用户ID,将用户分流到指定服务器。

用户分流还可以使用智能DNS技术,将用户分流到距离自己最近的交易服务器上。

还可以根据路由TTL值与Ping值进行优先级分配。

14.5.2.2. 会话保持

交易终端与交易服务器一旦连接,出现超时中断,人为退出,下一次连接还应该上一次连接的那台交易服务器。

如果是交易服务器无法连接将会重新分配一台,并记录配置

如果交易服务器采用的是双向通知,或者消息队列技术,可不必记录登陆服务器,可能按照最小连连接数算法分配服务器。

14.5.3. API 应用程序接口

上文反复提到了API,关于API请参考我的另一边文章 《CVS开发框架》 ,这里不再引用。

图 14.9. CVS开发框架


框架讲述的设计思路,你可以使用你最熟悉语言技术重新实现。

14.5.4. 大数据的问题

我们这个行业不是门户网站,所以数据量不会非常大,但我们要未雨绸缪,我们在设计阶段就将这些问题考虑进去,使将来不会困扰我们。

数据量最大的当属交易数据,有些金融产品双向交易,交易时间长,所以交易数据的累积相当可观。

虽然你可以定时删除,但我不建议。我认为交易数据需要永久保留,为什么? 当交易数据积累到一定数量级,我们就可以从多角度数据挖掘,为决策提供数据支持。

14.5.4.1. 第一个阶分区表

第一个阶段数据分区存储,实施规划是五年之内。五年之内你可以使用分区这种技术存储数据。分许能够保持索引的连续,方便复杂的数据库查询。

14.5.4.2. 第二个阶分库分表

当数据庞大到无论怎么优化都无法提高查询性能是,这时你就要考虑数据库拆分了。

数据库拆分需要遵循几个原则,不仅仅是按照年份或月份分库分表。

数据库拆分

  1. 基于用户拆分,要能保证查询某个用户的数据不需要跨库,也不需要联合多表查询,这样会降低查询效率。
  2. 基于业务拆分,某些业务所用到的表,集中放在一台服务器上,保证数据查询不需要跨库,或者联合多表查询。

图 14.10. 传统的分表方案

传统的分表方案

传统方法,将数据日期切分,这回带来索引的不连续问题,且查询时必须加带日期,以便定位到指定的表中。如果需要做数据挖掘,需要统计四年的数据,必须查询四次,效率大打折扣。

图 14.11. 推荐的分表方案

推荐的分表方案

新的拆分方案,通过哈西算法均匀的将用户分配到指定数据库中或表中。这样所有针对该用户的操作都能在同一个数据库中完成。

图 14.12. 基于功能分表方案

基于功能分表方案

根据不同的功能拆分数据库或表,也是一种非常不错选择。

14.5.5. 数据校对

由于网络传输丢包和延迟等客观情况,当交易数据量比较大且并发请求过多时,可能导致数据无法同步成功或丢失,造成两个交易系统数据不一致。 两个交易数据库的数据不对等,为了解决该问题,可采开发一个数据校对工具,对数据进行比对和修复。确保数据一致性和完整性。