Java中雪花算法重復(fù)id問題
原理解析
雪花算法實(shí)現(xiàn)簡單、適配性強(qiáng),無論是電商訂單、日志追蹤還是分布式存儲(chǔ),都能滿足 “唯一、有序、高效、可擴(kuò)展” 的核心需求,因此成為分布式ID主流選擇。雪花算法生成的ID是一個(gè)64位的整數(shù),由多段不同意義的數(shù)字拼接而成,這種分段設(shè)計(jì)讓每個(gè)ID既帶著時(shí)間印記,又能規(guī)避多機(jī)器沖突,就像身份證通過地址碼、出生日期碼、順序碼等分段信息實(shí)現(xiàn)全國唯一標(biāo)識(shí),既有序又精準(zhǔn)。
符號(hào)位 + 時(shí)間戳 + 數(shù)據(jù)中心ID + 機(jī)器ID + 序列號(hào)
- 符號(hào)位(1位):始終為0(表示正數(shù))。這保證了生成的 ID 是正整數(shù)。
- 時(shí)間戳(41位):雪花算法的核心部分, 記錄生成ID時(shí)的毫秒級(jí)時(shí)間戳(當(dāng)前時(shí)間減去起始時(shí)間的差值),該部分保證了ID的大體有序性。41位能表示的時(shí)間范圍約為 2^41 毫秒 ≈ 69年。使用一個(gè)最近的起始時(shí)間(如 2025-01-01
00:00:00),可以大幅減少時(shí)間戳占用的位數(shù)。 - 數(shù)據(jù)中心ID(5位):用于標(biāo)識(shí)生成ID的邏輯數(shù)據(jù)中心,允許最多 2^5 = 32個(gè)數(shù)據(jù)中心。
- 工作節(jié)點(diǎn)ID(5位):用于標(biāo)識(shí)數(shù)據(jù)中心內(nèi)的具體工作節(jié)點(diǎn)(機(jī)器、服務(wù)進(jìn)程、Pod 等),允許每個(gè)數(shù)據(jù)中心最多 2^5 =
32個(gè)工作節(jié)點(diǎn)。在實(shí)際開發(fā)中,數(shù)據(jù)中心(高位)+
工作節(jié)點(diǎn)(低位)經(jīng)常被視為一個(gè)整體10位的機(jī)器ID,用于標(biāo)識(shí)集群中的唯一節(jié)點(diǎn)(機(jī)器/服務(wù)實(shí)例),最多允許 2^10 =
1024個(gè)唯一節(jié)點(diǎn)。 - 序列號(hào)(12位):用來解決同一節(jié)點(diǎn)在同一毫秒內(nèi)生成多個(gè) ID
時(shí)的沖突問題。每個(gè)節(jié)點(diǎn)在每毫秒內(nèi)都可以獨(dú)立地從0開始遞增生成序列號(hào),當(dāng)序列號(hào)用完(達(dá)到
4095)后,會(huì)強(qiáng)制等待到下一毫秒再繼續(xù)生成。對(duì)于12位序列號(hào),單節(jié)點(diǎn)每毫秒最多生成4096個(gè)ID,要達(dá)到這個(gè)并發(fā)量很極端(單節(jié)點(diǎn)超過400萬QPS),現(xiàn)實(shí)中很難溢出。
以下為java實(shí)現(xiàn)的雪花算法代碼示例(未考慮時(shí)鐘回?fù)埽?,起始時(shí)間決定了算法能生成ID的有效時(shí)長,通常將起始時(shí)間設(shè)為項(xiàng)目上線日期。
public class SnowflakeIdGenerator {
// 起始時(shí)間戳,這里以2025-07-01 00:00:00為基準(zhǔn)
private final long startTimeStamp = 1751299200000L;
// 機(jī)器ID所占位數(shù)
private final long workerIdBits = 5L;
// 數(shù)據(jù)中心ID所占位數(shù)
private final long dataCenterIdBits = 5L;
// 序列號(hào)所占位數(shù)
private final long sequenceBits = 12L;
// 機(jī)器ID最大值 31
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 數(shù)據(jù)中心ID最大值 31
private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
// 機(jī)器ID向左移位數(shù)
private final long workerIdShift = sequenceBits;
// 數(shù)據(jù)中心ID向左移位數(shù)
private final long dataCenterIdShift = sequenceBits + workerIdBits;
// 時(shí)間戳向左移位數(shù)
private final long timestampShift = sequenceBits + workerIdBits + dataCenterIdBits;
// 序列號(hào)掩碼 4095
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
// 工作機(jī)器ID
private final long workerId;
// 數(shù)據(jù)中心ID
private final long dataCenterId;
// 序列號(hào)
private long sequence = 0L;
// 上次生成ID的時(shí)間戳
private long lastTimestamp = -1L;
// 構(gòu)造函數(shù)
public SnowflakeIdGenerator(long workerId, long dataCenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("Worker ID 不能大于 " + maxWorkerId + " 或小于 0");
}
if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
throw new IllegalArgumentException("數(shù)據(jù)中心 ID 不能大于 " + maxDataCenterId + " 或小于 0");
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
}
// 生成下一個(gè)ID
public synchronized long nextId() {
long currentTimestamp = System.currentTimeMillis();
if (currentTimestamp == lastTimestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 當(dāng)前毫秒內(nèi)序列號(hào)已用完,等待下一毫秒
currentTimestamp = waitNextMillis(lastTimestamp);
}
} else {
// 時(shí)間戳改變,重置序列號(hào)
sequence = 0L;
}
lastTimestamp = currentTimestamp;
// 按規(guī)則組合生成ID
return ((currentTimestamp - startTimeStamp) << timestampShift) |
(dataCenterId << dataCenterIdShift) |
(workerId << workerIdShift) |
sequence;
}
// 等待下一毫秒
private long waitNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
// 測(cè)試示例
public static void main(String[] args) {
SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
for (int i = 0; i < 10; i++) {
System.out.println(idGenerator.nextId());
}
}
}
為什么會(huì)出現(xiàn)重復(fù)ID?
雪花算法雖代碼量少、實(shí)現(xiàn)簡單,卻并非萬無一失。不少研發(fā)人員常常直接從網(wǎng)上拷貝現(xiàn)成的工具類,或是用大模型生成代碼后直接用于生產(chǎn)環(huán)境 —— 直到某天突然收到用戶反饋:自己賬號(hào)的數(shù)據(jù)出現(xiàn)了錯(cuò)亂,明明只買了一件衣服,訂單卻顯示多個(gè)其他辣眼的商品,還附帶陌生的收貨地址。手忙腳亂一頓排查后,竟發(fā)現(xiàn)數(shù)據(jù)庫中出現(xiàn)了少量訂單SN重復(fù)的異常數(shù)據(jù),不由得心生疑惑:雪花算法不是每毫秒能生成4096個(gè)不重復(fù)編號(hào)嗎?訂單服務(wù)部署了十幾個(gè)節(jié)點(diǎn),但業(yè)務(wù)量真有這么大嗎?到底為什么會(huì)出現(xiàn)重復(fù)呢?我們一起來一探究竟。
機(jī)器ID重復(fù)
為什么重復(fù):
多個(gè)運(yùn)行的節(jié)點(diǎn)使用相同的數(shù)據(jù)中心ID(datacenter-id)和工作節(jié)點(diǎn)ID(worker-id)。即在同一毫秒內(nèi),如果多個(gè)節(jié)點(diǎn)的機(jī)器ID相同、系統(tǒng)時(shí)間戳相同,序列號(hào)就可能從相同起點(diǎn)開始分配并重疊,導(dǎo)致生成完全相同的ID三元組
(時(shí)間戳, 機(jī)器ID, 序列號(hào))。
典型現(xiàn)象
多數(shù)研發(fā)人員會(huì)將數(shù)據(jù)中心ID和工作節(jié)點(diǎn)ID硬編碼在代碼中,或在配置文件里設(shè)置了相同的
datacenter-id與worker-id,這直接導(dǎo)致無論部署多少個(gè)節(jié)點(diǎn),機(jī)器ID都完全一致。
如何解決:
核心原則必須確保整個(gè)分布式集群中,任何兩個(gè)同時(shí)工作的節(jié)點(diǎn),它們的 (數(shù)據(jù)中心ID, 工作節(jié)點(diǎn)ID) 二元組(或者將二者視為10位合并的“機(jī)器ID”)必須是唯一的!
(1)手動(dòng)配置文件:在啟動(dòng)服務(wù)前,為每個(gè)節(jié)點(diǎn)的配置文件(如 application.properties, application.yml, configmap 等)顯式配置一個(gè)唯一的 datacenter-id 和 worker-id。該方案簡單直觀,但繁瑣,易出錯(cuò)(配置沖突),適合小型、靜態(tài)集群,不適用于節(jié)點(diǎn)動(dòng)態(tài)伸縮的集群。
(2)系統(tǒng)環(huán)境變量:在部署節(jié)點(diǎn)(物理機(jī)、虛擬機(jī)、容器)時(shí),通過啟動(dòng)腳本、容器編排系統(tǒng)(如K8s Deployment/StatefulSet 的env)為每個(gè)實(shí)例設(shè)置唯一的 SNOWFLAKE_DATACENTER_ID和SNOWFLAKE_WORKER_ID環(huán)境變量,服務(wù)啟動(dòng)時(shí)讀取這些環(huán)境變量。
(3)利用基礎(chǔ)設(shè)施的唯一性:
Kubernetes StatefulSet會(huì)為每個(gè) Pod
分配一個(gè)固定且有序的唯一索引(從0開始)。比如名為snowflake-app的StatefulSet有3個(gè)Pod:snowflake-app-0,
snowflake-app-1, snowflake-app-2。應(yīng)用程序可以讀取 spec.podName(通常是
HOSTNAME環(huán)境變量),解析末尾的數(shù)字索引,將這個(gè)索引直接用作工作節(jié)點(diǎn)ID。若業(yè)務(wù)需要擴(kuò)容至超過worker-id最大閾值(如32個(gè)以上Pod),直接使用索引會(huì)導(dǎo)致worker-id重復(fù),需結(jié)合數(shù)據(jù)中心ID(datacenter-id)拆分(如用 StatefulSet 名稱哈希作為datacenter-id)。公有云(如阿里云、華為云、騰訊云ECS)會(huì)為每個(gè)虛擬機(jī)實(shí)例分配一個(gè)唯一ID,Pod(如Deployment)運(yùn)行時(shí)也有自己的ID。應(yīng)用程序可以在啟動(dòng)時(shí)通過查詢實(shí)例/容器的元數(shù)據(jù)服務(wù)獲取這個(gè)唯一ID,然后對(duì)這個(gè)較長的ID進(jìn)行哈希并取模,映射到可用的datacenter-id和worker-id范圍內(nèi)(如總ID%1024,得到 0-1023的一個(gè)值)。該方案需要依賴特定平臺(tái)的 API/服務(wù)。哈希取模存在極小沖突風(fēng)險(xiǎn),需要設(shè)計(jì)好映射邏輯。
利用IP地址 (網(wǎng)絡(luò)標(biāo)識(shí)):應(yīng)用程序直接獲取其運(yùn)行環(huán)境(Pod、容器、虛擬機(jī)、物理機(jī))的IP地址,對(duì)整個(gè)IP地址字符串或二進(jìn)制表示計(jì)算哈希值取模,然后取模,映射為datacenter-id和worker-id。在Kubernetes 中,在Kubernetes中,Pod通常可以通過status.podIP獲得,Deployment Pod重建通常會(huì)獲得新IP;虛擬機(jī)/物理機(jī)IP也可能因維護(hù)、遷移或網(wǎng)絡(luò)配置變更而改變。該方案同樣存在極小概率ID沖突,且需容忍獲取IP的性能開銷和失敗風(fēng)險(xiǎn)。
// 獲取機(jī)器ID
private static long getNodeId() {
try {
InetAddress address = findFirstNonLoopbackAddress();
String ip = address.getHostAddress();
int hash = ip.hashCode();
// 確保非負(fù)數(shù)并取模最大節(jié)點(diǎn)ID
long nodeId = (hash & 0x7FFFFFFF) % (MAX_NODE_ID + 1);
System.out.println("使用IP地址: " + ip + " 生成機(jī)器ID: " + nodeId);
return nodeId;
} catch (Exception e) {
// 異常時(shí)隨機(jī)生成節(jié)點(diǎn)ID
long nodeId = new Random().nextInt((int) (MAX_NODE_ID + 1));
System.out.println("獲取IP失敗,隨機(jī)生成機(jī)器ID: " + nodeId);
return nodeId;
}
}
// 查找第一個(gè)非環(huán)回IPv4地址
private static InetAddress findFirstNonLoopbackAddress() throws SocketException {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface iface = interfaces.nextElement();
if (iface.isLoopback() || iface.isVirtual() || !iface.isUp()) {
continue;
}
Enumeration<InetAddress> addresses = iface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) {
return addr;
}
}
}
throw new RuntimeException("未找到非環(huán)回IPv4地址");
}
(4)外部協(xié)調(diào)服務(wù):使用分布式協(xié)調(diào)服務(wù)(如ZooKeeper, etcd, Redis, 數(shù)據(jù)庫)來注冊(cè)節(jié)點(diǎn)并分配唯一的機(jī)器 ID。如Leaf-Snowflake改進(jìn)了雪花算法,機(jī)器ID由Zookeeper協(xié)調(diào)分配、百度UID Generator啟動(dòng)時(shí)向DB注冊(cè)節(jié)點(diǎn)分配唯一worker_id。
流程示例:
- 節(jié)點(diǎn)啟動(dòng)時(shí),連接到協(xié)調(diào)服務(wù)。
- 如果節(jié)點(diǎn)宕機(jī)或與協(xié)調(diào)服務(wù)斷開連接(session超時(shí)),協(xié)調(diào)服務(wù)會(huì)自動(dòng)刪除其對(duì)應(yīng)的臨時(shí)節(jié)點(diǎn),該機(jī)器ID被釋放,可以被新節(jié)點(diǎn)申請(qǐng)使用。
- 節(jié)點(diǎn)將這個(gè)唯一的序號(hào)作為它的機(jī)器ID(或從中計(jì)算 datacenter-id 和 worker-id,如序號(hào) % 1024)。序號(hào)在服務(wù)運(yùn)行期間保持不變。
- 節(jié)點(diǎn)讀取自己創(chuàng)建的節(jié)點(diǎn)的序號(hào)(如 0000000005)。
- 協(xié)調(diào)服務(wù)保證創(chuàng)建的有序節(jié)點(diǎn)的名稱(包含一個(gè)單調(diào)遞增的序號(hào))是唯一的。
- 節(jié)點(diǎn)嘗試在一個(gè)預(yù)設(shè)的路徑下(如 /snowflake/workers)創(chuàng)建一個(gè)臨時(shí)有序節(jié)點(diǎn)。
優(yōu)點(diǎn): 無需預(yù)配置,自動(dòng)處理節(jié)點(diǎn)加入/離開,ID分配唯一且可靠,支持大規(guī)模集群。
缺點(diǎn): 增加了外部依賴和復(fù)雜度。
(5)設(shè)計(jì)機(jī)器ID位數(shù)的考慮:默認(rèn)10位能支持 1024 個(gè)節(jié)點(diǎn),對(duì)大多數(shù)公司規(guī)模通常夠用??梢罁?jù)業(yè)務(wù)規(guī)模靈活調(diào)整:
- 并發(fā)量高但集群規(guī)模不大(節(jié)點(diǎn)少): 可以減少datacenter-id和worker-id 總位數(shù)(比如降到8位甚至更少),把節(jié)省出來的位數(shù)加到sequence序列號(hào)上。這樣每個(gè)節(jié)點(diǎn)每毫秒可以生成更多的ID。
- 集群規(guī)模巨大(超過1024節(jié)點(diǎn)): 需要增加datacenter-id和worker-id總位數(shù)(比如設(shè)為12位)。這時(shí)需要犧牲timestamp或sequence 的位數(shù)(如時(shí)間戳減到40,序列號(hào)減到11位)。犧牲時(shí)間戳位數(shù)會(huì)縮短系統(tǒng)的可用年限;犧牲序列號(hào)會(huì)降低單節(jié)點(diǎn)/毫秒的最大并發(fā)量。
時(shí)鐘回?fù)?/h2>
為什么重復(fù):系統(tǒng)時(shí)間因?yàn)镹TP同步失敗、閏秒調(diào)整、虛擬機(jī)/容器掛起恢復(fù)、人為設(shè)置錯(cuò)誤等原因發(fā)生了向后跳躍,導(dǎo)致雪花算法生成ID時(shí)使用了之前已生成ID的時(shí)間戳部分,進(jìn)而可能產(chǎn)生重復(fù)ID。
如何解決:大部分雪花算法的優(yōu)秀實(shí)現(xiàn)都包含了時(shí)鐘回?fù)軝z測(cè)和處理機(jī)制,如拋出異常、短暫等待、使用備用邏輯。
為什么重復(fù):系統(tǒng)時(shí)間因?yàn)镹TP同步失敗、閏秒調(diào)整、虛擬機(jī)/容器掛起恢復(fù)、人為設(shè)置錯(cuò)誤等原因發(fā)生了向后跳躍,導(dǎo)致雪花算法生成ID時(shí)使用了之前已生成ID的時(shí)間戳部分,進(jìn)而可能產(chǎn)生重復(fù)ID。
如何解決:大部分雪花算法的優(yōu)秀實(shí)現(xiàn)都包含了時(shí)鐘回?fù)軝z測(cè)和處理機(jī)制,如拋出異常、短暫等待、使用備用邏輯。
(1)預(yù)防為主:禁止手動(dòng)時(shí)間修改;NTP通過頻率調(diào)整、分散度控制、時(shí)鐘篩選、步進(jìn)限制等機(jī)制防止時(shí)間回?fù)?,如使用chrony進(jìn)行平滑時(shí)間調(diào)整(stepping → slewing)、配置clock slew而非 jump避免突變。
(2)拋出異常:當(dāng)檢測(cè)到時(shí)鐘回?fù)軙r(shí),直接拋出異常,停止生成ID,等待人工干預(yù)或時(shí)間恢復(fù)正常。該方案簡單安全,但影響業(yè)務(wù)連續(xù)性。
//處理時(shí)鐘回?fù)?
if (currentTimestamp < lastTimestamp) {
throw new ClockBackwardException(
"Clock moved backwards. Refusing to generate id for " +
(lastTimestamp - currentTimestamp) + " milliseconds");
}
(3)等待時(shí)鐘恢復(fù)(適合毫秒級(jí)輕度回?fù)埽喝舭l(fā)現(xiàn)回?fù)?,不立即?bào)錯(cuò),而是阻塞等待 ,直到系統(tǒng)時(shí)間 ≥ lastTimestamp。該方案短暫阻塞,可能影響性能。
// 處理時(shí)鐘回?fù)?
if (currentTimestamp < lastTimestamp) {
long offset = lastTimestamp - currentTimestamp;
// 回?fù)軙r(shí)間小于1秒,阻塞等待
if (offset <= MAX_BACKWARD_TIME) {
currentTimestamp = waitForClockRecovery(lastTimestamp);
} else {
// 回?fù)軙r(shí)間超過1秒,拋出異常
throw new RuntimeException("Clock moved backwards too much: " + offset + "ms");
}
}
private long waitForClockRecovery(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp < lastTimestamp) {
//短暫休眠避免CPU空轉(zhuǎn)
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted while waiting for clock recovery", e);
}
timestamp = System.currentTimeMillis();
}
System.out.println("Clock recovered after " + (timestamp - lastTimestamp) + "ms");
return timestamp;
}
(4)回?fù)苎a(bǔ)償:通過累積所有歷史回?fù)軙r(shí)間,使生成器內(nèi)部時(shí)間永遠(yuǎn)領(lǐng)先于系統(tǒng)時(shí)間,可避免使用Thread.sleep()造成的性能瓶頸。
// 可容忍的最大時(shí)鐘回?fù)埽ê撩耄?
private static final long MAX_BACKWARD_MS = 1000;
// 發(fā)生時(shí)鐘回?fù)?
if (currentTimestamp < lastTimestamp) {
long backwardMs = lastTimestamp - currentTimestamp;
// 超過容忍閾值則拋出異常
if (backwardMs > MAX_BACKWARD_MS) {
throw new IllegalStateException("Clock moved backwards by " + backwardMs + " ms, exceeding maximum allowed value");
}
// 記錄回?fù)軙r(shí)間用于補(bǔ)償
clockOffset += backwardMs;
// 補(bǔ)償當(dāng)前時(shí)間戳
currentTimestamp = System.currentTimeMillis()+clockOffset;
}
(5)擴(kuò)展位機(jī)制(秒級(jí)以上嚴(yán)重回?fù)埽盒薷难┗ㄋ惴ńY(jié)構(gòu),預(yù)留幾位用于表示“是否處于回?fù)軤顟B(tài)”或“回?fù)艽螖?shù)”。當(dāng)發(fā)生回?fù)軙r(shí),增加“回?fù)馨姹咎?hào)”,即使時(shí)間戳相同,版本不同也能區(qū)分ID。
到此這篇關(guān)于Java中雪花算法重復(fù)id問題的文章就介紹到這了,更多相關(guān)Java雪花算法ID重復(fù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis saveBatch 性能調(diào)優(yōu)的實(shí)現(xiàn)
本文主要介紹了MyBatis saveBatch 性能調(diào)優(yōu)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
Spring Boot如何使用httpcomponents實(shí)現(xiàn)http請(qǐng)求
這篇文章主要介紹了Spring Boot使用httpcomponents實(shí)現(xiàn)http請(qǐng)求的示例代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
使用SpringMVC訪問Controller接口返回400BadRequest
這篇文章主要介紹了使用SpringMVC訪問Controller接口返回400BadRequest,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
java?AES加密/解密實(shí)現(xiàn)完整代碼(附帶源碼)
這篇文章主要介紹了java?AES加密/解密實(shí)現(xiàn)的相關(guān)資料,包括AES加密算法的基本原理、Java加密API的使用方法以及項(xiàng)目實(shí)現(xiàn)的步驟和代碼示例,需要的朋友可以參考下2025-04-04
Springboot整合Shiro實(shí)現(xiàn)登錄與權(quán)限校驗(yàn)詳細(xì)解讀
本文給大家介紹Springboot整合Shiro的基本使用,Apache?Shiro是Java的一個(gè)安全框架,Shiro本身無法知道所持有令牌的用戶是否合法,我們將整合Shiro實(shí)現(xiàn)登錄與權(quán)限的驗(yàn)證2022-04-04
Spring?Boot?如何通過ServletRequestHandledEvent事件實(shí)現(xiàn)接口請(qǐng)求的性能監(jiān)控
在Spring框架中,監(jiān)控接口請(qǐng)求的性能可以通過ServletRequestHandledEvent事件實(shí)現(xiàn),這篇文章給大家介紹Spring?Boot?如何通過ServletRequestHandledEvent事件實(shí)現(xiàn)接口請(qǐng)求的性能監(jiān)控,感興趣的朋友跟隨小編一起看看吧2024-08-08
基于Java 數(shù)組內(nèi)存分配的相關(guān)問題
本篇文章是對(duì)Java中數(shù)組內(nèi)存分配進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

