日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯網營銷解決方案
Java技術干貨分享:淺談訂單號生成設計方案

簡單的方式

創(chuàng)新互聯是一家集網站建設,蕭縣企業(yè)網站建設,蕭縣品牌網站建設,網站定制,蕭縣網站建設報價,網絡營銷,網絡優(yōu)化,蕭縣網站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯網需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學習、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網站。

基于數據庫 auto_increment_increment 來獲取 ID。首先在數據庫中創(chuàng)建一張 sequence 表,其中 seq_name 用以區(qū)分不同業(yè)務標識,從而實現支持多種業(yè)務場景下的自增 ID, current_value 為當前值, _increment 為步長,可支持分布式數據庫的哈希策略。

 
 
 
  1. CREATE TABLE `sequence` ( 
  2. `seq_name` varchar(200) NOT NULL, 
  3. `current_value` bigint(20) NOT NULL,
  4. `_increment` int(4) NOT NULL, 
  5. PRIMARY KEY (`seq_name`) 
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8

通過 SELECT LAST_INSERT_ID() 方法,更新 sequence 表,進行 ID 遞增,并同時獲取上次更新的值。這里注意, current_value = LAST_INSERT_ID(current_value + _increment) 將更新的 ID 賦值給了 LAST_INSERT_ID ,否則返回的將是行 id。

 
 
 
  1. UPDATE sequence
  2. SET
  3. current_value = LAST_INSERT_ID(current_value + _increment)
  4. WHERE
  5. seq_name = #{seqName}

最后 Dao 提供服務,需要提醒的是注意數據庫的事務隔離級別,如果將 getSeq() 方法放到 Service 中有事務的方法里,將出現問題,因為數據庫事務開啟會創(chuàng)建一張視圖,在事務沒有提交之前,更新的 ID 還沒有被提交到數據庫中,這在多線程并發(fā)操作的情況下,如果事務里的其他方法導致性能慢了,可能出現兩個請求獲取到相同的 ID,所以解決方法一是不要將 getSeq() 方法放到有事務的方法里,另一種就是將 getSeq() 方法的隔離界別為 PROPAGATION_REQUIRES_NEW ,實現開啟新事務,外層事務不會影響內部事務的提交。

 
 
 
  1. @Autowired 
  2. private SeqDao seqDao;
  3. @Autowired 
  4. private PlatformTransactionManager transactionManager; 
  5. @Override 
  6. public long getSeq(final String seqName) throws Exception { 
  7. TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); 
  8. // 事務行為,獨立于外部事物獨立運行
  9. transactionTemplate
  10. .setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
  11. return (Long) transactionTemplate.execute(new TransactionCallback() { 
  12. public Object doInTransaction(TransactionStatus status) { 
  13. try { 
  14. Seq seq = new Seq(); 
  15. seq.setSeqName(seqName); 
  16. if (seqDao.update(seq) == 0) {
  17. throw new RuntimeException("seq update failure."); 
  18. return seq.getId(); 
  19. } catch (Exception e) {
  20. throw new RuntimeException("seq update error."); 
  21. }); 
  22. }

稍復雜一點的方法

上述的方法的問題,想必大家都知道,就是每次獲取 ID 都要調用數據庫,在高并發(fā)的情況下會對數據庫產生極大的壓力,我們的改進方法也很簡單,就是一次申請一個段的 ID,然后發(fā)到內存里,每次獲取 ID 先從內存里取,當內存中的 ID 段全部被獲取完畢,則再一次調用數據庫重新申請一個新的 ID 段。

同樣有數據庫表的設計,通過 Name 區(qū)分業(yè)務,用 ID 標明已經申請到的最大值。當然如果是分布式架構,也可以通過增加步長屬性來實現。

 
 
 
  1. CREATE TABLE `sequence_value` ( 
  2. `Name` varbinary(50) DEFAULT NULL, 
  3. `ID` int(11) DEFAULT NULL 
  4. ) ENGINE = InnoDB DEFAULT CHARSET = utf8

Step 是 ID 段的內存對象,有兩個屬性,其中 currentValue 當前的使用到的值,endValue 是內存申請的最大值。

 
 
 
  1. class Step { 
  2. private long currentValue; 
  3. private long endValue; 
  4. Step(long currentValue, long endValue) { 
  5. this.currentValue = currentValue; 
  6. this.endValue = endValue; 
  7. public void setCurrentValue(long currentValue) { 
  8. this.currentValue = currentValue; 
  9. public void setEndValue(long endValue) { 
  10. this.endValue = endValue; 
  11. public long incrementAndGet() { 
  12. return ++currentValue; 
  13. }

代碼的實現稍微復雜一點,獲取 ID 會根據業(yè)務標識 sequencename,先從內存獲取 Step 的 ID 段,如果為 null,則從數據庫中讀取當前最新的值,并根據步長計算 Step,然后返回請求 ID。如果從內存中直接獲取到 Step,則直接取 ID,并對 currentValue 進行加一。當 currentValue 的值超過 endValue 時,則更新數據庫的 ID,重新計算 Step。

 
 
 
  1. private Map stepMap = new HashMap(); 
  2. public synchronized long get(String sequenceName) { 
  3. Step step = stepMap.get(sequenceName); 
  4. if(step ==null) { 
  5. step = new Step(startValue,startValue+blockSize); 
  6. stepMap.put(sequenceName, step); 
  7. } else {
  8. if (step.currentValue < step.endValue) { 
  9. return step.incrementAndGet(); 
  10. }
  11. if (getNextBlock(sequenceName,step)) { 
  12. return step.incrementAndGet(); 
  13. throw new RuntimeException("No more value."); 
  14. private boolean getNextBlock(String sequenceName, Step step) { 
  15. // "select id from sequence_value where name = ?"; 
  16. Long value = getPersistenceValue(sequenceName); 
  17. if (value == null) {
  18. try { 
  19. // insert into sequence_value (id,name) values (?,?) 
  20. value = newPersistenceValue(sequenceName); 
  21. } catch (Exception e) { 
  22. value = getPersistenceValue(sequenceName); 
  23. // update sequence_value set id = ? where name = ? and id = ? 
  24. boolean b = saveValue(value,sequenceName) == 1; 
  25. if (b) { 
  26. step.setCurrentValue(value); 
  27. step.setEndValue(value+blockSize);
  28. return b; 
  29. }

使用該方法獲取 ID 可以減少對數據庫的訪問量,以降低數據庫的壓力,但是同樣需要注意,獲取 ID 同樣關注數據庫事務問題,因為當系統(tǒng)重啟的時候,stepMap 為 null,所以會取數據庫查詢當前 ID,更計算更新 Step,然后更新數據庫的 ID。如果該方法被放到數據庫事務里,由于其他方法性能慢了,導致查詢之后沒有及時更新,并發(fā)情況下另一個線程查詢的時候,可能會獲取到該線程未提交的 ID,因而出現兩個線程獲取到相同的 ID 問題。

本文小結

訂單號生成是一個非常簡單的功能,但是在高并發(fā)的場景下,高性能和高可用就成為了需要關注的要點。所以,實際工作中的每一個小細節(jié)都值得我們去深思。


新聞名稱:Java技術干貨分享:淺談訂單號生成設計方案
本文路徑:http://www.5511xx.com/article/dhhhjeh.html