Springboot3利用redis生成唯一訂單號的實現(xiàn)示例
生成訂單號
生成唯一訂單號的方法有很多種,包括uuid,雪花算法等等,還可以利用數(shù)據(jù)庫的約束生成唯一的id,比如自增,但是數(shù)據(jù)庫的性能比較低,如果使用數(shù)據(jù)庫來生成訂單號效率比較低。我們可以考慮使用redis來生成唯一的訂單號。redis天然單線程,又支持lua腳本原子性操作。所以很好實現(xiàn)這些功能。
代碼實現(xiàn)
import jakarta.annotation.Resource; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; import java.util.Collections; @RestController public class distr_lock { @Resource private RedisTemplate<String,String> redisTemplate; @GetMapping("/order") public void order() { for (int i = 0; i < 50; i++) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 20; i++) { String s = generateOrderId(); System.out.println(s); } } }).start(); } } /** * 我們可以采用這樣一種策略, * 時間精確到秒+7位數(shù)的自增數(shù)字 * 這樣可以實現(xiàn)同一個用戶在同一秒下單的不超過1000萬單 * 然后使用時間在redis創(chuàng)建一個key,進行自增,然后30秒過期 */ public String generateOrderId(){ DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendValue(ChronoField.YEAR, 4) // 年份,4位數(shù)字 .appendValue(ChronoField.MONTH_OF_YEAR, 2) // 月份,2位數(shù)字 .appendValue(ChronoField.DAY_OF_MONTH, 2) // 日期,2位數(shù)字 .appendValue(ChronoField.HOUR_OF_DAY, 2) // 小時,2位數(shù)字 .appendValue(ChronoField.MINUTE_OF_HOUR, 2) // 分鐘,2位數(shù)字 .appendValue(ChronoField.SECOND_OF_MINUTE, 2) // 秒,2位數(shù)字 .toFormatter(); // 獲取當前時間的字符串表示,不使用-或:作為分隔符 String currentDateTime = LocalDateTime.now().format(formatter); // Long count = getAndExpireCounter(currentDateTime); Long count = getAndExpireCounterByLua(currentDateTime); String format = String.format("%07d", count); return currentDateTime + format; } public Long getAndExpireCounter(String key) { // 獲取Redis連接,以便執(zhí)行事務 Long increment = 0L; increment = redisTemplate.opsForValue().increment(key); if (increment!=null && increment == 1) { // 設置過期時間,這里并沒有使用事務或者lua腳本 // 因為我覺得上面的自增操作是原子性的,也就是肯定會得到一個值為1 // 如果說redis服務崩了,到這一步?jīng)]有設置好過期時間,那么我們整個 // 服務也會崩潰,這個key就會一直存在于服務器,會出現(xiàn)這個問題,為了嚴謹 // 最好使用lua腳本編寫,項目上線會保證更可靠的效果 redisTemplate.expire(key, 30, java.util.concurrent.TimeUnit.SECONDS); } // 獲取自增后的值 return increment; } /** * 整個腳本的作用是: * 1.檢查一個鍵是否存在于Redis中。 * 2.如果鍵不存在,則設置該鍵的值為1,并為其設置60秒的過期時間,然后返回1。 * 3.如果鍵已經(jīng)存在,則將其值增加1,并返回增加后的新值。 * Redis執(zhí)行Lua腳本時是原子的,這意味著在執(zhí)行這個腳本的過程中, * 不會有其他腳本或命令同時修改這個鍵,從而保證了操作的原子性和一致性。 * @param key * @return */ public Long getAndExpireCounterByLua(String key) { // 定義Lua腳本 String script = "if redis.call('EXISTS', KEYS[1]) == 0 then " + " redis.call('SET', KEYS[1], 1) " + " redis.call('EXPIRE', KEYS[1], 30) " + " return 1 " + "else " + " return redis.call('INCR', KEYS[1]) " + "end"; // 使用Redis的腳本執(zhí)行功能 DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText(script); redisScript.setResultType(Long.class); // 執(zhí)行腳本 return redisTemplate.execute(redisScript, Collections.singletonList(key)); }
到此這篇關于Springboot3利用redis生成唯一訂單號的實現(xiàn)示例的文章就介紹到這了,更多相關Springboot3 redis生成唯一訂單號內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
IntelliJ IDEA 2020 安裝和常用配置(推薦)
這篇文章主要介紹了IntelliJ IDEA 2020 安裝和常用配置(推薦),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08sharding-jdbc 兼容 MybatisPlus動態(tài)數(shù)據(jù)源的配置方法
這篇文章主要介紹了sharding-jdbc 兼容 MybatisPlus動態(tài)數(shù)據(jù)源的配置方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-07-07詳解基于JWT的springboot權限驗證技術實現(xiàn)
這篇文章主要介紹了詳解基于JWT的springboot權限驗證技術實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-11-11成功解決IDEA2020 Plugins 連不上、打不開的方法
這篇文章主要介紹了成功解決IDEA2020 Plugins 連不上、打不開的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-06-06教你如何把Eclipse創(chuàng)建的Web項目(非Maven)導入Idea
這篇文章主要介紹了教你如何把Eclipse創(chuàng)建的Web項目(非Maven)導入Idea,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04