정렬 된 목록을 저장하기위한 데이터베이스를 디자인하는 방법은 무엇입니까?


42

정렬 된 목록을 데이터베이스 안에 저장하려고합니다. 다음 작업을 효율적으로 수행하고 싶습니다.

  1. 삽입 (x)-테이블에 레코드 x 삽입
  2. 삭제 (x)-테이블에서 레코드 x 삭제
  3. Before (x, n)-정렬 된 목록에서 레코드 x 앞에있는 'n'레코드를 반환합니다.
  4. After (x, n)-x 레코드 다음에 오는 'n'레코드를 정렬 된 목록으로 반환합니다.
  5. First (n)-정렬 된 목록에서 첫 번째 'n'레코드를 반환합니다.
  6. Last (n)-정렬 된 목록에서 마지막 'n'레코드를 반환합니다.
  7. 비교 (x, y)-테이블에서 두 개의 레코드 x와 y가 주어지면 x> y인지 찾으십시오.

내가 생각할 수있는 간단한 방법은 일종의 '순위'속성을 테이블에 저장하고 해당 속성을 정렬하여 쿼리하는 것입니다. 그러나이 방법에서는 순위가있는 레코드를 삽입 / 수정하는 데 많은 비용이 소요됩니다. 더 좋은 방법이 있습니까?

특히 Amazon의 SimpleDB를 사용하여 테이블을 구현하려고합니다. 그러나 관계형 데이터베이스에 대한 일반적인 대답도 도움이되어야합니다.

로드 프로파일 업데이트 :

웹 응용 프로그램을 위해 이것을 계획하고 있기 때문에 응용 프로그램을 사용하는 사용자 수에 따라 다릅니다.

100k 명의 활성 사용자가있는 경우 (슈퍼 낙관주의 : P) 하루에 대략적인 예상 수치는 다음과 같습니다.

500k 선택, 100k 삽입 및 삭제, 500k 업데이트

나는 테이블이 총 500k까지 자랄 것으로 기대합니다.

업데이트, 삽입 및 비교 작업을 최적화하려고합니다. 항목의 순위가 지속적으로 변경되므로 테이블을 업데이트해야합니다.


예상되는로드 프로파일을 조금 정교하게하십시오. 하루에 몇 개의 선택 / 삽입 / 업데이트가 있습니까? 어떤 작업을 가장 최적화 하시겠습니까? 하루에 테이블이 얼마나 커지거나 전체적으로 커질 것으로 예상하십니까?
Nick Chammas

이것은 플레이어 순위 보드입니까? 어쨌든, 귀하의 예상 하중 프로파일을 기반으로 한 피드백으로 아래 답변을 업데이트했습니다.
Nick Chammas

아니 그것은 플레이어 순위 보드가 아닙니다.
chitti

어떤 접근법을 사용하셨습니까?
Nick Chammas

나는 여기에 무엇을 요구하는지 또는 당신이해야 할 일의 세탁 목록에서 무엇을 할 필요가 있는지조차 확실하지 않습니다.
Evan Carroll

답변:


22

순위가 완전히 임의적이지는 않지만 다른 속성 (예 : 이름, 플레이어 점수 등)에서 파생 된 경우 Joel의 답변을 잘 살펴보십시오 .

이 경우 이다 데이터의 임의의 특성, 그 레코드의 테이블의 열로 저장해야합니다. Amazon의 SimpleDB가 일반적인 RDBMS와 유사하다고 가정하면이 열을 인덱싱하고 적절한 인덱싱 전략으로 위의 모든 쿼리를 빠르게 충족시킬 수 있습니다. 이것은 RDBMS에 정상입니다.

높은 삽입 및 업데이트 활동과 상대적으로 높은 읽기 활동이 예상되는 경우 다음을 수행하는 것이 좋습니다.

  • 특히 쿼리의 대다수가 순위에 위배되는 경우 순위에 테이블을 클러스터링하십시오. 그렇지 않은 경우 또는 SimpleDB에서 클러스터링 키를 선택할 수없는 경우 순위를 선행 열로 사용하여 색인을 작성하십시오. 이것은 쿼리 3-6을 만족시킬 것입니다.
  • 먼저 레코드의 인덱스가 순위를 매긴 다음 순위를 매 깁니다 (또는 SQL Server 세계에서는 레코드 및 INCLUDE-ing 순위 또는 순위가 클러스터 된 경우 레코드 만)하면 쿼리 7이 충족됩니다.
  • 데이터를 적절하게 간격을 두어 (예 : FILLFACTORSQL Server에서 설정) 작업 1과 2를 최적화 할 수 있습니다 . 순위에 클러스터링하는 경우 특히 중요합니다.
  • 순위를 삽입하거나 업데이트 할 때 순위 삽입 또는 업데이트를 수용하기 위해 기존 레코드의 순위를 다시 매길 가능성을 최소화하기 위해 가능한 한 순위 번호 사이의 간격을 최대한 유지하십시오. 예를 들어, 1000 단계로 레코드 순위를 매기는 경우 변경 횟수와 삽입 횟수가 절반 정도 인 충분한 공간을 최소한으로 남겨두고 해당 변경 사항과 직접 관련이없는 레코드의 순위를 다시 매겨 야합니다.
  • 매일 밤 모든 레코드의 순위를 다시 매겨 그들 사이의 순위 차이를 재설정합니다.
  • 기존 레코드 수를 기준으로 예상되는 삽입 또는 업데이트 수를 수용 할 수 있도록 대량 재 랭킹 빈도와 순위 간격 크기를 조정할 수 있습니다. 따라서 100K 레코드가 있고 삽입 및 업데이트가 10 %가 될 것으로 예상되는 경우 10K 새로운 순위를위한 충분한 공간을두고 밤마다 다시 순위를 정하십시오.
  • 500K 레코드의 순위를 다시 매기는 것은 비용이 많이 드는 작업이지만 하루 또는 주외 근무 시간에 한 번 수행하면 데이터베이스에 적합합니다. 순위 차이를 유지하기위한이 시간 외 대량 재 순위 지정은 정규 및 피크 시간 동안 각 순위 업데이트 또는 삽입에 대해 많은 레코드를 다시 지정해야하는 시간을 절약 해줍니다.

100K + 크기의 테이블에서 100K + 읽기를 예상하는 경우 링크 된 목록 접근 방식을 사용하지 않는 것이 좋습니다. 해당 크기에 맞게 확장되지 않습니다.


순위를 수정할 수 있습니다. 나는 계급이 지속적으로 변하고 새로운 기록이 지속적으로 삽입되기를 기대하고 있습니다. 순위가있는 새 요소를 삽입 할 때 새 레코드 아래의 모든 레코드 순위를 정렬 순서로 변경 해야하는 경우가 걱정됩니다. 데이터베이스에 수천 개의 레코드가있을 때 비용이 많이 드는 작업입니까?
chitti

@chitti-아, 그건 걱정입니다. 순위를 정돈하고 (예 : 0, 1000, 2000, 3000, ...) 순위 격차가 채워지면 정기적으로 모든 레코드의 순위를 다시 매길 수 있습니다. 그러나 수만 개가 넘는 레코드를 기대하는 경우에는 확장 할 수 없습니다.
Nick Chammas

1
@chitti-이것은 실제로 다소 재미있다. 이는 데이터베이스 엔진이 데이터를 추가하고 변경함에 따라 데이터를 정렬하고 재정렬하기 때문에 데이터를 인덱싱 할 때 처리하는 문제입니다. 당신이 보면 FILLFACTOR당신은 내가 순위 변경 및 삽입을위한 공간을 만드는 기술 단지 계급 격차로, 기본적으로 인덱스의 레코드에 대한 여분의 공간을 창조하기위한 것입니다 볼 수 있습니다.
Nick Chammas

2
업데이트 된 답변에 감사드립니다. '순위'는 내 데이터의 임의의 속성입니다. 나는 사용자 정의 인덱스 열이 내가 원하는 것임을 거의 확신한다. 비슷한 질문 으로이 SO 링크 를 확인하십시오 . 최상위 답변은 이러한 순위 열을 처리하는 방법에 대한 권장 사항을 제공합니다.
chitti

@chitti - 허용 대답 하도록 질문에 중대하다. 여기에 자세히 설명한 것과 동일한 접근 방법을 제안하며 순위 대신에 유연성을 크게 확장하기 위해 정수 대신 소수를 사용하는 추가 제안이 있습니다. 좋은 발견.
Nick Chammas

13

나는 일반적으로 당신이 설명하는 "순위"방법을 사용합니다. 항목을 재정렬해야 할 때 행을 업데이트하지 않고 종종 목록의 모든 레코드를 삭제하고 새 항목을 적절한 순서로 다시 삽입하는 데 어려움을 겪었습니다. 이 방법은 검색에 최적화되어 있습니다.

다른 방법은 테이블에서 "전임자"리플 렉 티브 외래 키 열을 사용하여 레코드를 연결된 목록으로 모델링하는 것입니다.

ID   setID   item       predecessor
---  ------  ------     ------------
1    1       Apple      null
2    1       Orange     1
3    2       Cucumber   null
4    1       Pear       2
5    1       Grape      4
6    2       Carrot     3

오버 헤드가 거의없이 목록을 쉽게 검색하고 항목을 추가 및 제거 할 수 있지만 올바른 순서로 레코드를 가져 오는 것은 까다로울 수 있습니다. 아마도 별칭이 많은 테이블 조인이 많은 단일 쿼리에서 영리한 방법이있을 수 있습니다.

트리 스타일 관계 (범주, 폴더, 집합 및 하위 집합)를 모델링 할 때이 후자의 접근 방식을 자주 사용합니다. 나는 일반적으로 내 응용 프로그램의 전체 트리를 재구성하는 일종의 재귀 함수를 가지고 있습니다.


2
링크 된리스트 모델은 깔끔합니다. SQL Server에서 이러한 계층 구조를 순서대로 검색하려면 재귀 적 CTE를 사용해야합니다 .
Nick Chammas

그러나 계층 구조를 구축하는 것은 키가 큰 테이블의 경우 비용이 많이 듭니다. 장점은 순위 변경 / 삽입 등을 쉽게 할 수 있다는 것입니다. chitti의 예상로드 프로파일에 따라 실제로 이것이 최선의 방법 일 수 있습니다.
Nick Chammas

연결된 목록 옵션은 비교를 제외한 모든 작업에 가장 적합합니다. 비교되는 두 요소 사이의 경로를 추적하지 않고 비교를 어떻게 구현할 수 있습니까?
chitti

항목의 ID가 있으면 Compare ()의 의미를 잘못 이해하지 않는 한 Compare ()가 간단하다고 생각합니다. "x> y 인 경우 찾기"라고 말했을 때 "x가 y보다 앞에 오는 경우 찾기"를 의미 했습니까? 나는 목록을 걷는 사용자 정의 인덱스 또는 저장 프로 시저가 없다면 쉽게 볼 수 없다 (또는 @ Nick이 언급 한 흥미로운 CTE 기능).
bpanulla

5
이 유형의 솔루션은 또한 그래프 데이터 모델 ( en.wikipedia.org/wiki/Graph_theory )과 비슷합니다 . 그래프 노드 및 에지를 저장하도록 최적화 된 스토리지 시스템이 RDBMS보다 더 나은 솔루션 일 수 있습니다. Neo4J와 같은 트리플 및 쿼드 스토어와 그래프 데이터베이스가 이에 능숙합니다.
bpanulla

6

순위를 계산 한 다음 인덱스를 작성하는 데 사용되는 속성저장하는 것이 좋습니다. 데이터베이스가 데이터를 물리적으로 순위가 매겨진 순서로 저장하거나 수동으로 관리되는 연결 목록을 사용하도록 강요하는 대신 데이터베이스 엔진이 설계된 작업을 수행하게하지 않는 이유는 무엇입니까?


2
'순위를 계산하는 데 사용되는 속성'이 임의적이면 어떻게됩니까? 예 : 사용자의 임의의 동작에 따라 순서가 바뀌는 장바구니 항목 집합입니다.
chitti

순위가 임의적이라고 말할 때, 무슨 뜻입니까? 순위를 계산하는 데 사용하는 알고리즘이 있어야합니다. 예 : "장바구니 항목 기반"-방법에 따라? 순위 계산을위한 드라이버 인 데이터베이스에 무언가가 저장되어 있어야합니다. 여러 가지의 조합이 될 수 있지만 이러한 것은 고객 테이블 또는 고객 관련 테이블에 저장해야합니다. 데이터에 있으면이를 계산하는 함수를 작성할 수 있습니다. 계산할 수 있으면 저장하고 색인을 작성할 수 있습니다.
Joel Brown

장바구니에서 품목의 순서를 유지해야하며 웹 UI를 사용하여 사용자가 주문을 '임의로'변경할 수 있다고 가정합니다. 이러한 항목 목록을 데이터베이스에 어떻게 저장하고 정렬 순서를 어떻게 유지 하시겠습니까?
chitti

쇼핑 카트에서 항목의 순서를 "임의로 변경"함으로써 사용자가 올바르게 이해하면 사용자가 목록에서 항목을 위아래로 끌어서 원하는 위치에 놓을 수 있음을 의미합니다. 나는 그것이 약간의 생각으로 나를 때리는 것 같아요. 왜 사용자가 그렇게할까요? 그들이 할 수 있다면, 많이할까요? 장바구니에서 간단한 일련의 항목을 사용하는 것이 실제로 많은 성능 문제가 있습니까? 카트의 항목 수부터 순서대로 FK까지의 순서 번호가 필요한 색인을 제공하는 것 같습니다. 항목이 끌 리면 항목을 업데이트하십시오.
Joel Brown

3
쇼핑 카트는 '순위'가 임의적 일 수있는 경우가 있음을 보여주기 위해 제시 한 예일뿐입니다. 좋은 예가 아닐 수도 있습니다. netflix DVD 대기열이 더 좋은 예일 수 있습니다. 인수를 위해 사용자가 임의로 재정렬 할 수있는 100k 개의 항목이있는 netflix 대기열을 상상하고 1 분마다 수행하십시오. 이 가상 응용 프로그램에서 순서가 지정된 영화 목록을 저장할 데이터베이스를 어떻게 설계 하시겠습니까?
chitti

1

이는 simpleDB와 같은 비 RDBMS의 한계입니다. 필요한 기능은 simpleDB의 DB 쪽에서 구현할 수 없으며 프로그래밍 쪽 / 응용 프로그램에서 구현해야합니다.

와 같은 RDBMS의 SQL server경우 필요한 기능은 클러스터형 인덱스의 기초입니다.

  • 삽입 (x)-테이블에 레코드 x를 삽입> 단순 삽입.
  • 삭제 (x)-테이블에서 레코드 x 삭제> 단순 삭제.
  • Before (x, n)-정렬 된 목록에서 레코드 x 앞에있는 'n'레코드를 반환합니다. > x가 값보다 작은 상위 n 개 결과를 선택하고 절별로 정렬하십시오.

  • After (x, n)-x 레코드 다음에 오는 'n'레코드를 정렬 된 목록으로 반환합니다. > x가 값보다 큰 상위 n 개의 결과를 선택하고 절별로 정렬하십시오.

  • First (n)-정렬 된 목록에서 첫 번째 'n'레코드를 반환합니다. > 상위 n 개의 결과를 선택하십시오.

  • Last (n)-정렬 된 목록에서 마지막 'n'레코드를 반환합니다. > desc로 주문 후 상위 n 개의 결과를 선택하십시오.

  • 비교 (x, y)-테이블에서 두 개의 레코드 x와 y가 주어지면 x> y인지 찾으십시오. > TSQL IF 문.

SimpleDB는 자동 색인, 정렬 및 기본 쿼리 언어를 제공 합니다. RDBMS를 선택해도 문제가 남아 있습니다. 문제는 데이터베이스의 데이터 순위가 임의로 변경되어 색인 할 수있는 단일 속성 (사용자 지정 순위 열을 사용하지 않는 한)으로 캡처 할 수 없기 때문입니다.
chitti

0

다음은 모든 삽입 후 Postgres 테이블의 순위를 재조정하는 데 사용한 것입니다.

CREATE OR REPLACE FUNCTION re_rank_list() RETURNS trigger AS $re_rank_list$
DECLARE
    temprow record;
    row_idx integer := 1;    
BEGIN
    FOR temprow IN
    SELECT * FROM your_schema.your_list WHERE list_id = NEW.list_id ORDER BY rank ASC
    LOOP
        UPDATE your_schema.your_list SET rank = row_idx * 100 WHERE id = temprow.id;
        row_idx := row_idx + 1;
    END LOOP;
    RETURN NEW;
END;
$re_rank_list$ LANGUAGE plpgsql;


CREATE TRIGGER re_rank_list AFTER UPDATE ON your_schema.your_list_value
    FOR EACH ROW 
    WHEN (pg_trigger_depth() = 0)
    EXECUTE PROCEDURE re_rank_list();

필자의 유스 케이스의 경우 성능은 문제가되지 않지만 결코 깨지거나 이상하게 행동하지 않을 것이라는 확신이 중요하다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.