개요
서비스에서 380만 정도 Key에 21GB 정도의 메모리가 할당되어 운영되고 있습니다.
메모리 DB 라는 관점에서, 21GB 라는 메모리는 큰 편이라고 볼 순 없지만
AWS EC2 환경에서 r5.2xlarge 타입으로 운영되고 있고 Scale Up 을 하는순간 추가 비용이 발생하는 문제와
디스크처럼 여유롭게 공간을 확보하기 힘들기 때문에 개선이 필요할 것으로 보았습니다.
개선방법
포스팅에서 다루지 않을 개선 방법
가장 편한 방법은 필요없는 데이터를 삭제하는 것과 위에서 말한것처럼 Scale Up 을 하는 것 입니다.
( 사실 라이브 상황에서 이건 쉬운 방법이 아닙니다.. ㅠㅠ )
Scale Up 을 하는동안의 DownTime 을 감당해야 하고 (이중화로 구성되어 있기 때문에 서비스 다운은 아니지만요.)
필요없는 데이터를 추려 삭제하는 방법또한 시간이 소요됩니다.
포스팅에서 다룰 개선 방법
또하나의 방법은 데이터 저장을 메모리를 낭비하면서 하고 있진 않는지 확인해 보는 방법입니다.
확인을 위해서는 Key 가 어떻게 저장이 되고 있는지 알아야 하고,
OverHead 는 얼마나 발생하는지를 알아야 합니다.
아래에서 정리해보겠습니다.
참고로 이 방법도 만만치는 않습니다. 구조 개선은 개발 코드도 뜯어 고쳐야 하기 때문입니다.
키와 데이터타입의 오버헤드 비용
레디스에서는 Key 와 DataType 을 관리하기 위한 OverHead 가 발생합니다.
제가 직접 계산해본 것은 아니지만 RedisGate 사이트에 따르면 아래와 같이 나와있습니다.
* 레디스 버전마다 다르기 때문에 이에 대한 부분은 정확하지 않을 수 있습니다.
- 키의 관리 메모리 : 50 바이트
- String 타입의 관리 메모리 : 30 바이트
- Lists 타입의 관리 메모리 : 15 바이트
- Sets 타입의 관리 메모리 : 75 바이트
- ZSets 타입의 관리 메모리 : 120 바이트
- Hash 타입의 관리 메모리 : 100 바이트
redisgate.kr/redis/configuration/server_memory.php
데이터 타입에 따른 레디스 사용 메모리를 간략하게 비교하기 위해 타입에 대해 먼저 설명하면
String 은 데이터 저장할때마다 키의 관리 메모리가 들어가지만
Hash 는 같은 Key 내에 Fileds 로 구분하여 데이터를 저장하기 때문에 하나의 키 관리 비용만 들어가게 할 수 있습니다.
테스트
레디스 버전 : 2.8.4
임시 테스트
최초 키가 적재될 때는 used_memory 가 동일했지만,
데이터를 넣을수록 used_memory 가 String 이 올라가는 것을 볼 수 있습니다.
String 타입 | Hash 타입 |
info memory # Memory used_memory:500248 |
info memory # Memory used_memory:500248 |
set Key0 "Dummy Data" | hset K0 Key0 "Dummy Data" |
500344info memory # Memory used_memory:500344 |
info memory # Memory used_memory:500344 |
set Key1 "Dummy Data" | hset K0 Key1 "Dummy Data" |
info memory # Memory used_memory:500440 |
info memory # Memory used_memory:500360 |
set Key2 "Dummy Data" | hset K0 Key2 "Dummy Data" |
info memory # Memory used_memory:500536 |
info memory # Memory used_memory:500392 |
본격적인 테스트
키의 갯수를 좀 더 늘려봅니다.
이번에는 스크립트를 통해서 테스트 건수를 정하면서 진행해 보도록 하겠습니다.
스크립트는 다음과 같고 필요하신 분은 가져다 쓰셔도 되고, 레디스 접속하는 부분만 수정해 주시면 됩니다.
다른 타입으로 테스트가 필요하시는 분은 레디스 적재하는 부분에서 저장방식만 변경하면 됩니다.
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
$ cat key.sh
test_cnt=100000
dummy="THIS IS DUMMY DATA FOR TEST."
echo "String Type" > result.txt
echo "============" >> result.txt
echo "before" >> result.txt
echo "info memory" >> result.txt
redis-cli -p 6161 -a P@ssword info memory >> result.txt
echo "info keyspace" >> result.txt
redis-cli -p 6161 -a P@ssword info keyspace >> result.txt
echo "============" >> result.txt
i=0
while [ 1 = 1 ]
do
i=`expr $i + 1`
redis-cli -p 6161 -a P@ssword set $i "$dummy" > /dev/null 2>&1
if [ $i = $test_cnt ]
then
break
fi
done
echo "============" >> result.txt
echo "after" >> result.txt
echo "info memory" >> result.txt
redis-cli -p 6161 -a P@ssword info memory >> result.txt
echo "info keyspace" >> result.txt
redis-cli -p 6161 -a P@ssword info keyspace >> result.txt
echo "============" >> result.txt
i=0
while [ 1 = 1 ]
do
i=`expr $i + 1`
redis-cli -p 6161 -a P@ssword del $i > /dev/null 2>&1
if [ $i = $test_cnt ]
then
break
fi
done
echo "============" >> result.txt
echo "finish" >> result.txt
echo "info memory" >> result.txt
redis-cli -p 6161 -a P@ssword info memory >> result.txt
echo "info keyspace" >> result.txt
redis-cli -p 6161 -a P@ssword info keyspace >> result.txt
echo "============" >> result.txt
###############################################################################################################
echo "" >> result.txt
echo "" >> result.txt
echo "" >> result.txt
echo "hSet Type" >> result.txt
echo "============" >> result.txt
echo "before" >> result.txt
echo "info memory" >> result.txt
redis-cli -p 6161 -a P@ssword info memory >> result.txt
echo "info keyspace" >> result.txt
redis-cli -p 6161 -a P@ssword info keyspace >> result.txt
echo "============" >> result.txt
i=0
j=0
k=0
while [ 1 = 1 ]
do
i=`expr $i + 1`
j=`expr $i % 1000`
redis-cli -p 6161 -a P@ssword hset $k $i "$dummy" > /dev/null 2>&1
if [ $j = 0 ]
then
k=`expr $k + 1`
fi
if [ $i = $test_cnt ]
then
break
fi
done
echo "============" >> result.txt
echo "after" >> result.txt
echo "info memory" >> result.txt
redis-cli -p 6161 -a P@ssword info memory >> result.txt
echo "info keyspace" >> result.txt
redis-cli -p 6161 -a P@ssword info keyspace >> result.txt
echo "============" >> result.txt
i=0
j=0
k=0
while [ 1 = 1 ]
do
i=`expr $i + 1`
j=`expr $i % 1000`
redis-cli -p 6161 -a P@ssword hdel $k $i > /dev/null 2>&1
if [ $j = 0 ]
then
k=`expr $k + 1`
fi
if [ $i = $test_cnt ]
then
break
fi
done
echo "============" >> result.txt
echo "finish" >> result.txt
echo "info memory" >> result.txt
redis-cli -p 6161 -a P@ssword info memory >> result.txt
echo "info keyspace" >> result.txt
redis-cli -p 6161 -a P@ssword info keyspace >> result.txt
echo "============" >> result.txt
|
cs |
테스트 결과
test_cnt 가 1,000 일 때
- String 타입 : Key 1,000 개
- Hash 타입 : Key 1 개 / Fields 1,000 개
결과 : 2.6 % 정도의 메모리 개선 효과
테스트 전 | 테스트 후 | 키 정리 후 | ||
String | Used Memory (byte) | 500,248 | 620,408 | 500,248 |
Keys | 0 | 1,000 | 0 | |
Hash | Used Memory (byte) | 500,248 | 604,600 | 500,248 |
Keys | 0 | 1 | 0 |
test_cnt 가 100,000 일 때
- String 타입 : Key 100,000 개
- Hash 타입 : Key 100 개 / Fields 1,000 개
결과 : 2.6% 정도의 메모리 개선 효과
테스트 전 | 테스트 후 | 키 정리 후 | ||
String | Used Memory (byte) | 500,248 | 12,748,792 | 502,264 |
Keys | 0 | 100,000 | 0 | |
Hash | Used Memory (byte) | 502,264 | 12,376,456 | 502,248 |
Keys | 0 | 100 | 0 |
결론
String 과 Hash 타입의 저장에 따른 메모리 사용률 비교 결과
Hash 타입이 String 보다 2.6% 정도의 메모리 개선 효과가 나타났습니다.
생각하는 것만큼 확.. 와닿지는 않아서 아쉬웠습니다만 ㅠㅠ
레디스 사이즈가 커질수록 저장공간 확보도 클 것이고 (예를들면,, 1TB 면 30 GByte 확보가능)
그래도 타입에 따른 메모리 개선을 할 수 있다는 점에서는 소득이 있었던 것으로 보입니다.
추가로, 오버헤드 비용은 레디스 버전마다 다른 것으로 알고 있습니다.
버전에 따라 개선의 폭이 클 수도 있기에 데이터 타입 테스트를 진행해 보는것을 추천드립니다.
레디스 데이터 타입 오버헤드
레디스 메모리 사용량
레디스 메모리 최적화
레디스 메모리 효율적으로
'Database > Redis' 카테고리의 다른 글
[Redis] Asynchronous AOF fsync is taking too long (disk is busy?). 오류 (0) | 2021.03.07 |
---|---|
[Redis] save 동작 시 메모리 사용률에 대한 정리 (0) | 2021.01.09 |
[Redis] Jedis 에서 Sentinel 의 pub/sub 을 제대로 인지할까? (0) | 2020.12.27 |
[Redis] Jedis 에서 Redis Sentinel 을 통한 마스터 정보 얻는 방법 (0) | 2020.12.23 |
[Redis] 리눅스환경에서 JAVA 와 Jedis 로 Redis 접속하는 방법 (2) | 2020.12.20 |