2009-05-12 2 views
4

내가 작은 일을위한 포인트 게임을 만드는거야, 그래서 같은 스키마가 있습니다비정규 화 된 스키마를 최신으로 유지하는 최선의 방법은 무엇입니까?

create table points (
    id int, 
    points int, 
    reason varchar(10) 
) 

와 사용자가 가지고있는 점의 수를 얻는 것은 간단하다 :

select sum(points) as total from points where id = ? 

그러나 성능은 점 테이블이 확장됨에 따라 점점 더 많은 문제가되었습니다. 다음과 같이하고 싶습니다.

create table pointtotal (
    id int, 
    totalpoints int 
) 

동기화 상태를 유지하는 가장 좋은 방법은 무엇입니까? 모든 변경 사항에 대해 점수를 업데이트하려고합니까? 매일 스크립트를 실행합니까?

(I 오른쪽 키가 가정 - 그들이 편의상 탈락했다)

편집 : 여기

내가 왼쪽으로 몇 가지 특성이 있지만 도움이 될해야합니다

삽입/업데이트에 포인트가 모두 빈번하지는 않습니다. 많은 수의 항목이 있으며 많은 요청이 있습니다. 키는 꽤 사소한 것으로 보입니다.

+0

백엔드 언어는 Perl이지만 중요한지 확실하지 않습니다. – Timmy

답변

8

모범 사례은 정규화 된 데이터베이스 스키마를 사용합니다. 그런 다음 DBMS는 최신 정보를 유지하므로 사용자는 필요하지 않습니다.

그러나 나는 비정규 화 된 디자인을 매력적으로 만드는 단점을 이해합니다. 이 경우 모범 사례는 모든 변경 사항에서 전체를 업데이트하는 것입니다. 트리거 을 조사하십시오. 이 방법의 장점은 전체 내용을 변경 내용과 동기화 할 수 있으므로 내용이 오래 되었는지 여부를 전혀 생각할 필요가 없다는 것입니다. 하나의 변경이 커밋되면 업데이트 된 총도 커밋됩니다.

그러나 이것은 동시 변경에 대한 약점이 있습니다. 동일한 합계에 대한 동시 변경을 수용해야하고 합계가 "결국 일관성이 있음"을 허용 할 수있는 경우 주기적 재 계산을 사용하면 한 번에 하나의 프로세스 만 전체를 변경한다는 것을 확신 할 수 있습니다.

캐시 합계가 데이터베이스 외부에있는 것이 좋습니다. memcached 또는 응용 프로그램 변수에 저장되므로 값을 표시해야 할 때마다 데이터베이스에 접근 할 필요가 없습니다.


쿼리 "select sum(points) as total from points where id = ?"해야 하지 당신이 행의 거대한 숫자와 요청이 많은 경우에도 2 초 정도 걸릴.

(id, points) 이상으로 정의한 covering index이있는 경우 쿼리는 테이블에서 데이터를 전혀 읽지 않고 결과를 생성 할 수 있습니다. 그것은 인덱스 자체에서 값을 읽음으로써 전체를 계산할 수 있습니다. EXPLAIN을 사용하여 쿼리를 분석하고 추가 열의 "인덱스 사용"참고를 찾으십시오.

CREATE TABLE Points (
    id  INT, 
    points INT, 
    reason VARCHAR(10), 
    KEY id (id,points) 
); 

EXPLAIN SELECT SUM(points) AS total FROM Points WHERE id = 1; 

+----+-------------+--------+------+---------------+------+---------+-------+------+--------------------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra     | 
+----+-------------+--------+------+---------------+------+---------+-------+------+--------------------------+ 
| 1 | SIMPLE  | points | ref | id   | id | 5  | const | 9 | Using where; Using index | 
+----+-------------+--------+------+---------------+------+---------+-------+------+--------------------------+ 
+0

이상적이지만, 사람들이 2 초 동안 쿼리를 기다리도록 유도하십시오! – Timmy

+2

"id =? 인 지점에서 합계를 선택하십시오." 2 초가 걸리지 않아야합니다. –

+0

트리거가 도움이 될 수 있습니다. 삽입/업데이트가 자주 발생하지 않는다는 것에 대해서는 언급하지 않았습니다. – Timmy

2

반드시 기본 테이블을 정규화 된 상태로 유지하십시오.잠재적으로 하루가 지난 데이터를 처리 할 수 ​​있다면 스크립트를 실행하여 일정을 잡을 수 있습니다. 일정을 잡아서 새 테이블을 채우고 채울 수 있습니다. 소스 테이블에서 매일 밤 재현하는 것이 가장 좋습니다. 두 테이블 간의 불일치를 막을 수 있습니다.

id가 색인 된 필드가있는 레코드가 매우 빨리 합쳐져야하므로 레코드의 크기가 매우 느리거나 레코드 수가 매우 커야합니다. 몇 초 후에 사용자 응답 시간을 향상시킬 수 있다면 DB purists가 반대하더라도 롤업 테이블을 사용하지 않아도됩니다.

+0

레코드 수와 요청 수가 많습니다. – Timmy

1

동일한 테이블에 여분의 totalpoints 열이 있고 모든 행 작성/업데이트에 대한 totalpoints 값을 만들거나 업데이트하십시오.

특정 레코드의 총점이 필요한 경우 총점을 계산하지 않고 값을 조회 할 수 있습니다. 예를 들어 totalpoint의 마지막 값이 필요한 경우 다음과 같이 얻을 수 있습니다.

SELECT totalpoint FROM point ORDER BY id DESC LIMIT 1; 
1

또 다른 접근 방식이 있습니다. 캐싱. 단 몇 초 또는 몇 분 동안 캐시 된 경우에도 자주 액세스하는 가치가 있습니다. 그리고 cache-fetch와 cache-update를 분리 할 수 ​​있습니다. 그런 식으로 합리적인 현재 값은 항상 일정 시간에 반환됩니다. 까다로운 부분은 가져 오기가 업데이트를 수행하는 새로운 프로세스를 생성하는 것입니다.

1

데이터 액세스 및 수정에 사용하는 레이어를 만드는 것이 좋습니다. 이러한 DB 액세스 기능을 사용하면 모든 테이블에서 데이터 유지 관리를 캡슐화하여 중복 데이터를 동기화 할 수 있습니다.

1

매우 복잡하지 않으므로이 경우 어느 방향 으로든 갈 수 있습니다.

데이터를 일시적으로 일치시키지 않고 충분한 중복성을 확보하고 주기적으로 불일치를 해결하는 것이 좋습니다. 그러나 주기적 프로세스의 조기 실행을 장려하는 방아쇠 메커니즘을 갖추는 데 아무런 해가 없습니다.

일을 일관되게 유지하기 위해 이벤트 기반 알림 스타일 코드를 사용하는 것이 더 복잡한 경우 코드를 크게 복잡하게 만들고 인증을 어렵게 만들 수 있기 때문에 이런 식으로 느낍니다.

1

또한 다른보고 스키마를 만들고 계산을 수행하는 프로세스를 통해 일정 간격으로 다시로드 할 수도 있습니다. 이것은 실시간 정보에는 적용 할 수 없지만 일을하는 표준 방법입니다.

관련 문제