성별에 인덱스를 건다면?
남,여 인덱스
예전에 면접에서 들은 질문이기도 하고, 면접 예상 질문 목록에 보면 흔히 보이는 주제인 것 같다.
누워있다가 갑자기 생각나서 머릿속으로 실험만 하다가 한번 직접 코드를 쳐보기로 했다. 내 생각으로는 성별 단일 인덱스는 큰 효과를 보기 어려울 것 같았고, 복합 인덱스를 걸면 조금이라도 효과를 볼 수 있지 않을까 하는 궁금증에 시작하게 되었다.
예를 들어서 성별만 인덱스를 잡으면
id | 성별 |
---|---|
1 | 남 |
2 | 여 |
이런 식으로 정렬될테고, 이진 탐색의 효과를 보기 어렵다고 생각했다. 오히려 인덱스 테이블을 참조하고 오는 시간이 더 걸려서 비효율적일 것이라 생각했고, 만약 성별 + 이름과 같은 복합 인덱스를 건다면 조금은 이진 탐색의 효과를 볼 수 있지 않을까 생각했다.
성별 | 이름 |
---|---|
남 | Bob |
남 | David |
남 | Frank |
여 | Alice |
여 | Claire |
여 | Ellen |
이런 식으로 값이 정렬될 테니까, 풀스캔보다는 빠르게 진행되지 않을까 생각했고, 코드로 테스트해보기로 했다.
실험하기
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Table(indexes = {@Index(name = "test_index",columnList = "gender or gender, name or null")})
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Builder
public class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@Enumerated(EnumType.STRING)
private Gender gender;
}
@SpringBootTest
public class DBTest {
@Autowired
private TestRepository testRepository;
@Test
void 데이터_날리기(){
testRepository.deleteAll();
}
@Test
void 데이터_주입용(){
long size = 1000000;
for(long i = 0; i < size; i++){
String temp = UUID.randomUUID().toString();
TestEntity test = TestEntity.builder()
.gender(Gender.values()[(int)(i % 2)])
.name(temp)
.build();
testRepository.save(test);
}
for(int i = 1; i < size; i += (int) (size/ 10)){
String name = testRepository.findById((long)i).get().getName();
System.out.println("\""+ name + "\",");
}
}
@ParameterizedTest
@ValueSource(strings = {"99ff1cb4-f507-4f7c-8b9e-e8c1977789c2",
"4b36b9b1-32f3-45a9-b72a-6de8a8244a38",
"35143152-19dc-4c8d-acc6-faf41a1287a5",
"1385729e-f986-48fe-a821-0fc32a66cd61",
"6b15be5a-8c1a-49df-8222-a3c2a72c2765",
"f2ce33a2-9c70-415c-b4eb-c532cecb687d",
"f96e00a3-87d7-46bb-a07b-aad8f30dfd64",
"638a0ccf-400f-4c63-9b4c-a93555d35f67",
"319cd995-c61b-4fde-88c8-4d0c0f68383c",
"451c56b9-2706-4dbd-851c-22da6ef4094a",})
void 성별_이름(String value){
TestEntity entity = testRepository.findByGenderAndName(Gender.MALE, value);
}
}
100만건의 이름 샘플을 찾기가 힘들어서 이름은 UUID로 대체했다.
순서는 인덱스를 안걸고 해봤고, 성별만 인덱스를 걸어봤고, 성별 + 이름 으로 인덱스를 걸어봤다.
인덱스 X
웜업과 같은 이슈로 첫 번째 테스트 시간을 제외한다면 평균적으로 415ms 정도 걸렸다.
성별 인덱스
근소하지만 성별만 인덱스를 걸었을 때는 583ms 소요되었다.
복합 인덱스 (성별 + 이름)
평균 3.8ms가 걸렸다. 압도적으로 빠르다.
결과 분석
처음에 예상한 결과와 어느정도 맞아떨어지는 것 같다. 몇 가지 흥미로운건 잘못된 인덱스는 오히려 탐색시간을 늘리는 것을 확인할 수 있었고, 어느 정도 인덱스 구실만 할 수 있게 달아줘도 탐색 소요시간이 압도적으로 단축되는 것을 확인할 수 있었다.
참고
- 혼자 실험