nextByCalendarAndRandom
, 序號生成是採用兩位亂數,然後亂數產生了衝突,一毫秒內產生的兩個亂數有衝突,nextByCalendarAndAtomicInteger
自增,就表示一毫秒最大隻有100個請求能進來?超過就肯定會衝突?nextByLocalDateTimeAndAtomicInteger
方法,也有每毫秒超100必定重複的限制 <dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.35</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.35</version>
</dependency>
@UtilityClass
public class IdWork {
@Deprecated
public static String nextByCalendarAndRandom(String merchantNo) {
Calendar now = Calendar.getInstance();
long random1 = Math.round(Math.random() * 9);
long random2 = Math.round(Math.random() * 9);
String timestamp = (now.get(Calendar.YEAR) + "").substring(2)
+ (now.get(Calendar.MONTH) + 1)
+ now.get(Calendar.DAY_OF_MONTH)
+ now.get(Calendar.HOUR_OF_DAY)
+ now.get(Calendar.MINUTE)
+ now.get(Calendar.SECOND)
+ now.get(Calendar.MILLISECOND);
return merchantNo + timestamp + random1 + random2;
}
@Deprecated
public static String nextByLocalDateTimeAndRandom(String merchantNo) {
LocalDateTime now = LocalDateTime.now();
long random1 = Math.round(Math.random() * 9);
long random2 = Math.round(Math.random() * 9);
String timestamp = (now.getYear() + "").substring(2)
+ now.getMonthValue()
+ now.getDayOfMonth()
+ now.getHour()
+ now.getMinute()
+ now.getSecond()
+ (now.getNano() / 1000000);
return merchantNo + timestamp + random1 + random2;
}
@Deprecated
public static String nextByCalendarAndAtomicInteger(String merchantNo) {
Calendar now = Calendar.getInstance();
String timestamp = (now.get(Calendar.YEAR) + "").substring(2)
+ (now.get(Calendar.MONTH) + 1)
+ now.get(Calendar.DAY_OF_MONTH)
+ now.get(Calendar.HOUR_OF_DAY)
+ now.get(Calendar.MINUTE)
+ now.get(Calendar.SECOND)
+ now.get(Calendar.MILLISECOND);
return merchantNo + timestamp + getSeqNo();
}
@Deprecated
public static String nextByLocalDateTimeAndAtomicInteger(String merchantNo) {
LocalDateTime now = LocalDateTime.now();
String timestamp = (now.getYear() + "").substring(2)
+ now.getMonthValue()
+ now.getDayOfMonth()
+ now.getHour()
+ now.getMinute()
+ now.getSecond()
+ (now.getNano() / 1000000);
return merchantNo + timestamp + getSeqNo();
}
public static String nextBySnowflake(String merchantNo) {
return merchantNo + IdGenerator.next();
}
private static AtomicInteger seqNo = new AtomicInteger(1);
private static String getSeqNo() {
int curSeqNo = seqNo.getAndIncrement();
if (curSeqNo > 99) { // 重置,也可以取模
seqNo = new AtomicInteger(1);
}
if (curSeqNo < 10) {
return "0" + curSeqNo;
}
return curSeqNo + "";
}
public static void main(String[] args) {
String next1 = IdWork.nextByCalendarAndRandom("900087");
System.out.println(next1);
String next2 = IdWork.nextByLocalDateTimeAndRandom("900087");
System.out.println(next2);
String next3 = IdWork.nextByCalendarAndAtomicInteger("900087");
System.out.println(next3);
String next4 = IdWork.nextByLocalDateTimeAndAtomicInteger("900087");
System.out.println(next4);
String next5 = IdWork.nextBySnowflake("900087");
System.out.println(next5);
}
}
public class IdTest {
@Benchmark
public String getIdBySnowflake() {
return IdWork.nextBySnowflake("900087");
}
@Benchmark
public String nextByCalendarAndRandom() {
return IdWork.nextByCalendarAndRandom("900087");
}
@Benchmark
public String nextByLocalDateTimeAndRandom() {
return IdWork.nextByLocalDateTimeAndRandom("900087");
}
@Benchmark
public String nextByCalendarAndAtomicInteger() {
return IdWork.nextByCalendarAndAtomicInteger("900087");
}
@Benchmark
public String nextByLocalDateTimeAndAtomicInteger() {
return IdWork.nextByLocalDateTimeAndAtomicInteger("900087");
}
public static void main(String[] args) throws RunnerException {
// 吞吐量
// Options opt = new OptionsBuilder()
// .include(IdTest.class.getSimpleName())
// .mode(Mode.Throughput)
// .forks(1)
// .build();
// 平均耗時
Options opt = new OptionsBuilder()
.include(IdTest.class.getSimpleName())
.mode(Mode.AverageTime)
.timeUnit(TimeUnit.NANOSECONDS)
.forks(1)
.build();
new Runner(opt).run();
}
// 吞吐量
// Benchmark Mode Cnt Score Error Units
// IdTest.getIdBySnowflake thrpt 5 4070403.840 ± 11302.832 ops/s
// IdTest.nextByCalendarAndAtomicInteger thrpt 5 4201822.821 ± 177869.095 ops/s
// IdTest.nextByCalendarAndRandom thrpt 5 4085723.001 ± 47505.309 ops/s
// IdTest.nextByLocalDateTimeAndAtomicInteger thrpt 5 5036852.390 ± 153313.836 ops/s
// IdTest.nextByLocalDateTimeAndRandom thrpt 5 5199148.189 ± 405132.888 ops/s
// 平均耗時
// Benchmark Mode Cnt Score Error Units
// IdTest.getIdBySnowflake avgt 5 245.739 ± 0.302 ns/op
// IdTest.nextByCalendarAndAtomicInteger avgt 5 239.174 ± 4.244 ns/op
// IdTest.nextByCalendarAndRandom avgt 5 251.084 ± 5.798 ns/op
// IdTest.nextByLocalDateTimeAndAtomicInteger avgt 5 197.332 ± 0.779 ns/op
// IdTest.nextByLocalDateTimeAndRandom avgt 5 212.105 ± 1.888 ns/op
}
型別 | 作用域 | 描述 | 備註 |
---|---|---|---|
Benchmark | ElementType.METHOD | 最重要的註解,標記需要執行的方法 | |
BenchmarkMode | ElementType.METHOD, ElementType.TYPE | 統計的維度,有吞吐量,平均耗時,也可以組合使用 | |
Fork | ElementType.METHOD, ElementType.TYPE | 複製多個程序來執行方法,每輪預設Iteration迴圈5次,如果fork 3,則會執行3*5 次,一般預設值1就可以 | |
Measurement | ElementType.METHOD, ElementType.TYPE | 方法控制:迴圈次數,每次迴圈時間以及對應的時間單位 | |
Warmup | ElementType.METHOD,ElementType.TYPE | 預熱,避免系統冷啟動導致的效能測試不準 | |
OutputTimeUnit | ElementType.METHOD, ElementType.TYPE | 輸出時間單位,預設是秒 | |
Param | ElementType.FIELD | 可以指定遍歷引數,針對特殊欄位測試不同的效能 | |
Setup | ElementType.METHOD | 啟動類設定,類似 junit Before型別註解 | |
TearDown | ElementType.METHOD | 銷燬類設定,類似junit After型別註解,一般用於銷燬池化的資源 | |
Threads | ElementType.METHOD,ElementType.TYPE | ||
Timeout | ElementType.METHOD,ElementType.TYPE | ||
AuxCounters | ElementType.TYPE | 輔助計數器,可以統計 @State 修飾的物件中的 public 屬性被執行的情況 | |
Group | ElementType.METHOD | ||
GroupThreads | ElementType.METHOD | ||
CompilerControl | ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE | 內聯擴充套件是一種特別的用於消除呼叫函數時所造成的固有時間消耗方法,這裡用來控制方法或類是否內聯 | |
OperationsPerInvocation | ElementType.METHOD, ElementType.TYPE |
BenchmarkMode 執行模式(可以多個組合執行)
型別 | 描述 |
---|---|
Throughput | 每段時間執行的次數,一般是秒 |
AverageTime | 平均時間,每次操作的平均耗時 |
SampleTime | 在測試中,隨機進行取樣執行的時間 |
SingleShotTime | 在每次執行中計算耗時 |
All | 所有模式 |
// 常用的註解
@BenchmarkMode({Mode.Throughput,Mode.AverageTime})
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
public class BenchmarkTest {
@Benchmark
public long test() {}
}
// 使用 OptionsBuilder 建造者模式構建 Options, 然後在main方法執行,建議使用
Options opt = new OptionsBuilder()
.include(IdTest.class.getSimpleName())
.mode(Mode.AverageTime)
.mode(Mode.Throughput)
.timeUnit(TimeUnit.NANOSECONDS)
.warmupIterations(3)
.warmupTime(TimeValue.seconds(1))
.measurementIterations(5)
.measurementTime(TimeValue.seconds(1))
.forks(1)
.build();
避免迴圈
JVM會對迴圈進行優化,這樣會導致獲取的測試結果不準確。
jmh-java-microbenchmark-harness
jenkov: java-performance
jmh-benchmark-with-examples
Java基準測試工具 —— JMH使用指南