数据库sequence序列高速缓存池——尽量减少排队和数据库的交互

获取数据库序列

林小应

1.问题描述

由于业务需要,应用程序中很多地方会用到数据库序列sequence,如果每次都去数据库获取,会比较耗时。
在Java代码中,我们通常调用存储过程或执行sql生成一个sequence,
select xxx.nextnvl from dual;
如果直接调用存储过程或执行sql取sequence,算上事务开销,一次需要3~4ms。在大型分布式系统中(如江苏电信的营业系统、淘宝网、大型社交网站),4ms将是一个很大的开销,试想如果需要的序列已经存储在于内存中,平均每次取sequence的时间损耗将低于0.01ms。

2.期望

避免每次前往数据库取序列,让获取sequence就像从内存中取一个对象一样简单、决不允许重复读取。

3.设计原理

获取sequence的Java代码类似如下:

     String seq = generateSeq(String seqType);

输入参数:seqType

输出参数:sequence

我的思路是:在内存中开辟一个大容器(HashMap),存放各种seqType的sequence队列(Queue)。由一个守护线程来完成队列的装填。
当第一次取某种类型的sequence,在容器中注册一下,记录下seqType。这样守护线程就能察觉到,守护线程不断检测所有类型的sequence队列。如果容量不足,就会启动装填机制,从数据获取一批新的sequence将空队列装满。

4. 实现

具体实现过程需要保证,多线程环境中资源锁的问题。这里我们用的HashMapConcurrentHashMap,是一种线程安全的高性能Map。Map中的seqType所对应的“sequence队列”,已经换成了队列组、即一个ArrayList中存放多个sequence队列。这样主要是为了降低并发带来的开销。

根据入参seqTypeA,首先取到的是一个ArrayList<ConcurrentLinkedQueue>,再根据ArrayList的size随机一个整数cur,再取ArrayList中第cur个sequence队列。并发压力和size的大小成反比!

示意图:

5. JVM序列池的生命周期

序列池的生命周期为:第一次注册 到 应用程序终止

6. 需要注意的问题

A. 不能读到脏数组,一个sequence不能被两次读到。上面我们的sequence都是从数据库来,保存在同步队列ConcurrentLinkedQueue中,这一点可以保证。

B. sequence的顺序,如果从数据库直接读取顺序是一致的。采用sequence池机制,可能后面读到的比前面读到的小/大。我们可以将上面的ArrayList的size设置成1就可以解决顺序问题(不使用于分布式环境)。

C.命中率问题,如果sequence池空了,主线程就会直接前往数据库。为了提高命中率,我们的系统中采用了备用池——主池空了以后,立即从备用池拉一个现成的队列替换当前队列(这个非常有效)。

D.灵活的配置,如队列池的深度(队列的长度)、宽度(ArrayList的size)、实时的开关机制等

7. 总结

以上只能简单说一下,我们对于这种频繁获取sequence情况的解决方案。我们的系统是40000000用户,分布式系统,多数据库。采用了sequence池机制以后,获取sequence的时间大大缩短了。

还有很多细节,大家动手做的时候再慢慢琢磨。

qq:346420558

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值