生成整数自增ID(集群主键生成服务)

    在集群的环境中,有这种场景
    需要整数自增ID,这个整数要求一直自增,并且需要保证唯一性.
生成整数自增ID(集群主键生成服务)

Web服务器集群调用这个整数生成服务,然后根据各种规则,插入指定的数据库.
    
    一般来说,整数自增可以通过几个方式实现.
    1.MySQL 单独建一个表,使用Auto_increment特性.

  1. CREATE TABLE `test` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. PRIMARY KEY (`id`)
  4. ) ENGINE=InnoDB DEFAULT CHARSET=utf8

如果需要生成ID,则Insert一个记录,然后获取last_insert_id()得到ID值
生成整数自增ID(集群主键生成服务)
    
    这种方式的优点是简单,而且比较快.
    缺点是这个表会越来越大,需要定期进行清理.

2.Oracle 序列
    优点很明显,足够快,而且不占用空间.
    缺点..你需要有Oracle

3.mysql 单行更新自增
生成整数自增ID(集群主键生成服务)
    需要生成ID的时候,进行如下调用
生成整数自增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生成服务,避免单点故障.
    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;//每次生成一万个id 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) {//如果当前值>数据库最大值,重新生成id
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);//更新db中最大值
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.效果非常明显.

上一篇:CSS选择符详解之关系选择符篇


下一篇:linux 3.4.103 内核移植到 S3C6410 开发板 移植失败 (问题总结,日本再战!)