Redis는 싱글 스레드(Single-thread) 기반으로 데이터를 처리합니다.
즉 멀티 스레드의 동시성 이슈를 걱정하지 않아도 됩니다.
그러면 실제로도 그러한지 테스트 해보겠습니다.
삽입 및 조회 (score가 작을 수록 등수가 높다.)
@Service
public class RankingService {
private static final String LEADERBOARD_KEY = "leaderBoard";
@Autowired
StringRedisTemplate redisTemplate;
public boolean setUserScore(String userId, int score) {
ZSetOperations zSetOps = redisTemplate.opsForZSet(); //sorted set을 가져올 수 있다.
zSetOps.add(LEADERBOARD_KEY, userId, score);
return true;
}
// 순위 조회
public long getUserRanking(String userId) {
ZSetOperations zSetOps = redisTemplate.opsForZSet();
Long rank = zSetOps.reverseRank(LEADERBOARD_KEY, userId);
return rank;
}
}
100만개 동시에 삽입 .parallel을 하지 않으면 428초 정도 걸렸다.
@Test
void insertScoreParallel() {
List<Rank> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
int score = (int) (Math.random() * 1000000); //1~99만9999
String userId = "user_" + i;
list.add(new Rank(userId, score));
}
StopWatch stopWatch = new StopWatch();
stopWatch.start();
list.stream().parallel()
.forEach(x -> rankingService.setUserScore(x.userId(), x.score()));// 삽입
stopWatch.stop();
// 삽입에 걸린 시간도 확인해 봤습니다.
System.out.println("걸린 시간: " + stopWatch.getTotalTimeSeconds() + " sec");
// 대략 18초 걸림
}
record Rank(String userId, int score){}
127.0.0.1:6379> ZRANGEBYSCORE leaderBoard 0 20 WITHSCORES
1) "user_256458"
2) "0"
3) "user_565379"
4) "2"
5) "user_390389"
6) "3"
7) "user_557898"
8) "4"
9) "user_89502"
10) "5"
11) "user_122037"
12) "7"
13) "user_624195"
14) "7"
15) "user_173991"
16) "8"
17) "user_211299"
18) "8"
19) "user_390728"
20) "10"
21) "user_342466"
22) "11"
23) "user_745281"
24) "12"
25) "user_32578"
26) "13"
27) "user_396664"
28) "14"
29) "user_439914"
30) "14"
31) "user_536363"
32) "14"
33) "user_825456"
34) "15"
35) "user_944722"
36) "15"
37) "user_875773"
38) "16"
39) "user_361690"
40) "17"
41) "user_192094"
42) "18"
127.0.0.1:6379>
순위가 내림차순으로 잘 지켜져있는걸 눈으로 확인할 수 있다.
Redis는 싱글 스레드라서 레이스 컨디션은 없다.