成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java技術干貨分享:淺談訂單號生成設計方案

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

 [[273845]]

簡單的方式

基于數據庫 auto_increment_increment 來獲取 ID。首先在數據庫中創建一張 sequence 表,其中 seq_name 用以區分不同業務標識,從而實現支持多種業務場景下的自增 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 中有事務的方法里,將出現問題,因為數據庫事務開啟會創建一張視圖,在事務沒有提交之前,更新的 ID 還沒有被提交到數據庫中,這在多線程并發操作的情況下,如果事務里的其他方法導致性能慢了,可能出現兩個請求獲取到相同的 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.  
  19. return seq.getId();  
  20. } catch (Exception e) { 
  21. throw new RuntimeException("seq update error.");  
  22.  
  23.  
  24. });  

稍復雜一點的方法

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

同樣有數據庫表的設計,通過 Name 區分業務,用 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.  
  8. public void setCurrentValue(long currentValue) {  
  9. this.currentValue = currentValue;  
  10.  
  11. public void setEndValue(long endValue) {  
  12. this.endValue = endValue;  
  13.  
  14. public long incrementAndGet() {  
  15. return ++currentValue;  
  16.  

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

  1. private Map<String,Step> stepMap = new HashMap<String, Step>();  
  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.  
  14. throw new RuntimeException("No more value.");  
  15.  
  16. private boolean getNextBlock(String sequenceName, Step step) {  
  17. // "select id from sequence_value where name = ?" 
  18. Long value = getPersistenceValue(sequenceName);  
  19. if (value == null) { 
  20. try {  
  21. // insert into sequence_value (id,namevalues (?,?)  
  22. value = newPersistenceValue(sequenceName);  
  23. } catch (Exception e) {  
  24. value = getPersistenceValue(sequenceName);  
  25.  
  26.  
  27. // update sequence_value set id = ? where name = ? and id = ?  
  28. boolean b = saveValue(value,sequenceName) == 1;  
  29. if (b) {  
  30. step.setCurrentValue(value);  
  31. step.setEndValue(value+blockSize); 
  32.  
  33. return b;  

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

本文小結

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

責任編輯:華軒 來源: 今日頭條
相關推薦

2019-08-23 08:09:18

訂單號生成數據庫ID

2025-01-02 09:06:43

2021-07-01 06:58:12

高并發訂單號SCM

2024-10-14 12:05:56

2024-09-04 08:55:56

2020-10-21 12:10:30

訂單號Java代碼

2019-08-16 08:59:33

技術軟件HTML

2021-12-28 06:55:09

事故訂單號績效

2010-09-08 16:17:37

SIP協議棧

2019-09-27 08:18:13

負載均衡核心Key

2009-10-12 16:50:00

2012-07-11 10:49:34

鮑爾默Surface

2009-10-19 13:50:57

布線設計方案

2022-07-05 09:38:47

模型RBACABAC

2009-10-19 14:39:10

2019-03-13 16:09:47

VMware虛擬化服務器

2012-08-21 09:42:24

設計架構設計原則

2009-11-19 15:43:02

路由器設計

2009-02-09 10:41:00

IP城域網設計規劃

2025-03-03 00:45:00

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩在线不卡 | 日韩欧美亚洲 | 97视频免费 | 一区二区三区精品视频 | 一区二区三区四区视频 | 福利国产 | 久久成人免费 | 日韩视频中文字幕 | 成人中文字幕在线观看 | 久久久久国产 | 久久久久免费 | 国产福利在线 | 尤物在线精品视频 | 免费看大片bbbb欧美 | 麻豆91av| 女朋友的闺蜜3韩国三级 | 国产亚洲日本精品 | 毛片一区二区三区 | 九九久久精品 | 日韩在线一区二区三区 | 精品国产欧美一区二区 | 久久精品亚洲精品国产欧美kt∨ | 国产精品观看 | 天天综合干 | 亚洲精品乱码 | 国产一区不卡 | 九九热这里只有精品6 | 午夜国产一级 | 亚洲一二视频 | 成人国产a | 精久久久 | 日韩综合在线播放 | 国产日韩视频 | 波多野结衣在线观看一区二区三区 | 欧美久久一区二区 | 日日干日日操 | 黄免费在线 | 一级片免费视频 | 日日干日日操 | 欧美色综合网 | 久久男人天堂 |