Springboot3利用redis生成唯一訂單號(hào)的實(shí)現(xiàn)示例
生成訂單號(hào)
生成唯一訂單號(hào)的方法有很多種,包括uuid,雪花算法等等,還可以利用數(shù)據(jù)庫(kù)的約束生成唯一的id,比如自增,但是數(shù)據(jù)庫(kù)的性能比較低,如果使用數(shù)據(jù)庫(kù)來(lái)生成訂單號(hào)效率比較低。我們可以考慮使用redis來(lái)生成唯一的訂單號(hào)。redis天然單線程,又支持lua腳本原子性操作。所以很好實(shí)現(xiàn)這些功能。
代碼實(shí)現(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();
}
}
/**
* 我們可以采用這樣一種策略,
* 時(shí)間精確到秒+7位數(shù)的自增數(shù)字
* 這樣可以實(shí)現(xiàn)同一個(gè)用戶在同一秒下單的不超過(guò)1000萬(wàn)單
* 然后使用時(shí)間在redis創(chuàng)建一個(gè)key,進(jìn)行自增,然后30秒過(guò)期
*/
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) // 小時(shí),2位數(shù)字
.appendValue(ChronoField.MINUTE_OF_HOUR, 2) // 分鐘,2位數(shù)字
.appendValue(ChronoField.SECOND_OF_MINUTE, 2) // 秒,2位數(shù)字
.toFormatter();
// 獲取當(dāng)前時(shí)間的字符串表示,不使用-或:作為分隔符
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í)行事務(wù)
Long increment = 0L;
increment = redisTemplate.opsForValue().increment(key);
if (increment!=null && increment == 1) {
// 設(shè)置過(guò)期時(shí)間,這里并沒(méi)有使用事務(wù)或者lua腳本
// 因?yàn)槲矣X(jué)得上面的自增操作是原子性的,也就是肯定會(huì)得到一個(gè)值為1
// 如果說(shuō)redis服務(wù)崩了,到這一步?jīng)]有設(shè)置好過(guò)期時(shí)間,那么我們整個(gè)
// 服務(wù)也會(huì)崩潰,這個(gè)key就會(huì)一直存在于服務(wù)器,會(huì)出現(xiàn)這個(gè)問(wèn)題,為了嚴(yán)謹(jǐn)
// 最好使用lua腳本編寫(xiě),項(xiàng)目上線會(huì)保證更可靠的效果
redisTemplate.expire(key, 30, java.util.concurrent.TimeUnit.SECONDS);
}
// 獲取自增后的值
return increment;
}
/**
* 整個(gè)腳本的作用是:
* 1.檢查一個(gè)鍵是否存在于Redis中。
* 2.如果鍵不存在,則設(shè)置該鍵的值為1,并為其設(shè)置60秒的過(guò)期時(shí)間,然后返回1。
* 3.如果鍵已經(jīng)存在,則將其值增加1,并返回增加后的新值。
* Redis執(zhí)行Lua腳本時(shí)是原子的,這意味著在執(zhí)行這個(gè)腳本的過(guò)程中,
* 不會(huì)有其他腳本或命令同時(shí)修改這個(gè)鍵,從而保證了操作的原子性和一致性。
* @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));
}到此這篇關(guān)于Springboot3利用redis生成唯一訂單號(hào)的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Springboot3 redis生成唯一訂單號(hào)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)戰(zhàn)之簡(jiǎn)單的文件管理器
這篇文章主要介紹了Java實(shí)戰(zhàn)之簡(jiǎn)單的文件管理器,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04
SpringBoot中對(duì)應(yīng)2.0.x版本的Redis配置詳解
這篇文章主要為大家介紹了SpringBoot中對(duì)應(yīng)2.0.x版本的Redis配置詳解,文中的實(shí)現(xiàn)步驟講解詳細(xì),感興趣的小伙伴們可以了解一下2022-06-06
Java利用Stream?API提高代碼的簡(jiǎn)潔度和效率的操作方法
Stream?API是Java?8引入的一項(xiàng)新特性,它提供了一種聲明性、函數(shù)式的編程方式來(lái)處理集合(例如List、Set)等數(shù)據(jù),那么Stream?API到底能帶來(lái)什么好處呢?今天,我就結(jié)合我多年的開(kāi)發(fā)經(jīng)驗(yàn),帶你深入了解如何使用Stream?API優(yōu)化代碼,提高效率,減少冗余2025-08-08
Spring Boot結(jié)合ECharts案例演示示例
本文主要主要介紹了Spring Boot結(jié)合ECharts案例演示示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
Springboot錯(cuò)誤處理機(jī)制實(shí)現(xiàn)原理解析
這篇文章主要介紹了springboot錯(cuò)誤處理機(jī)制實(shí)現(xiàn)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
SpringBoot實(shí)戰(zhàn)之SSL配置詳解
今天小編就為大家分享一篇關(guān)于SpringBoot實(shí)戰(zhàn)之SSL配置詳解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02

