2009-12-03 4 views
1

40 개의 가능한 메시지 풀에서 임의의 메시지를 보내는 기능을 추가하는 약 30,000 명의 회원이있는 사이트가 있습니다. 회원은 같은 메시지를 두 번받을 수 없습니다.이것은 비정규 화의 경우입니까?

하나의 테이블에는 40 개의 메시지가 있고 다른 테이블에는 메시지와 구성원 간의 다 대다 관계가 매핑되어 있습니다.

cron 스크립트가 매일 실행되고 30,000에서 구성원을 선택하고 40에서 메시지를 선택한 다음이 메시지가 이전에이 사용자에게 전송되었는지 확인합니다. 그렇지 않으면 메시지를 전송합니다. '예'일 경우,이 구성원이 아직 수신하지 않은 메시지를 찾을 때까지 조회를 다시 실행합니다.

내가 지금 걱정하는 것은이 m-m 테이블이 매우 커지게된다는 것입니다. 30,000 명의 회원과 40 개의 메시지에서 우리는 이미 보내지 않은 메시지를 찾기 위해 검색해야하는 120 만 개의 행을 가지고 있습니다.

비정규 화의 경우입니까? 회원 테이블에서 메시지가 전송 될 때마다 1 플래그가 추가되는 40 개의 열 (message_1, message_2 ... message_40)을 추가 할 수 있습니다. 내가 틀리지 않으면 cron 스크립트의 쿼리를 훨씬 빠르게 실행할 수 있습니다.

?

답변

4

을,하지만 당신은 모든 메시지를 선택한 경우는 빠른 방법은되지 않을 것 아직 사용자에게 보내지 않은 다음 임의로 선택하십시오.

SELECT 
    CONCAT_WS(',', messages.ids) unsent_messages, 
    user.id user 
FROM 
    messages, 
    user 
WHERE 
    messages.id NOT IN (
     SELECT 
      id 
     FROM 
      sent_messages 
     WHERE 
      user.id = sent_messages.user 
    ) 
GROUP BY ids 
+0

동의합니다. 아래에서 조금 더 자세히 설명했습니다. –

+0

CONCAT_WS를 사용하여 첫 번째 SELECT 문에서 수행중인 작업을 명확하게 설명해 주시겠습니까? – stef

+0

글쎄, 쉼표 구분자를 사용하여 보내지 않은 모든 메시지의 ID를 연결합니다. 이렇게하면 모든 사용자에 대한 ID 목록을 하나의 SELECT에 반환 할 수 있습니다. –

0

정규화는 중복성을 줄여 주며 대용량의 데이터가있는 경우 수행 할 작업입니다. 당신은 비정규화할 필요가 없습니다. 멤버와 메시지 사이에 M-to-M 테이블이 있어야합니다.

M 대 M 데이터 증가에 따라 이전 데이터를 보관할 수 있습니다. 귀하의 cron 작업이이 작업을 위해 매일 실행되고 현재 날짜의 데이터 만 고려하기 때문에 어떤 충돌도 보이지 않습니다. 따라서 매주 M-to-M 테이블 데이터를 보관할 수 있습니다.

멤버 테이블에 coloumn을 추가하여 비정규 화하면 유지 관리 문제가 발생할 것으로 생각됩니다. 나는 같은 것을 추천하지 않는다. 오래된 데이터를 보관하면 문제를 방지 할 수 있습니다.

+0

이전 데이터를 보관할 때 40 개의 메시지를 모두받은 사용자의 레코드를 삭제한다는 의미입니까? – stef

+0

자주 액세스하지 않는 다른 데이터베이스로 데이터를 이동하여 주 응용 프로그램이 필요한 데이터 만 가지고 있으며 보관 된 테이블에 이전 데이터를 유지하는 M-2-M 테이블에서 잘 작동하도록합니다. –

1

보낸 사람 ID를 구성원 테이블의 varchar 필드에 추가 할 수도 있습니다. 매너가 좋음에도 불구하고 특정 멤버에 대해 아직 보내지 않은 메시지를 얻기 위해 하나의 명령문을 쉽게 사용할 수 있습니다. 다만이 같은

(당신과 함께 IDS 둘러싸고 경우 '-') 나는 그 원래의 질문에 대답하지 않습니다 알고

SELECT message.id 
FROM member, message 
WHERE member.id = 2321 
AND member.sentmessages NOT LIKE '%-' && id && '-%' 
0

당신은 당신의 m-m 테이블의 임의의 문자열과 보낸 마지막 메시지의 오프셋 (offset)에 대한 포인터를 미리 할당에 의해 무작위로 메시지를 보내는의 효과를 얻을 수 있습니다

이 의사 MySQL은 여기를 참조하십시오.

더욱 상세하게는, 열로
memberId, messageIdList 숯 (80) 또는 VARCHAR,
lastMessage INT,
기본 키 memberId이다 MemberMessages 테이블을 생성한다.

cron 작업에 대한 의사 코드는 다음과 같습니다.

하나. 회원에 대한 다음 메시지를 선택하십시오. 이 멤버의 MemberMessages에 행이 없으면 2 단계로 이동하십시오. 다음 메시지를 선택하는 SQL은

select substr(messageIdList, 2*lastMessage + 1, 2) as nextMessageId 
from MemberMessages 
where member_id = ? 

는 다음 경우는 0으로 재설정하는 (39)에 도달하지 않는 한 lastMessage가 1 씩 증가 업데이트

nextMessageId

에 의해 확인 된 메시지를 보낼 것 같습니다.

update MemberMessages 
set lastMessage = MOD(lastMessage + 1, 40) 
where member_id = ? 

두. messageIds의 무작위 목록을 다음과 같은 두 개의 문자열로 만듭니다. 2117390740... 이것은 임의의 메시지 ID를 80 자 문자열로 사용하는 목록입니다. member_id 설정에 대한 MemberMessages 행을 80 자 문자열에 삽입하고 last_message을 1로 설정하십시오.

목록에서 첫 번째 커플을 식별 한 메시지를 구성원에게 보냅니다.

0

(보내지 않음) 메시지 만 저장할 수 있습니다. 이는 구성원이나 메시지 유형 (외래 키 및 트리거로 자동화 할 수없는 항목)을 추가하거나 제거 할 때 추가 유지 관리를 의미하지만 전달을 단순화합니다. 각 사용자의 임의의 줄을 선택하고 메시지를 보내고 줄을 제거합니다. 또한 메시지가 전송 될 때 데이터베이스 크기가 작아집니다 .-)

1

1.2M 행당 @ 8 바이트 (오버 헤드)는별로 많지 않습니다. 너무 작기 때문에 인덱싱이 필요하지 않을 것입니다. (물론 당신이해야합니다.)

0

대기열/힙 종류를 만들 수 있습니다.

ReceivedMessages

다음
UserId 
MessageId 

:

보낼 멤버를 선택 메시지를 선택 :

SELECT * FROM Messages WHERE MessageId NOT IN (SELECT MessageId FROM ReceivedMessages WHERE UserId = @UserId) LIMIT 1 

다음 ReceivedMessages

에의 MessageID 및 사용자 아이디를 삽입하고 논리를 보내려면 어떻게합니까 여기

도움이 되었기를 바랍니다.

0

"임의"를 원하는 임의의 방법에 따라이 작업을 수행하는 더 쉬운 방법이 있습니다.

하루의 시작 부분에 사용자에게 보낼 메시지의 순서를 설명하는 [0..39]라는 배열을 뒤섞어 놓는 것을 고려하십시오.

또한 사용자에게 메시지를 보내는 데 사용되는 최대 40 개의 Cron 작업이 있다고 가정합니다. N 번째 cron 작업을 감안할 때, 그리고 ID는 선택된 사용자 ID, 숫자, 당신은 보낼 M, 메시지의 인덱스를 선택할 수 있습니다

M = (A [N] + ID) % 40

이를 (A [N]이 다르기 때문에) 같은 ID로 동일한 메시지를 두 번받지 못하고 무작위로 선택된 두 명의 사용자가 동일한 메시지를받을 확률이 1/40입니다. 좀 더 "임의성"을 원한다면 잠재적으로 다중 배열을 사용할 수 있습니다.