千寻

道路很长, 开始了就别停下!

0%

Id生成器

资料


单表

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

分库分表

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

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

            graph TD
            A[消费端] -->|Get ID| B(缓存)
B --> C{判断是否超出范围}
C -->|没有超出| A[消费端]
C -->|超出范围| E[生产服务]
E -->F[数据库]
          
  1. 本地引入一个client二方包,当有记录要插入数据库表时,调用nextId方法生成一个id,由于是提前分配的,大多数情况下从本地cache取,如果分配完了,需要从服务器再次申请。
1
2
3
4
private final ConcurrentHashMap<CacheKey, CachedRange> cache = new ConcurrentHashMap<CacheKey, CachedRange>();

// CacheKey:业务场景
// CachedRange:当前批次可用的id区间范围
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}
}
  1. 初始化时或者分配的区间段用完,此时需要从远程服务器申请
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 获取一个可用的IDRange, 结果为闭区间[a, b]
// size:表示一次获取id的区间长度
public IDRange getNextRange(String app, String key, int size) {
synchronized (this) {
IDRange result = new IDRange();
IDRange range = this.getAndUpdate(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 getAndUpdate(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("select Update;", 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;
}