分享

一个生成全局唯一Sequence ID的高并发工厂类 (Java)

 quasiceo 2014-02-02

一个生成全局唯一Sequence ID的高并发工厂类 (Java)

发表于10个月前(2013-03-21 17:26)   阅读(593) | 评论(1 14人收藏此文章, 我要收藏
0

Sequence是数据库系统按照一定规则自动增加的数字序列。这个序列一般作为代理主键(因为不会重复),没有其他任何意义。 

Sequence是数据库系统的特性,有的数据库实现了Sequence,有的则没有。比如Oracle、DB2、PostgreSQL数据库实现Sequence, 而MySQL、SQL Server、Sybase等数据库没有Sequence。

那么如何给一个不支持Sequence的数据库增加支持呢?

下面将给出Java版本的代码。这个代码还实现了对 Sequence ID的高效缓存,并不是每一次调用都会对数据库进行访问,在高并发环境下性能优异。

1. SequenceId.java

01package uuid;
02 
03public class SequenceId {
04    public static final long NOT_FOUND = 0;
05    private static final long STEP = 100;
06    private final SequenceIdProvider provider;
07    private final String name;
08    private final long beginValue;
09    private long value;
10 
11    protected SequenceId(SequenceIdProvider provider, String name, long beginValue) {
12        this.provider = provider;
13        this.name = name;
14        this.beginValue = beginValue;
15        this.value = -1;
16 
17        if (beginValue <= 0) {
18            throw new IllegalArgumentException("begin value must be great than zero.");
19        }
20    }
21 
22    public String getName() {
23        return name;
24    }
25 
26    public synchronized long nextVal() {
27        if (value < 0) {
28            value = provider.load(name);
29            if (value <= NOT_FOUND) {
30                value = beginValue - 1;
31            }
32            provider.store(name, value + STEP);
33        }
34 
35        value++;
36 
37        if (value % STEP == 0) {
38            provider.store(name, value + STEP);
39        }
40 
41        return value;
42    }
43}
2. SequenceIdProvider.java

01package uuid;
02 
03public interface SequenceIdProvider {
04     
05    public SequenceId create(String name);
06     
07    public SequenceId create(String name, long begin);
08     
09    public long load(String name);
10 
11    public void store(String name, long value);
12     
13}
3. JdbcSequenceIdProvider.java

001package uuid;
002 
003import java.sql.*;
004import javax.sql.DataSource;
005 
006public class JdbcSequenceIdProvider implements SequenceIdProvider {
007 
008    private static final String TABLE_NAME = "_SEQUENCE_";
009    private final DataSource dataSource;
010 
011    public JdbcSequenceIdProvider(DataSource dataSource) {
012        this.dataSource = dataSource;
013 
014        confirmTableExists();
015    }
016 
017    @Override
018    public SequenceId create(String name) {
019        return new SequenceId(this, name, 1);
020    }
021 
022    @Override
023    public SequenceId create(String name, long begin) {
024        return new SequenceId(this, name, begin);
025    }
026 
027    private void confirmTableExists() {
028        Connection conn = null;
029        try {
030            conn = dataSource.getConnection();
031 
032            ResultSet rs = conn.getMetaData().getTables(null, null, TABLE_NAME, null);
033            boolean found = rs.next();
034            rs.close();
035 
036            if (!found) {
037                Statement stmt = conn.createStatement();
038                String sql = "create table " + TABLE_NAME + " (name varchar(50) not null, next_val long not null, primary key(name))";
039                stmt.execute(sql);
040                stmt.close();
041            }
042        } catch (SQLException e) {
043            throw new RuntimeException(e);
044        } finally {
045            close(conn);
046        }
047    }
048 
049    private void close(Connection conn) {
050        if (conn != null) {
051            try {
052                conn.close();
053            } catch (SQLException e) {
054                throw new RuntimeException(e);
055            }
056        }
057    }
058 
059    @Override
060    public long load(String name) {
061        long value = SequenceId.NOT_FOUND;
062        Connection conn = null;
063        try {
064            conn = dataSource.getConnection();
065            String sql = "select next_val from " + TABLE_NAME + " where name=?";
066            PreparedStatement ps = conn.prepareStatement(sql);
067            ps.setString(1, name);
068            ResultSet rs = ps.executeQuery();
069            if (rs.next()) {
070                value = rs.getLong(1);
071            }
072            rs.close();
073            ps.close();
074        } catch (SQLException e) {
075            throw new RuntimeException(e);
076        } finally {
077            close(conn);
078        }
079        return value;
080    }
081 
082    @Override
083    public void store(String name, long value) {
084        Connection conn = null;
085        try {
086            conn = dataSource.getConnection();
087            String sql = "update " + TABLE_NAME + " set next_val=? where name=?";
088            PreparedStatement ps = conn.prepareStatement(sql);
089            ps.setLong(1, value);
090            ps.setString(2, name);
091            int updated = ps.executeUpdate();
092            ps.close();
093 
094            if (updated == 0) {
095                sql = "insert into " + TABLE_NAME + " (name, next_val) values (?,?)";
096                ps = conn.prepareStatement(sql);
097                ps.setString(1, name);
098                ps.setLong(2, value);
099                ps.executeUpdate();
100                ps.close();
101            }
102        } catch (SQLException e) {
103            throw new RuntimeException(e);
104        } finally {
105            close(conn);
106        }
107    }
108}
4. GlobalSequenceIdProvider.java

01package uuid;
02 
03import javax.sql.DataSource;
04 
05public class GlobalSequenceIdProvider {
06 
07    private static final SequenceId global;
08    private static final SequenceId table1;
09    private static final SequenceId table2;
10 
11    static {
12        SequenceIdProvider provider = new JdbcSequenceIdProvider(getDataSource());
13        global = provider.create("global");
14        table1 = provider.create("table1");
15        table2 = provider.create("table2", 1000);
16    }
17 
18    public static long nextVal() {
19        return global.nextVal();
20    }
21 
22    public static long nextVal_table1() {
23        return table1.nextVal();
24    }
25 
26    public static long nextVal_table2() {
27        return table2.nextVal();
28    }
29 
30    private static DataSource getDataSource() {
31        // TODO:
32        return null;
33    }
34 
35}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多