av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

淺談?dòng)唵翁柹傻脑O(shè)計(jì)方案

今天討論分享下訂單號生成的簡單實(shí)現(xiàn)方案,為實(shí)際場景中需要用到訂單號生成服務(wù)提供解決思路。

10年積累的做網(wǎng)站、網(wǎng)站建設(shè)經(jīng)驗(yàn),可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有鳳陽免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

最簡單的方式

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

 
 
 
  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 表,進(jìn)行 ID 遞增,并同時(shí)獲取上次更新的值。這里注意,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}
  6.         

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

 
 
 
  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. // 事務(wù)行為,獨(dú)立于外部事物獨(dú)立運(yùn)行
  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.     });
  25. }

稍復(fù)雜一點(diǎn)的方法

上述的方法的問題,想必大家都知道,就是每次獲取 ID 都要調(diào)用數(shù)據(jù)庫,在高并發(fā)的情況下會(huì)對數(shù)據(jù)庫產(chǎn)生極大的壓力,我們的改進(jìn)方法也很簡單,就是一次申請一個(gè)段的 ID,然后發(fā)到內(nèi)存里,每次獲取 ID 先從內(nèi)存里取,當(dāng)內(nèi)存中的 ID 段全部被獲取完畢,則再一次調(diào)用數(shù)據(jù)庫重新申請一個(gè)新的 ID 段。同樣有數(shù)據(jù)庫表的設(shè)計(jì),通過 Name 區(qū)分業(yè)務(wù),用 ID 標(biāo)明已經(jīng)申請到的最大值。當(dāng)然如果是分布式架構(gòu),也可以通過增加步長屬性來實(shí)現(xiàn)。

 
 
 
  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 段的內(nèi)存對象,有兩個(gè)屬性,其中 currentValue 當(dāng)前的使用到的值,endValue 是內(nèi)存申請的最大值。

 
 
 
  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.     }
  17. }

代碼的實(shí)現(xiàn)稍微復(fù)雜一點(diǎn),獲取 ID 會(huì)根據(jù)業(yè)務(wù)標(biāo)識 sequencename,先從內(nèi)存獲取 Step 的 ID 段,如果為 null,則從數(shù)據(jù)庫中讀取當(dāng)前最新的值,并根據(jù)步長計(jì)算 Step,然后返回請求 ID。如果從內(nèi)存中直接獲取到 Step,則直接取 ID,并對 currentValue 進(jìn)行加一。當(dāng) currentValue 的值超過 endValue 時(shí),則更新數(shù)據(jù)庫的 ID,重新計(jì)算 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.     }
  12. if (getNextBlock(sequenceName,step)) {
  13. return step.incrementAndGet();
  14.     }
  15. throw new RuntimeException("No more value.");
  16. }
  17. private boolean getNextBlock(String sequenceName, Step step) {
  18. // "select id from sequence_value where name = ?";
  19.     Long value = getPersistenceValue(sequenceName);
  20. if (value == null) {
  21. try {
  22. // insert into sequence_value (id,name) values (?,?)
  23. value = newPersistenceValue(sequenceName);
  24.         } catch (Exception e) {
  25. value = getPersistenceValue(sequenceName); 
  26.         }
  27.     }
  28. // update sequence_value set id = ?  where name = ? and id = ?
  29.     boolean b = saveValue(value,sequenceName) == 1;
  30. if (b) {
  31.         step.setCurrentValue(value);
  32.         step.setEndValue(value+blockSize);
  33.     }
  34. return b;
  35. }

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

本文小結(jié)

訂單號生成是一個(gè)非常簡單的功能,但是在高并發(fā)的場景下,高性能和高可用就成為了需要關(guān)注的要點(diǎn)。所以,實(shí)際工作中的每一個(gè)小細(xì)節(jié)都值得我們?nèi)ド钏肌?/p>

【本文是專欄作者張開濤的原創(chuàng)文章,作者微信公眾號:開濤的博客,id:kaitao-1234567】


網(wǎng)站題目:淺談?dòng)唵翁柹傻脑O(shè)計(jì)方案
分享地址:http://uogjgqi.cn/article/djhoieo.html
掃二維碼與項(xiàng)目經(jīng)理溝通

我們在微信上24小時(shí)期待你的聲音

解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流