做者使用了很巧妙的方法,讓分佈式的系統產生惟一的ID。項目依賴於MySQL的事務性以及行級鎖。java
首先須要幾個數據庫,每一個DB中建立幾張表。用來存儲ID,框架對錶中的數據進行selec和update操做。個人理解是數據庫和表的數量因分佈式系統數量和事務數量多少自行設計。每一個數據庫對應一個DBConfig。表中存的數據大體爲name value,name即應用系統名,value是ID。算法
項目的核心以下:數據庫
1.更新ID框架
首先取消自動提交事務分佈式
connection.setAutoCommit(false);
更新事務的第一步是查出當前的ID值測試
String querySql = "select value from sequence" + tableIndex + " where name='" + sequenceName + "' for update;"; statement = connection.createStatement(); queryResult = statement.executeQuery(querySql);
此處select for update必須配合InnoDB引擎以及數據庫事務使用,做用是鎖住表一行數據,防止其餘事務進行update操做。接着計算新的值:設計
queryValue = queryValue + step * (dbSize * tableSize);
step:步長,dbSize:數據庫數量,tableSize:表數量。意味着每一個表中存放的都是沒有交集的ID。例如dbSize=2,tableSize=2,step=100,當value==1時,表明1-400這些ID都屬於該系統。引入step的目的是減小系統與數據庫之間的IO操做,造成一個cache,相似內存調度中的遷移整個塊到內存。後面也會看到。接着更新數據:code
String updateSql = "update sequence" + tableIndex + " set value= " + queryValue + " where name='" + sequenceName + "';"; int result = statement.executeUpdate(updateSql);
最後提交事務,完成ID值更新對象
connection.commit();
2. 「cache」隊列
前面提到了這個思想,其實現很簡單,
if ((nextValue - startValue) % step == 0) { nextValue = updateValue(); } nextValue = nextValue + 1; return nextValue - 1;
uodateValue()就是上面的過程
3.隊列
已經有了多個數據庫、多張表,那麼應用系統應該如何使用呢?
BlockingQueue<SequenceTable> sequenceTableQueue = new ArrayBlockingQueue<>(databaseConfigs.size() * tableSize);
在init過程當中建立一個阻塞隊列,大小就是表的總數量,將每一個sequence表對象放入隊列,接下來的操做就顯而易見了,當須要獲取新的ID是,poll隊列,拿到下一個sequenceTable中的值,若是達到step這一臨界值,作update操做,如上。拿到了惟一ID後,將table put回隊列。實現以下:
SequenceTable sequenceTable = pollFreeConnectionQueue(); if (null == sequenceTable) { throw new SequenceNoFreeConnectionException("there is no free connection! idName=" + idName); } //獲取值 long nextValue = sequenceTable.nextValue(); //釋放鏈接 putFreeConnectionQueue(sequenceTable); return nextValue;
以上就是框架的核心思想和實現。其思想和算法比較清楚。經過建立多數據庫、多表;以及step步長減輕數據庫的壓力,下降了鎖的觸發頻率,進而提升tps。若是要上線的話,須要根據公司內部事務數量測試選擇幾個數據庫幾張表,step的大小,以達到最高的效率。很好的框架,點個贊!