문제

여가 시간에는 데이터베이스 백엔드로 소규모 멀티플레이어 게임을 작성하기 시작했습니다.저는 플레이어 로그인 정보를 다른 게임 정보(인벤토리, 통계, 상태)와 분리하려고 했는데 친구가 이 정보를 제시한 것은 최선의 아이디어가 아닐 수도 있습니다.

모든 것을 한 테이블에 모아두는 것이 더 낫지 않을까요?

도움이 되었습니까?

해결책

성능을 무시하는 것으로 시작하고 가능한 가장 논리적이고 이해하기 쉬운 데이터베이스 디자인을 만들 수 있습니다. 플레이어와 게임 객체 (검? 체스 조각?)가 개념적으로 별도의 것들이라면 별도의 테이블에 넣으십시오. 플레이어가 물건을 가지고 다닐 수 있다면 "플레이어"테이블을 참조하는 "Things"테이블에 외국 키를 넣습니다. 등등.

그런 다음 수백 명의 플레이어와 수천 가지가 있고 플레이어가 게임에서 뛰어 다니면서 데이터베이스 검색이 필요한 작업을 수행하면 디자인이됩니다. 아직 적절한 인덱스를 추가하면 충분히 빠르십시오.

물론, 수천 명의 동시 플레이어를 계획한다면, 각각 각각은 매 초마다 자신의 물건을 재고하거나 다른 엄청난 데이터베이스 검색 (그래픽 엔진으로 렌더링 된 각 프레임에 대한 검색)을 생각해야합니다. 성능. 그러나이 경우 일반적인 디스크 기반 관계형 데이터베이스는 테이블을 디자인하는 방법에 관계없이 어쨌든 빠르지 않습니다.

다른 팁

관계형 데이터베이스는 세트에서 작동하며 데이터베이스 쿼리는 없음 ( "찾을 수 없음") 또는 더 많은 결과를 제공 할 수 있습니다. 관계형 데이터베이스는 쿼리 (관계)를 실행하여 항상 정확히 하나의 결과를 제공하기위한 것이 아닙니다.

실생활이 있다고 언급하고 싶다고 말했습니다. ;-). 일대일 관계 ~할 것 같다 의미가 있습니다. 즉, 테이블의 열 수가 임계 레벨에 도달하면이 테이블을 분할하는 것이 더 빠를 수 있습니다. 그러나 이러한 종류의 조건은 실제로 드 rare니다.

실제로 테이블을 분할 할 필요가 없다면 그렇게하지 마십시오. 적어도 나는 당신의 경우 모든 데이터를 얻기 위해 두 개의 테이블 이상을 선택해야한다고 가정합니다. 이것은 아마도 한 테이블에 모두 저장하는 것보다 느립니다. 그리고 당신의 프로그래밍 논리는 적어도 그렇게함으로써 조금 덜 읽을 수있게됩니다.

에디스는 말한다: 정규화에 대한 주석 : 글쎄, 나는 데이터베이스가 최대로 정규화 된 것을 보지 못했고 아무도 실제로 그것을 옵션으로 추천하지 않습니다. 어쨌든, 나중에 열이 정규화 될지 정확히 알지 못하면 (예 : 다른 테이블에 넣음) 나중에 "테이블"대신 하나의 테이블 로이 작업을 수행하는 것이 더 쉽습니다. "

유지할지 여부

플레이어 로그인 정보 [재고, 통계 및 상태)에서 다른 사람과 별도로 [별도)

종종 정규화의 문제입니다.Wikipedia(제3정규형, 비정규화) 정의.이를 여러 테이블로 분리할지 여부는 저장되는 데이터에 따라 다릅니다.질문을 확장하면 이것이 구체화됩니다.우리가 저장하려는 데이터는 다음과 같습니다.

  • 사용자 이름:사용자가 시스템에 로그인할 수 있도록 하는 고유한 이름
  • 비밀번호:비밀번호:사용자의 고유성을 보장하는 사용자 이름과 연결됨
  • 이메일:사용자의 이메일 주소최근에 게임을 하지 않은 사용자가 게임에서 사용자를 '찔러' 볼 수 있도록 하기 위해
  • 성격:캐릭터에 대해 별도의 게임 내 이름이 있을 수 있음
  • 상태:현재 활성화된 플레이어 및/또는 캐릭터입니다.
  • 체력:캐릭터의 예시 통계

이를 단일 테이블로 저장할 수도 있고 여러 테이블로 저장할 수도 있습니다.

단일 테이블 예

플레이어가 이 게임 세계에 단일 캐릭터를 가지고 있다면 단일 테이블이 적절할 수 있습니다.예를 들어,

CREATE TABLE players(
  id         INTEGER NOT NULL,
  username   VARCHAR2(32) NOT NULL,
  password   VARCHAR2(32) NOT NULL,
  email      VARCHAR2(160),
  character  VARCHAR2(32) NOT NULL,
  status     VARCHAR2(32),
  hitpoints  INTEGER,

  CONSTRAINT pk_players PRIMARY KEY (uid)
);

이렇게 하면 플레이어 테이블에 대한 단일 쿼리를 통해 플레이어에 대한 모든 관련 정보를 찾을 수 있습니다.

다중 테이블 예

그러나 단일 플레이어가 여러 다른 캐릭터를 가질 수 있도록 하려면 이 데이터를 두 개의 다른 테이블에 분할해야 합니다.예를 들어 다음을 수행할 수 있습니다.

-- track information on the player
CREATE TABLE players(
  id         INTEGER NOT NULL,
  username   VARCHAR2(32) NOT NULL,
  password   VARCHAR2(32) NOT NULL,
  email      VARCHAR2(160),

  CONSTRAINT pk_players PRIMARY KEY (id)
);

-- track information on the character
CREATE TABLE characters(
  id         INTEGER NOT NULL,
  player_id  INTEGER NOT NULL,
  character  VARCHAR2(32) NOT NULL,
  status     VARCHAR2(32),
  hitpoints  INTEGER,

  CONSTRAINT pk_characters PRIMARY KEY (id)
);
-- foreign key reference
ALTER TABLE characters
  ADD CONSTRAINT characters__players
  FOREIGN KEY (player_id) REFERENCES players (id);

이 형식은 플레이어와 캐릭터 모두에 대한 정보를 볼 때 조인이 필요합니다.그러나 이렇게 하면 레코드 업데이트로 인해 불일치가 발생할 가능성이 줄어듭니다.후자의 인수는 일반적으로 여러 테이블을 옹호할 때 사용됩니다.예를 들어 단일 테이블 형식에 두 문자(gollum, frodo)가 있는 플레이어(LotRFan)가 있는 경우 사용자 이름, 비밀번호, 이메일 필드가 중복됩니다.플레이어가 자신의 이메일/비밀번호를 변경하고 싶다면 두 기록을 모두 수정해야 합니다.하나의 레코드만 수정된 경우 테이블은 일관성이 없게 됩니다.그러나 LotRFan이 다중 테이블 형식으로 자신의 비밀번호를 변경하려는 경우 테이블의 단일 행이 업데이트됩니다.

기타 고려 사항

단일 테이블을 사용할지 아니면 여러 테이블을 사용할지는 기본 데이터에 따라 다릅니다.여기에 설명되지 않았지만 다른 답변에서 언급된 것은 최적화가 종종 두 개의 테이블을 가져와 단일 테이블로 결합한다는 것입니다.

또한 앞으로 이루어질 변경 유형을 아는 것도 중요합니다.예를 들어, 여러 캐릭터를 보유할 수 있는 플레이어에 대해 단일 테이블을 사용하도록 선택하고 플레이어 테이블의 필드를 증가시켜 플레이어가 실행한 총 명령 수를 추적하려는 경우이로 인해 단일 테이블 예에서는 더 많은 행이 업데이트됩니다.

인벤토리는 어쨌든 다중 관계이므로 자체 테이블 (최소한 외국 키에 색인)이있을 수 있습니다.

또한 각 사용자마다 공개 물건과 분리 된 개인 물건 (예 : 다른 사람이 볼 수있는 것)을 원할 수도 있습니다. 많은 사람들이 당신의 공개 속성을 볼 수 있지만 개인의 속성을 볼 수 있기 때문에 더 나은 캐시 히트를 제공 할 수 있습니다.

1-1 관계는 수직 분할 일뿐입니다. 다른 것은 없습니다. 어떤 이유로 든 모든 데이터를 동일한 테이블에 저장하지 않으면하십시오 (여러 서버에서 분할을 위해).

보시다시피, 이것에 대한 일반적인 합의는 "그것이 의존한다"는 것입니다. @campbell과 다른 사람들이 언급 한 것처럼 성능/쿼리 최적화가 쉽게 떠 오릅니다.

그러나 당신이 구축하는 애플리케이션 별 데이터베이스의 종류는 전반적인 애플리케이션 설계를 고려하여 시작하는 것이 가장 좋습니다. 애플리케이션 별 데이터베이스 (많은 앱 또는보고 도구와 함께 사용하도록 설계된 데이터베이스와 반대로)의 경우 '이상적인'데이터 모델은 아마도 응용 프로그램 코드에서 구현 된 도메인 모델에 가장 잘 맵핑되는 것일 수 있습니다.

당신은 당신의 디자인을 호출 할 수 없을 수도 있습니다 MVC, 그리고 당신이 어떤 언어를 사용하고 있는지 모르겠지만, 당신은 논리 모델로 데이터 액세스를 랩핑하는 코드 나 클래스가있을 것으로 기대합니다. 이 레벨에서 응용 프로그램 디자인을 보는 방법은 데이터베이스가 어떻게 해야하는지에 대한 최상의 지표라고 생각합니다. 이상적으로 구조화하십시오.

일반적으로 이것은 데이터베이스 테이블에 도메인 객체를 일대일 매핑하는 것이 시작하기에 가장 좋은 장소입니다.

예를 들어 내가 의미하는 바를 설명하는 것이 가장 좋습니다.

사례 1 : 하나의 클래스 / 한 테이블

당신은 a 플레이어 수업. player.authenticate, player.logged_in?, player.status, player.hi_score, player.gold_credits. 이 모든 것이 단일 사용자에 대한 조치이거나 사용자의 단일 값 속성을 나타내는 한 단일 테이블은 논리적 응용 프로그램 설계 관점에서 시작점이어야합니다. 단일 클래스 (플레이어), 단일 테이블 (플레이어).

사례 2 : 다중 클래스 / 하나 이상의 테이블

어쩌면 나는 스위스-무장 나이프를 좋아하지 않을 것입니다 플레이어 수업 디자인이지만 측면에서 생각하는 것을 선호합니다. 사용자, 목록, 스코어 보드 그리고 계정. user.authenticate, user.logged_in?, user-> ac

도메인 모델에서 이러한 개념을 분리하면 내 코드가 더 명확하고 쉽게 쓸 수 있다고 생각할 것입니다. 이 경우 가장 좋은 시작점은 도메인 클래스를 개별 테이블에 직접 매핑하는 것입니다 : 사용자, 재고, 점수, 계정 등.

'이상적인'실용성을 만듭니다

도메인 객체를 테이블에 하나의 매핑이 이상적이고 논리적입니다 설계.

그것이 제가 선호하는 출발점입니다. 여러 클래스를 단일 테이블에 매핑하는 것처럼 박쥐에서 너무 똑똑해 지려고 노력하는 것은 피하는 것을 선호하는 문제를 초대하는 경향이 있습니다 (특히 교착 상태와 동시성 문제).

그러나 그것은 항상 이야기의 끝이 아닙니다. 물리적 데이터 모델을 최적화하기위한 별도의 활동을 의미 할 수있는 실질적인 고려 사항이 있습니다. 예를 들어 동시성/잠금 문제가 필요합니까? 행 크기가 너무 커지고 있습니까? 오버 헤드 인덱싱? 쿼리 성능 등

성능에 대해 생각할 때주의하십시오. 단지 한 테이블의 한 줄이 될 수 있다고해서 전체 행을 얻기 위해 한 번만 쿼리된다는 의미는 아닙니다. 멀티 우스터 게임 시나리오에는 다른 시간에 다른 플레이어 정보를 얻기 위해 일련의 통화가 포함될 수 있으며, 플레이어는 항상 '현재 가치'를 원합니다. 이는 매우 역동적 일 수 있습니다. 이는 매번 테이블에서 몇 개의 열만을 많이 요구할 수 있습니다 (이 경우 다중 테이블 디자인은 단일 테이블 디자인보다 빠를 수 있음).

나는 그들을 분리하는 것이 좋습니다. 데이터베이스의 이유가 아니라 개발 이유입니다.

플레이어 로그인 모듈을 코딩 할 때는 게임 정보에 대해 생각하고 싶지 않습니다. 그리고 Visa-versa. 테이블이 뚜렷한 경우 우려 사항을 분리하는 것이 더 쉬울 것입니다.

게임 정보 모듈에 플레이어 로그인 테이블과 함께 비즈니스 엉망이 없다는 사실로 요약됩니다.

아마도 쿼리 성능이 더 좋기 때문에이 상황에서 한 테이블에 넣을 수도 있습니다.

데이터 스토리지 오버 헤드를 줄이기 위해 정규화를 찾아야하는 중복 데이터 요소가있을 때만 다음 테이블 만 사용하려고합니다.

나는 동의한다 Thomas Padron-McCarthy의 답변:

수천 명의 동시 사용자의 경우를 해결하는 것은 문제가 아닙니다. 사람들이 게임을하게하는 것이 문제입니다. 가장 간단한 디자인으로 시작하십시오 - 게임을 완료하고, 작동하고, 재생 가능하며, 쉽게 확장 할 수 있습니다. 게임이 시작되면 정상화됩니다.

또한 인덱스는 데이터의 좁은보기를 포함하는 별도의 테이블로 볼 수 있음을 언급 할 가치가 있습니다.

사람들은 World of Warcraft에서 블리자드가 사용하는 디자인을 추론 할 수있었습니다. 플레이어가 켜진 퀘스트는 캐릭터와 함께 저장된 것 같습니다. 예 :

CREATE TABLE Players (
   PlayerID int,
   PlayerName varchar(50),
   ...
   Quest1ID int,
   Quest2ID int,
   Quest3ID int,
   ...
   Quest10ID int,
   ...
)

처음에는 최대 10 개의 퀘스트를 수행 할 수 있으며 나중에 25로 확장되었습니다.

CREATE TABLE Players (
   PlayerID int,
   PlayerName varchar(50),
   ...
   Quest1ID int,
   Quest2ID int,
   Quest3ID int,
   ...
   Quest25ID int,
   ...
)

이것은 분할 디자인과 대조적입니다.

CREATE TABLE Players (
   PlayerID int,
   PlayerName varchar(50),
   ...
)

CREATE TABLE PlayerQuests (
   PlayerID int,
   QuestID int
)

후자는 개념적으로 다루기가 훨씬 쉽고 임의의 수의 퀘스트를 허용합니다. 반면에 모든 정보를 얻으려면 플레이어와 플레이어 퀘스트에 합류해야합니다.

각 레코드 유형을 자체 테이블에 보관하는 것이 좋습니다.

플레이어 로그인은 한 유형의 레코드입니다. 인벤토리는 또 다른 것입니다. 대부분의 정보가 공유되지 않으므로 테이블을 공유해서는 안됩니다. 관련없는 정보를 분리하여 테이블을 간단하게 유지하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top