生成整数自增ID之ID生成策略

生成整数自增ID之ID生成策略

    使用整数作为索引ID是最优的方式,那么整数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个数)

  单线程 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;//每次生成一万个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);
    }
}




补充

数据库表主键的知识点:

Generator 为每个 POJO 的实例提供唯一标识。

一般情况,我们使用“native”。class 表示采用由生成器接口net.sf.hibernate.id.IdentifierGenerator 实现的某个实例,其中包括:

assigned

主键由外部程序负责生成,在 save() 之前指定一个。

hilo

通过hi/lo 算法实现的主键生成机制,需要额外的数据库表或字段提供高位值来源。

seqhilo

hilo 类似,通过hi/lo 算法实现的主键生成机制,需要数据库中的 Sequence,适用于支持 Sequence 的数据库,如Oracle

increment

主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:不能在集群下使用。

identity

采用数据库提供的主键生成机制。如DB2SQL ServerMySQL 中的主键生成机制。

sequence

采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence

native

 Hibernate 根据使用的数据库自行判断采用 identityhilosequence 其中一种作为主键生成方式。

uuid.hex

 Hibernate 基于128  UUID 算法生成16 进制数值(编码后以长度32 的字符串表示)作为主键。

uuid.string

uuid.hex 类似,只是生成的主键未进行编码(长度16),不能应用在 PostgreSQL 数据库中。

Foreign

使用另外一个相关联的对象的标识符作为主键。


内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://bjspace.net/post/91

相关推荐

发表吐槽

你肿么看?

你还可以输入 250 / 250 个字

嘻嘻 大笑 可怜 吃惊 抛媚眼 调皮 鄙视 示爱 哭 开心 偷笑 嘘 奸笑 委屈 抱抱 Dog 大兵 威武

评论信息框

吃奶的力气提交吐槽中...


既然没有吐槽,那就赶紧抢沙发吧!