UUID와 비교했을때 어떤 장점이 있을까?
라는 질문을 가지고 테스트 했습니다.UUID
와 TSID
의 생성, Set 삽입 연산의 처리 속도를 비교합니다.256
, 512
, 1,024
, 4,096
, 8,192
, 100,000
, 300,000
, 500,000
회로 고정합니다.
build.gradle
의존성 추가implementation 'com.github.f4b6a3:tsid-creator:5.2.6'
package org.example.benchmark;
import com.github.f4b6a3.tsid.Tsid;
import com.github.f4b6a3.tsid.TsidFactory;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TsidGenerateTest {
@ParameterizedTest
@ValueSource(ints = {256, 512, 1_024, 4_096, 8_192, 100_000, 300_000, 500_000})
void test(int numbersOfRequests) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
List<Callable<Tsid>> tasks = getTsidCallables(numbersOfRequests);
long start = System.currentTimeMillis();
service.invokeAll(tasks);
ResultWriter.write("generate", System.currentTimeMillis() - start);
}
private List<Callable<Tsid>> getTsidCallables(int numbersOfRequests) {
List<Callable<Tsid>> callables = new ArrayList<>();
TsidFactory factory = TsidFactory.newInstance256();
while (callables.size() < numbersOfRequests) {
callables.add(factory::create);
}
return callables;
}
}
package org.example.benchmark;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class UuidGenerateTest {
@ParameterizedTest
@ValueSource(ints = {256, 512, 1_024, 4_096, 8_192, 100_000, 300_000, 500_000})
void test(int numbersOfRequests) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
List<Callable<UUID>> tasks = getUuidCallables(numbersOfRequests);
long start = System.currentTimeMillis();
service.invokeAll(tasks);
ResultWriter.write("generate", System.currentTimeMillis() - start);
}
private List<Callable<UUID>> getUuidCallables(int numbersOfRequests) {
List<Callable<UUID>> callables = new ArrayList<>();
while (callables.size() < numbersOfRequests) {
callables.add(UUID::randomUUID);
}
return callables;
}
}
package org.example.benchmark;
import com.github.f4b6a3.tsid.TsidFactory;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TsidSetInsertTest {
@ParameterizedTest
@ValueSource(ints = {256, 512, 1_024, 4_096, 8_192, 100_000, 300_000, 500_000})
void test(int numbersOfRequests) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
List<Long> tsids = getIds(numbersOfRequests);
Set<Long> idSet = new TreeSet<>();
List<Callable<Boolean>> tasks = getTasks(tsids, idSet);
long start = System.currentTimeMillis();
service.invokeAll(tasks);
ResultWriter.write("set-insert", System.currentTimeMillis() - start);
}
private List<Long> getIds(int numbersOfRequests) {
List<Long> tsids = new ArrayList<>();
TsidFactory factory = TsidFactory.newInstance256();
while (tsids.size() < numbersOfRequests) {
tsids.add(factory.create().toLong());
}
return tsids;
}
private List<Callable<Boolean>> getTasks(List<Long> ids, Set<Long> idSet) {
List<Callable<Boolean>> tasks = new ArrayList<>();
ids.forEach(id -> tasks.add(() -> {
synchronized (idSet) {
return idSet.add(id);
}
}));
return tasks;
}
}
package org.example.benchmark;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class UuidSetInsertTest {
@ParameterizedTest
@ValueSource(ints = {256, 512, 1_024, 4_096, 8_192, 100_000, 300_000, 500_000})
void test(int numbersOfRequests) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
List<String> uuids = getIds(numbersOfRequests);
Set<String> idSet = new TreeSet<>();
List<Callable<Boolean>> tasks = getTasks(uuids, idSet);
long start = System.currentTimeMillis();
service.invokeAll(tasks);
ResultWriter.write("set-insert", System.currentTimeMillis() - start);
}
private List<String> getIds(int numbersOfRequests) {
List<String> uuids = new ArrayList<>();
while (uuids.size() < numbersOfRequests) {
uuids.add(UUID.randomUUID().toString());
}
return uuids;
}
private List<Callable<Boolean>> getTasks(List<String> ids, Set<String> idSet) {
List<Callable<Boolean>> tasks = new ArrayList<>();
ids.forEach(id -> tasks.add(() -> {
synchronized (idSet) {
return idSet.add(id);
}
}));
return tasks;
}
}
.csv
파일 생성package org.example.benchmark;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class ResultWriter {
public static void write(String fileName, long duration) {
String path = String.format("src/test/resources/results/%s.csv", fileName);
try (BufferedWriter writer = new BufferedWriter(new FileWriter(path, true))) {
writer.append(String.format(",%s", duration));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
.csv
파일 포매팅 스크립트# 생성 테스트
#!/bin/bash
for i in {1..10}; do
echo "$i 회차 실행..."
echo "test$i,256,512,1_024,4_096,8_192,100_000,300_000,500_000" >> src/test/resources/results/generate.csv
echo -n "tsid" >> src/test/resources/results/generate.csv
./gradlew test --tests "org.example.benchmark.TsidGenerateTest"
echo -e -n "\nuuid" >> src/test/resources/results/generate.csv
./gradlew test --tests "org.example.benchmark.UuidGenerateTest"
echo -e "\n" >> src/test/resources/results/generate.csv
done
-----------------------------------------------------------------------
# Set 삽입 테스트
#!/bin/bash
for i in {1..10}; do
echo "$i 회차 실행..."
echo "test$i,256,512,1_024,4_096,8_192,100_000,300_000,500_000" >> src/test/resources/results/set-insert.csv
echo -n "tsid" >> src/test/resources/results/set-insert.csv
./gradlew test --tests "org.example.benchmark.TsidSetInsertTest"
echo -e -n "\nuuid" >> src/test/resources/results/set-insert.csv
./gradlew test --tests "org.example.benchmark.UuidSetInsertTest"
echo -e "\n" >> src/test/resources/results/set-insert.csv
done
.csv
파일로 출력하고 엑셀 차트에 불러와 보기 편하게 만들어 봅니다.공유 스프레드시트 링크 - 생성 테스트
공유 스프레드시트 링크 - Set 삽입 테스트
결과 차트를 만들어 표와 그래프로 나타낸 문서입니다.
생성 테스트
Set 삽입 테스트
생성 테스트와 Set 삽입 테스트에서 모두 평균 소요시간이 UUID
대비 50% 이하로 유의미한 차이를 확인 할 수 있습니다.
TSID
나 UUID
모두 유니크한 식별자 값을 할당할 수 있지만 다음과 같은 차이점이 있습니다.MySQL
을 사용할 때, UUID
타입은 BINARY(16)
자료형을 사용하고 TSID
타입은 BIGINT
자료형을 사용합니다.BINARY(16)
은 128bit, BIGINT
는 64bit를 차지해 I/O작업과 저장공간에서 효율성을 기대할 수 있습니다.BIGINT
자료형을 사용하는 TSID
가 조회 쿼리 수행시에도 더 나은 성능을 기대할 수 있습니다.TSID
와 UUID
의 생성, Set
삽입 연산 중 한꺼번에 많은 작업이 집중될 때 소요시간을 비교해봤습니다.
테스트코드, 과정에서 이상한점, 문제점 등 이슈가 발견되면 댓글이나 이메일로 알려주세요. :)