分布式架构下序列实现

在最近的项目中,因为序列的问题,造成部分数据丢失,因此仔细思考了下载分布式环境下如何使用序列问题!

下面分几类情况说明下。

一、 数据量较小的应用

面对数据量较少的应用单个数据库既可以解决问题,那么针对序列来说,依靠数据库本身的序列既可以实现。(mysql此类没有序列的数据库,一样有对应的方法来实现类似序列的功能)

二、 大量数据的应用

针对数据量比较大的情况,多数企业对数据库的选择,最主要的需求就是方便扩充,目前对此类的实现是分库分表数据库! 另外为提高效率,多数还会同时使用读写分离,即吧不常用的参数等单独放在一个数据库中,另把实例数据放在另外一个数据库中!

在这种情况下使用序列会存在哪些问题呢?

1: 分库分表,或者读写分离面临跨库事物问题!

2: 重复问题!

解决方案:

通过缓存服务来实现

这种情况不建议走数据库序列,可以使用缓存,如redis,redis提供方法可以直接获取值,并且原值加1 incr 方法 redis为单线程执行,固改方法不会造成脏读,脏写

public int getNextValue(String sequenceId) throws Exception{

// redisTemplate.incr()

int nextValue = (int)((Math.random()*9+1));

//生成四位随机数

Long value = redisTemplate.incr(sequenceId+"_SEQUENCE");

JSONObject sequenceJson = JSONObject.parseObject(redisTemplate.get("PARAM_CONFIG_SEQUENCE_KEYZZ").toString());

JSONObject sequenceKey = JSONObject.parseObject(sequenceJson.getString("SEQUENCE_KEY"));

String valueRule = sequenceKey.getString(sequenceId);

Long valueMix = Long.parseLong(valueRule.split("-")[1]);

if(value>=valueMix){

value =Long.parseLong( valueRule.split("-")[0]);

redisTemplate.set(sequenceId+"_SEQUENCE",value.toString());

}

return value.intValue();

}

线程池加载序列

基本思想:

1: 使用线程池批量执行异步任务

2: 使用 FutureTask异步执行获取单个序列()

3: 使用ConcurrentLinkedQueue 缓存序列

 

private void _getSeq16() throws Exception {

long START_TIME = System.currentTimeMillis();

int CACHE_SIZE = Integer.valueOf(this.disConfig.getProperty("config.sequence.cacheSize", "100")).intValue();

int THREADS = StringUtils.isEmpty(this.disConfig.getProperty("config.sequence.threads")) ? Integer.valueOf(this.disConfig.getProperty("config.sequence.threads")).intValue() : Runtime.getRuntime().availableProcessors();

ExecutorService executorService = Executors.newFixedThreadPool(THREADS * 2);

List<FutureTask<String>> futureTaskList = new ArrayList();

this.sequenceCacheQueue.add(this.getSingleSequence());

List<String> tempSequenceList = new ArrayList();

this.logger.debug("get first sequence cost:" + (System.currentTimeMillis() - START_TIME) + "ms");

 

FutureTask futureTask;

for(int i = 0; i < CACHE_SIZE - 1; ++i) {

futureTask = new FutureTask(new Callable<String>() {

public String call() throws Exception {

return SequenceUtil.this.getSingleSequence();

}

});

futureTaskList.add(futureTask);

executorService.submit(futureTask);

}

 

Iterator i$ = futureTaskList.iterator();

 

while(i$.hasNext()) {

futureTask = (FutureTask)i$.next();

String sequenceStr = (String)futureTask.get();

if (!StringUtils.isEmpty(sequenceStr)) {

tempSequenceList.add(sequenceStr);

}

}

executorService.shutdown();

Collections.sort(tempSequenceList, new Comparator<String>() {

public int compare(String o1, String o2) {

return Long.parseLong(o1) > Long.parseLong(o2) ? 1 : -1;

}

});

i$ = tempSequenceList.iterator();

 

while(i$.hasNext()) {

String sequence = (String)i$.next();

this.sequenceCacheQueue.add(sequence);

}

 

this.logger.debug("get sequence size " + this.sequenceCacheQueue.size() + ",total execute time:" + (System.currentTimeMillis() - START_TIME) + "ms");

}

序列中的误区:

固定格式+随机数 方式

这种虽然实现起来简单,但是重复几率很高,哪怕固定格式是时间串

单机序列方式

单机应用的情况下没有问题,但是在分布式应用中,两天主机之前,存在重复

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值