## id生成器

---

如果单表，可以借助于mysql自带的id生成器每次自增+1的方式来生成主键id。

如果分库分表，需要提前在外部生成id，然后将记录插入到对应的分表中。


**其实原理很简单，只需实现一个id批量生成查询器即可，大概步骤：**

a）本地引入一个client二方包，当有记录要插入数据库表时，调用nextId方法生成一个id，由于是提前分配的，大多数情况下从本地cache取，如果分配完了，需要从服务器再次申请。


```
 private final ConcurrentHashMap<CacheKey, CachedRange> cache = new ConcurrentHashMap<CacheKey, CachedRange>();
 
 CacheKey：业务场景
 CachedRange：当前批次可用的id区间范围
 ```
 
 ```
  public long nextId(String app, String key) {
        synchronized (this){
            CacheKey cacheKey = new CacheKey(app, key);
            CachedRange cachedRange = this.cache.get(cacheKey);
            if (cachedRange == null || cachedRange.range.getEnd() < cachedRange.pos) {
                IDRange range = this.service.getNextRange(app, key, this.size);
                cachedRange = new CachedRange(range, range.getStart()) ;
            }
            long pos = cachedRange.pos;
            cachedRange.pos += 1;
            this.cache.put(cacheKey, cachedRange);
            return pos;
        }
    }
    
size：表示一次获取id的区间长度
    
```

b）初始化时或者分配的区间段用完，此时需要从远程服务器申请


```
获取一个可用的IDRange, 结果为闭区间[a, b]

public IDRange getNextRange(String app, String key, int size) {

        synchronized (this) {
            IDRange result = new IDRange();
            IDRange range = this.get4Update(app, key, size * this.PRE_ALOCATE_BATCH_NUM);
            result.setApp(app);
            result.setKey(key);
            result.setStart(range.getStart());
            result.setEnd(range.getEnd());

            this.logger.info("return range: {}", result);
            return result;
        }
    }
```

```
// 数据库查询

 @Transactional(value="crtTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public IDRange get4Update(String app, String key, int size) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("app", app);
        params.put("key", key);
        SqlSession sqlSession = this.commonSqlSessionTemplate.getSqlSessionFactory()
                .openSession();
        UniversalId universalId = sqlSession.selectOne("select4Update", params);
        if (universalId == null) {
            return null;
        }

        IDRange range = new IDRange();
        range.setApp(app);
        range.setKey(key);
        range.setStart(universalId.getValue() + 1);
        range.setEnd(universalId.getValue() + size - 1);

        universalId.setValue(universalId.getValue() + size);
        sqlSession.update("updateValue", universalId);
        return range;
    }
```
