| 
                        副标题[/!--empirenews.page--]
                            
最简单的方式 
基于数据库 auto_increment_increment 来获取 ID。首先在数据库中创建一张 sequence 表,其中 seq_name  用以区分不同业务标识,从而实现支持多种业务场景下的自增 ID, current_value 为当前值, _increment  为步长,可支持分布式数据库的哈希策略。 
- CREATE TABLE `sequence` (  
 - `seq_name` varchar(200) NOT NULL,  
 - `current_value` bigint(20) NOT NULL, 
 - `_increment` int(4) NOT NULL,  
 - PRIMARY KEY (`seq_name`)  
 - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 
 
  
通过 SELECT LAST_INSERT_ID() 方法,更新 sequence 表,进行 ID 递增,并同时获取上次更新的值。这里注意,  current_value = LAST_INSERT_ID(current_value + _increment) 将更新的 ID 赋值给了  LAST_INSERT_ID ,否则返回的将是行 id。 
 
- UPDATE sequence 
 - SET 
 - current_value = LAST_INSERT_ID(current_value + _increment) 
 - WHERE 
 - seq_name = #{seqName} 
 
  
最后 Dao 提供服务,需要提醒的是注意数据库的事务隔离级别,如果将 getSeq() 方法放到 Service  中有事务的方法里,将出现问题,因为数据库事务开启会创建一张视图,在事务没有提交之前,更新的 ID  还没有被提交到数据库中,这在多线程并发操作的情况下,如果事务里的其他方法导致性能慢了,可能出现两个请求获取到相同的 ID,所以解决方法一是不要将  getSeq() 方法放到有事务的方法里,另一种就是将 getSeq() 方法的隔离界别为 PROPAGATION_REQUIRES_NEW  ,实现开启新事务,外层事务不会影响内部事务的提交。 
- @Autowired  
 - private SeqDao seqDao; 
 - @Autowired  
 - private PlatformTransactionManager transactionManager;  
 - @Override  
 - public long getSeq(final String seqName) throws Exception {  
 - TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);  
 - // 事务行为,独立于外部事物独立运行 
 - transactionTemplate 
 - .setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);  
 - return (Long) transactionTemplate.execute(new TransactionCallback() {  
 - public Object doInTransaction(TransactionStatus status) {  
 - try {  
 - Seq seq = new Seq();  
 - seq.setSeqName(seqName);  
 - if (seqDao.update(seq) == 0) { 
 - throw new RuntimeException("seq update failure.");  
 - }  
 - return seq.getId();  
 - } catch (Exception e) { 
 - throw new RuntimeException("seq update error.");  
 - }  
 - }  
 - });  
 - } 
 
  
稍复杂一点的方法 
上述的方法的问题,想必大家都知道,就是每次获取 ID  都要调用数据库,在高并发的情况下会对数据库产生极大的压力,我们的改进方法也很简单,就是一次申请一个段的 ID,然后发到内存里,每次获取 ID  先从内存里取,当内存中的 ID 段全部被获取完毕,则再一次调用数据库重新申请一个新的 ID 段。 
同样有数据库表的设计,通过 Name 区分业务,用 ID 标明已经申请到的最大值。当然如果是分布式架构,也可以通过增加步长属性来实现。 
- CREATE TABLE `sequence_value` (  
 - `Name` varbinary(50) DEFAULT NULL,  
 - `ID` int(11) DEFAULT NULL  
 - ) ENGINE = InnoDB DEFAULT CHARSET = utf8 
 
  
Step 是 ID 段的内存对象,有两个属性,其中 currentValue 当前的使用到的值,endValue 是内存申请的最大值。 
- class Step {  
 - private long currentValue;  
 - private long endValue;  
 - Step(long currentValue, long endValue) {  
 - this.currentValue = currentValue;  
 - this.endValue = endValue;  
 - }  
 - public void setCurrentValue(long currentValue) {  
 - this.currentValue = currentValue;  
 - }  
 - public void setEndValue(long endValue) {  
 - this.endValue = endValue;  
 - }  
 - public long incrementAndGet() {  
 - return ++currentValue;  
 - }  
 - } 
 
  
                                                (编辑:我爱故事小小网_铜陵站长网) 
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! 
                     |