在集群的環境中,有這種場景
需要整數自增ID,這個整數要求一直自增,並且需要保證唯一性.
Web服務器集群調用這個整數生成服務,然后根據各種規則,插入指定的數據庫.
一般來說,整數自增可以通過幾個方式實現.
1.MySQL 單獨建一個表,使用Auto_increment特性.
如果需要生成ID,則Insert一個記錄,然后獲取last_insert_id()得到ID值
這種方式的優點是簡單,而且比較快.
缺點是這個表會越來越大,需要定期進行清理.
2.Oracle 序列
優點很明顯,足夠快,而且不占用空間.
缺點..你需要有Oracle
3.mysql 單行更新自增
需要生成ID的時候,進行如下調用
以上三種數據庫方式的效率對比如下(都是測試的虛擬環境,作為趨勢參考,數值是每秒生成的ID個數)
4.使用Redis自增
使用兩個Redis實例,一個分發奇數ID,一個分發偶數ID
任何一個Redis損壞,都可以切換到另外一個Redis實例.
5.使用程序模擬序列
下面的ID生成服務,初始化先從數據庫拿到一段ID,然后分發。
一旦ID耗盡,再從數據庫獲取一段ID。
可以啟動多個ID生成服務,避免單點故障.
ID生成服務 本身應該串行化,避免鎖競爭.
使用這種自定義序列,1百萬ID的生成時間是14s.效果非常明顯.

Web服務器集群調用這個整數生成服務,然后根據各種規則,插入指定的數據庫.
一般來說,整數自增可以通過幾個方式實現.
1.MySQL 單獨建一個表,使用Auto_increment特性.
- CREATE TABLE `test` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8

這種方式的優點是簡單,而且比較快.
缺點是這個表會越來越大,需要定期進行清理.
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 |
使用兩個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); } }