在集群的环境中,有这种场景
需要整数自增ID,这个整数要求一直自增,并且需要保证唯一性.
Web服务器集群调用这个整数生成服务,然后根据各种规则,插入指定的数据库.
一般来说,整数自增可以通过几个方式实现.
1.MySQL 单独建一个表,使用Auto_increment特性.
-
CREATE TABLE `test` (
-
`id` int(11) NOT NULL AUTO_INCREMENT,
-
PRIMARY KEY (`id`)
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8
如果需要生成ID,则Insert一个记录,然后获取last_insert_id()得到ID值
这种方式的优点是简单,而且比较快.
缺点是这个表会越来越大,需要定期进行清理.
2.Oracle 序列
优点很明显,足够快,而且不占用空间.
缺点..你需要有Oracle
3.mysql 单行更新自增
需要生成ID的时候,进行如下调用
以上三种数据库方式的效率对比如下(都是测试的虚拟环境,作为趋势参考,数值是每秒生成的ID个数)
|
单线程
|
5线程
|
10线程
|
20线程
|
|
MySQL Auto_increment
|
340-390
|
277
|
229
|
178
|
|
Oracle序列
|
714
|
555
|
454
|
454
|
|
MySQL 单行更新
|
303
|
136
|
66
|
19
|
4.使用Redis自增
使用两个Redis实例,一个分发奇数ID,一个分发偶数ID
任何一个Redis损坏,都可以切换到另外一个Redis实例.
5.使用程序模拟序列

下面的ID生成服务,初始化先从数据库拿到一段ID,然后分发。
一旦ID耗尽,再从数据库获取一段ID。
可以启动多个ID生成服务,避免单点故障.
ID生成服务本身应该串行化,避免锁竞争.
-
import java.sql.Connection;
-
import java.sql.DriverManager;
-
import java.sql.ResultSet;
-
import java.sql.Statement;
-
import java.util.concurrent.Callable;
-
import java.util.concurrent.ExecutionException;
-
import java.util.concurrent.ExecutorService;
-
import java.util.concurrent.Executors;
-
import java.util.concurrent.Future;
-
-
public class SeqGenerator {
-
private static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
-
-
private static int currentVal = -1;
-
private static int maxVal = -1;
-
private static int fetchSize = 10000;
-
-
static{
-
try {
-
fetchFromDB();
-
} catch (Exception e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
}
-
-
public static int getSeq() throws InterruptedException, ExecutionException {
-
Callable<Integer> c = new Callable<Integer>() {
-
@Override
-
public Integer call() throws Exception {
-
int result = currentVal;
-
if (currentVal > maxVal) {
-
fetchFromDB();
-
result = currentVal;
-
}
-
currentVal++;
-
return result;
-
-
}
-
};
-
Future<Integer> task = singleThreadExecutor.submit(c);
-
return task.get().intValue();
-
}
-
-
private static void fetchFromDB() throws Exception {
-
Class.forName("com.mysql.jdbc.Driver");
-
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "xx", "xx");
-
connection.setAutoCommit(false);
-
Statement st = connection.createStatement();
-
ResultSet rs = st.executeQuery("select * from test for update");
-
rs.next();
-
currentVal = rs.getInt(1) + 1;
-
rs.close();
-
st.executeUpdate("update test set id=id+" + fetchSize);
-
rs = st.executeQuery("select * from test for update");
-
rs.next();
-
maxVal = rs.getInt(1);
-
connection.commit();
-
rs.close();
-
st.close();
-
connection.close();
-
}
-
-
public static void main(String[] args) throws Exception {
-
int i = 1000000;
-
long start = System.currentTimeMillis();
-
-
while (i > 0) {
-
System.out.println(SeqGenerator.getSeq());
-
i--;
-
}
-
long end = System.currentTimeMillis();
-
System.out.println(end - start);
-
}
-
}
使用这种自定义序列,1百万ID的生成时间是14s.效果非常明显.
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29254281/viewspace-1811711/
,如需转载,请注明出处,否则将追究法律责任。