高性能、支持集群的单据号生成工具

企业应用中为单据分配单据号是经常会遇到的问题。这次要给大家介绍的Rumba Sequence可以用来生成单据号,它最大的优点是具有极高的性能(达到单机每秒生成几十万个单据号以上)。同时它又是为集群的应用服务器架构环境设计,也就是说支持在同一个集群中的多个服务器同时分配单据号,由此可以达到更高的性能。

Why

传统分配单据号会采用如下算法:首先查询单据表找到最大的单据号,然后在此基础上流水,得到新的单据号。这种算法带来的最大优点是能够最大程度确保单据号的顺序性,并且一般不会发生跳号。所谓顺序性,就是单据号的顺序即代表单据的发生顺序。但是,与此同时带来的是性能的损失,尤其是当遇到需要在短时间内大量产生单据时,比如在一个很短的时间内产生上万甚至更多的单据。会带来多大的性能损失呢?在一个真实事例中,为生成单据号消耗了整个处理的40%的时间。真是不测不知道,一测吓一跳!

导致问题的关键在于算法中“取得当前最大的单据号”,这显然是一个“阻塞(无法并行)”的操作。也就是说在同一个时刻,最多只有一个线程执行这样的操作,其它线程无论多少只好排队等待。而且上述处理的性能,还会随着数据表变大而变得越来越差。

另一方面为了能够提到处理的性能,除了优化算法外,剩下的手段就是设法并行地计算。因此如果存在这样频繁且长时间的阻塞的处理,就会严重制约整体性能的提高。这是无法通过提供更好或更多的硬件能够解决的了。

How

所以解决问题的关键有两点:一、尽可能减少阻塞操作发生的频率;二、即便发生阻塞,也尽块地释放资源。Rumba Sequence对此的解决方案,一方面仍然采用以数据库为核心,另一方面采用缓存算法。采用缓存算法可以减少访问共享的数据库资源,以减少阻塞操作发生的频率。另一方面采用专用的数据表存放状态数据,这样针对同一种单据只需要占据其中一条数据记录,从而避免单据量变大而导致性能下降,也就做到了阻塞时间尽可能的短。此外为了进一步提高数据库处理的性能,工具将其设计为独立事务。这样避免了由于同事务的其它业务没有提交,而导致发生阻塞时间变长。

带来的问题

无论是采用缓存算法,还是独立事务,都会带来一点“副作用”。(这样你还好意思推荐?别急,且听我分析……)

还记得前面对传统算法的优点分析么?首先是单据号的顺序性。当被运用在集群环境下多个服务器,每个服务器当第一次接受请求时,会一次性向数据库取出一批单据号,没有用掉的保留在内存中,直到内存中的单据号被用完,再次向数据库申请。由于在此期间服务器之间不会进行任何形式的通讯,也就是说是各自分配。这样也就无从确保按照顺序分配了。

另一个问题就是“跳号”了?当服务器缓存中存在没有分配掉的单据号时,服务器被重新启动。原有缓存中的单据号尽管没有被分配,但从数据库的角度看仍然已经被分配。因为服务器重启,又会重新向数据库申请新的单据号。这样就出现部分单据号丢失的情况。

然而,相比性能的提升,这些“副作用”真的很重要么?你的用户真的很在意这两个特性么?也许是的……,不过我不记得在我经历的项目中,用户把这两个特性列为必选。

这里透露一个“小技巧”,如果你将缓存大小设置为1,那么就可以有效地避免这两个副作用,而同时又不会出现随着数据量增大而性能下降的问题。

还可以做得更多

在此之前,我们一直讨论的单据号的话题,其实Rumba Sequence并不仅仅支持生成单据号。从它的名称上就可以看出,“Sequence”是顺序、次序的意思。如果你曾经使用过Oracle数据库提供的sequence的话,你可以认为这就是个Java版的sequence。

它的核心就是提供了一个顺序发生器,其返回值始终是长整型。单据号是在其基础上扩展而得,除了单据号以外,还提供了生成商品流通条形码的支持。也就是说,如果你愿意还可以在此基础上扩展出特定用途的编码,同时你仍然可以享受到高性能,以及支持集群的好处。

猜你喜欢

转载自blog.csdn.net/qweqwruio/article/details/81363766
今日推荐