介绍一个建的主键生成策略.
大体思路是这样子的:
程序中免不了要对数据进行新增操作, 而新增数据就需要一个id. 当每次插入数据时,使用数据库直接生成效率不高, 一种比较好的方法是 , 先使用数据库的sql语句(比如存储过程,函数)一次性生成1000条数据, 然后将这1000条数据放到系统缓存中. 每一次新增数据时, 从缓存中取出一条id , 这样就不用每次都从数据库直接生成. 当缓存中的数据不够用时, 我们再调用存储过程或函数, 一次性生成1000条放到缓存中.
有了这个思路, 我们就可以开始了.
我们先从数据库端开始, 因为要用数据库批量的生产主键.
先定义一个主键格式 , 格式为: yyyyMMdd + 12位序列号 (序列号递增,首端用0填充)
好了, 我们开始设计一个函数.
定义一个函数 : A( 参数1: 表名称table, 参数2: 主键数 num)
A() {
if(table中没有数据)
获取今天的日期 yyyyMMdd
生成 日期 + 序列号 的主键id
生成num条
if(table中有数据)
获取表中数据总数sum
获取今天的日期 yyyyMMdd
生成 日期 + (sum+1)开始的主键id
生成num条
返回num条主键id
}
定义一个存储过程, 存储过程调用这个函数A();
到这里, 数据库端我们就设计完成了, 每次调用存储过程, 都能拿到num条主键id .我们只需要传入表名和主键数量, 就能获取该表的新生成的主键
现在我们从程序入手, 看看程序上怎么设计
首先需要定义一个主键生成类,
类中有一个属性 Map<String , Vector> keyCatche , 这是个主键id缓存池, 我们从数据库中获取到的主键id都放在这里面, 是一个map结构的数据, 其中key存的是表名, vector存的是对应吧表生成的id.
我们还要定义一个常量, 代表缓存池的大小. 比如1000. 就代表缓存中每个表一次能存1000条主键id
我们定义一个函数来完成从数据库拿到一批主键的功能
函数GenIds( tableName , idNum)
{
创建数据库链接
调用存储过程(传入tableName , idNum)
得到一批主键
关闭数据库链接
}
我们接着来定义一个函数, 用来从缓存中获取主键id
// 参数1 tableName代表要获取那个表的ID, 参数2 idNum代表要获取多少个, 一般一次一个
函数getId(tableName , idNum)
{
if(keyCatche没有 tableName )
在缓存中新增一个tableName , Vector为空
if(keyCatche存在tableName )
Vector v = keyCatche.get(tableName)
if tableName在缓存中的id大于0
获取当前时间, 格式化时间为 yyyyMMdd , 一共8位 ,代表年月日
获取缓存中第一个id , 截取id的前8位, 和当前时间比较, 看看缓存中的id 是不是今天生成的.
如果不是,将缓存中的id清零 , 我们只要当天的id. (今天插入的id 必须要用今天的日期为id开头)
if v.size < idNum
缓存中的id个数 小于我们希望拿到的id个数 , 我们要从数据库重新去拿.
number = idNum + 1000 - v.size
// 调用我们定义的从数据库获取id的函数
list = GenIds( tableName , number )
//把新生成的id放到表对应的id缓存中
v.add(list)
//从当前缓存中顺序获取idNum个id作为函数返回值
v.get(0 , idNum)
然后从当前缓存中移除0~idNum个id , 就是说用N个id , 就要从缓存中删N个id
}
到这里, 整个设计就完成了.
需要注意的是
这样生成的id 只能保证表内唯一. 因为他的生成规则是 日期+ 递增序列号 , 不同表之间id是有可能一样的.
这样的id生成策略不支持服务的集群部署. 因为id是存在系统缓存中的. 集群部署时每个服务都有一个缓存池, 那样id换乱. 要集群部署, 起码的把id缓存池单独放在一个redis中 , 不能放在系统变量中.