生成整数自增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值

    
    这种方式的优点是简单,而且比较快.
    缺点是这个表会越来越大,需要定期进行清理.

    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生成服务本身应该串行化,避免锁竞争.

  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. import java.sql.ResultSet;
  4. import java.sql.Statement;
  5. import java.util.concurrent.Callable;
  6. import java.util.concurrent.ExecutionException;
  7. import java.util.concurrent.ExecutorService;
  8. import java.util.concurrent.Executors;
  9. import java.util.concurrent.Future;

  10. public class SeqGenerator {
  11.     private static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

  12.     private static int currentVal = -1;
  13.     private static int maxVal = -1;
  14.     private static int fetchSize = 10000;

  15.     static{
  16.         try {
  17.             fetchFromDB();
  18.         } catch (Exception e) {
  19.             // TODO Auto-generated catch block
  20.             e.printStackTrace();
  21.         }
  22.     }
  23.     
  24.     public static int getSeq() throws InterruptedException, ExecutionException {
  25.         Callable<Integer> c = new Callable<Integer>() {
  26.             @Override
  27.             public Integer call() throws Exception {
  28.                 int result = currentVal;
  29.                 if (currentVal > maxVal) {
  30.                     fetchFromDB();
  31.                     result = currentVal;
  32.                 }
  33.                 currentVal++;
  34.                 return result;

  35.             }
  36.         };
  37.         Future<Integer> task = singleThreadExecutor.submit(c);
  38.         return task.get().intValue();
  39.     }

  40.     private static void fetchFromDB() throws Exception {
  41.         Class.forName("com.mysql.jdbc.Driver");
  42.         Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "xx", "xx");
  43.         connection.setAutoCommit(false);
  44.         Statement st = connection.createStatement();
  45.         ResultSet rs = st.executeQuery("select * from test for update");
  46.         rs.next();
  47.         currentVal = rs.getInt(1) + 1;
  48.         rs.close();
  49.         st.executeUpdate("update test set id=id+" + fetchSize);
  50.         rs = st.executeQuery("select * from test for update");
  51.         rs.next();
  52.         maxVal = rs.getInt(1);
  53.         connection.commit();
  54.         rs.close();
  55.         st.close();
  56.         connection.close();
  57.     }

  58.     public static void main(String[] args) throws Exception {
  59.         int i = 1000000;
  60.         long start = System.currentTimeMillis();

  61.         while (i > 0) {
  62.             System.out.println(SeqGenerator.getSeq());
  63.             i--;
  64.         }
  65.         long end = System.currentTimeMillis();
  66.         System.out.println(end - start);
  67.     }
  68. }
    使用这种自定义序列,1百万ID的生成时间是14s.效果非常明显.

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/29254281/viewspace-1811711/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/29254281/viewspace-1811711/

你可能感兴趣的:(生成整数自增ID(集群主键生成服务))