Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redis을 이용한 투표 랭킹 시스템에서 동점자 처리 방법 고민 #9

Open
Yiseull opened this issue Feb 20, 2024 · 1 comment
Assignees

Comments

@Yiseull
Copy link
Owner

Yiseull commented Feb 20, 2024

📍 상황

Redis의 Sorted Set을 사용하여 투표에 대한 랭킹 시스템을 구현했다. Sorted Set은 score가 높은 순으로 정렬되는 특징을 가지고 있고, 현재 랭킹에서는 진행 중인 투표 참여자 수를 score로 하고 있다.

score가 같을 때 동점자 처리를 해줘야 하는 상황에 부딪혔고, 효율적인 동점자 처리 방법에 대해 고민하게 되었다.

동점자 처리에 대한 요구사항

  • score가 동점일 때는 투표 마감 시각이 더 임박한 쪽(투표 마감 시각이 더 빠른 쪽)이 더 높은 랭킹을 가지도록 해야 한다.

📍 생각한 구현 방법

동점자 처리를 위해 세 가지 방법을 생각해 봤다.

  1. Application 단에서 처리
  2. value에 시간에 따른 정렬 값을 추가하여 처리
  3. score 값을 정수부(투표 참여자 수)와 소수부(투표 마감 시각에 따른 정렬 값)로 분리하여 처리

1. Application 단에서 동점자 처리 (기각)

첫 번째 방법은 Application 단에서 동점자 처리를 하는 것이다. 이 방법은 레디스에서 랭킹을 조회한 후 동점자에 대해 투표 마감 시각이 더 빠른 순으로 또 다시 정렬을 하는 것이다.

그러나 이 방법을 사용하면 Redis를 자동 정렬을 위해 사용한 의미가 흐려지게 된다. 또한 복잡한 재정렬 코드가 추가되면서 관리 측면에서 효율적이지 않다고 판단하였다.

2. value에 시간에 따른 정렬 값을 추가하여 처리

두 번째 방법은 value에 시간에 따른 정렬 값을 추가하여 처리하는 것이다. Redis의 Sorted Set은 score 값이 같을 때 value 값이 사전적으로 큰 순으로 정렬된다. 현재 value는 String 타입이 아닌 Object 타입이므로 JSON 형태로 직렬화해 Redis에 저장하고 있다. 이 경우, 동점자가 발생했을 때 정렬 순서는 JSON의 순서 그대로 id, item1Image, item2Image, participants 필드의 순서에 따라 결정된다.

이 문제를 해결하기 위해 value에 투표 마감 시각을 가지고 만든 정렬 값을 맨 앞 필드에 두면, 동점자일 경우 투표 마감 시각이 더 빠른 투표가 높은 순위를 가지도록 처리할 수 있다.

여기서 투표 마감 시각을 이용한 정렬 값은 투표 마감 시각을 숫자로 변환한 후 큰 값에서 빼준 값이다. 이렇게 하면 투표 마감 시각이 더 빠를수록 더 높은 순위를 가질 것입니다.

아래는 value로 사용하고 있는 레코드이고, 맨 앞 필드에 secondSortValue라는 투표 마감 시각을 이용한 정렬 값을 추가한 것이다.

@Builder
public record VoteRankingInfo(
        double secondSortValue // 투표 마감 시각에 따른 정렬 값 추가
	Long id,
	String item1Image,
	String item2Image,
	int participants
) {

3. score 값을 정수부(투표 참여자 수)와 소수부(투표 마감 시각에 따른 정렬 값)로 분리하여 처리

세 번째 방법은 score 값을 정수부와 소수부로 분리하여 처리하는 것이다. 이 방법은 기존 score에 소수점을 추가하는 방법입니다. 이때 소수점은 투표 마감 시각에 따른 정렬 값인데, 이 값은 2번 방법과 동일하게 계산한다.

이렇게 하면 정수부가 같을 경우 (즉, 투표 참여자 수가 같을 경우) 소수부 (즉, 투표 마감 시각에 따른 정렬 값)에 따라 순위가 결정되므로 원하는 방향으로 동점자를 처리할 수 있을 것이다.


참고 자료

@Yiseull Yiseull self-assigned this Feb 20, 2024
@Yiseull Yiseull changed the title Redis의 Sorted Set을 사용한 랭킹 시스템에서 동점자 처리 Redis을 사용한 랭킹 시스템에서 동점자 처리에 대한 고민 Feb 20, 2024
@Yiseull Yiseull changed the title Redis을 사용한 랭킹 시스템에서 동점자 처리에 대한 고민 Redis Sorted Set을 이용한 투표 랭킹 시스템에서 동점자 처리 방법 고민 Feb 20, 2024
@Yiseull Yiseull changed the title Redis Sorted Set을 이용한 투표 랭킹 시스템에서 동점자 처리 방법 고민 Redis을 이용한 투표 랭킹 시스템에서 동점자 처리 방법 고민 Feb 20, 2024
@Yiseull
Copy link
Owner Author

Yiseull commented Feb 23, 2024

4. 투표 ID를 이용한 동점자 처리 (채택)

네 번째 방법은 투표 생성 및 마감 시간의 특성을 이용한 방법이다. 투표는 생성된 후 24시간 동안 진행되며, 일찍 생성된 투표는 일찍 마감된다. 이 규칙에 따라 투표의 ID 값이 작을수록 투표 마감이 임박한 것으로 볼 수 있다.

이 방법의 구현은 간단하다. value를 저장할 때 투표 ID 값을 그대로 저장하는 것이 아니라, Long.MAX_VALUE에서 투표 ID 값을 뺀 결과를 저장하는 것이다. score가 같을 경우, value 값이 사전적으로 큰 순서대로 정렬되는데 현재 value의 맨 앞 필드가 투표 ID이므로, 이 방법을 사용하면 투표 ID가 큰 순서로 정렬될 것이다. 즉, 원래의 투표 ID가 작은 순서로 정렬되고, 그 순서는 투표 마감이 임박한 순이다.

여기서 Long.MAX_VALUE에서 투표 ID를 빼는 이유는 투표 ID가 Long 타입이고, 가능한 가장 큰 값에서 빼야 투표 ID가 작을수록 value 값이 크게 만들 수 있기 때문이다.

@Yiseull Yiseull added the Redis label Feb 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant