如果单表,可以借助于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;
}