一个简单的数据库分库分表主键ID生成策略

Posted MachineLN

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个简单的数据库分库分表主键ID生成策略相关的知识,希望对你有一定的参考价值。


  • 一些数据量比较大的项目中,如果后台使用单机单表存储数据,则很容易遇到诸如操作缓慢、容量瓶颈和扩展困难等问题。尤其是对响应时间要求比较高的项目,数据库执行的速度直接决定了项目的成败,所以,大部公司对于性能要求比较高的项目都采用了分库分表的策略。虽然分库分表极大的提高了数据库的操作性能,但是进行拆分之后,数据库的操作便不如单表方便,比如如何让多表多库能够最均匀的分担流量、如何保证主键id的唯一性、如何进行事务管理和跨节点的集合及聚合运算等,这些问题现如今都有比较成熟的解决方案,本文即是针对分库和分表后如何生成主键id进行讨论。

  • 提到唯一性,我想大多数人都会想到UUID,也有很多的项目确实是使用UUID作为主键id,但是UUID生成的效率是非常低的,java中UUID的生成依赖系统提供随机数,而系统提供的随机数是源阻塞的,直接造成UUID生成效率低下。另外,当mysql数据库使用innoDB引擎时,在主键是有序的情况下,会使数据库的操作效率显著提高,这是因为innoDB采用b+树的存储结构,而UUID显然是无序的。还有一点也应当引起我们的注意,UUID本生的存储成本很高。

  • 另外一种使用比较普遍的方法就是构建一个Sequence表,该表中只有一个数据,这种方法正好也是我目前的正在开发的一个任务系统中使用的。该项目中当用户领取一个任务的时候,系统就会生成一个含有id(主键id)、userName(用户名)和taskId(任务id)等的记录,在生成该记录的时候会先查询sequence表中的SEQUENCE值,将该值加1之后作为用户任务记录的id插入到数据库中。该表的结构如下图:

  • 为了保证获取ID数值的唯一性,必须要对代码加上同步锁,这里为了减少与数据库的读写次数,建议先从数据库中获取到数值,一个块内的id获取可以放在内存中进行,块读取结束之后再回写到数据库中,这个块不宜设置的太大。

public synchronized long get(String sequenceName) {

   //Step是一个静态内部类,该类中维护这一个块的id

   Step step = stepMap.get(sequenceName);

//判断当前内存中是否存在该id
    if
(step ==null) {
        step = 
new Step(startValue,startValue+blockSize);
        
stepMap.put(sequenceNamestep);
    
else {

//如果已经存在内存中,则直接返回当前id+1的值
        
if (step.currentValue < step.endValue) {
            
return step.incrementAndGet();
        
}
    }

//如果当前的数据块使用完成,则将sequenceValue表中的SEQUENCE值更新为最新的id

//并返回下一个数据块,其中blockSize是数据块的大小
    
for (int i = 0i < blockSizei++) {
        
if (getNextBlock(sequenceName,step)) {
            
return step.incrementAndGet();
        
}
    }
 }

  • 这个方案应对一般的数据量访问没有什么问题,但是它有个很明显的缺陷,即sequence表的访问速度是性能瓶颈,每一次领取任务记录的插入都要同步的获取主键id。那么如何优化这个主键获取的策略呢,下一次将会在此基础上结合flickr团队发表的一种主键生成策略优化当前开发的系统。flicker团队的方案是针对每个表都设置一个初始值,该初始值是连续的,例如我们有4个任务表,那四个表的初始值可以分别设置为1,2,3,4,每个表递增的步长设置为4的倍数,例如递增步长为4,那对于第表1生成的主键id就是1,5,9,…,表2为2,6,10,…表3为3,7,11,…,表4为4,8,12,…,这样就能够保证主键id不重复。

  • 总结:本文目前介绍的方案简单且基础,适合初学者粗略的了解一下,应对一般的分库分表的项目是完全足够的,大家可以自己动手写一个模拟代码。


一个简单的数据库分库分表主键ID生成策略


一个简单的数据库分库分表主键ID生成策略



MachineLN 交流群请扫码加machinelp为好友:

以上是关于一个简单的数据库分库分表主键ID生成策略的主要内容,如果未能解决你的问题,请参考以下文章

数据库分库分表(二)Twitter-Snowflake(64位分布式ID算法)分析与JAVA实现

分库分表的 9种分布式主键ID 生成方案,挺全乎的

ssh注解中主键生成策略无效

分库分表之后,id 主键如何处理

分库分表之后,id 主键如何处理

分库分表之后,id 主键如何处理?