데이터베이스 디자인에서 불변성을 선호


26

Joshua Bloch의 Effective Java의 항목 중 하나는 클래스가 인스턴스를 가능한 한 적게, 바람직하게는 전혀 허용하지 않아야한다는 개념입니다.

종종 개체의 데이터는 어떤 형태의 데이터베이스에 유지됩니다. 이로 인해 데이터베이스 내에서 불변성의 아이디어, 특히 더 큰 시스템 내에서 단일 엔티티를 나타내는 테이블의 아이디어를 생각하게되었습니다.

내가 최근에 실험 한 것은 이러한 객체를 나타내는 행을 테이블에 업데이트하는 것을 최소화하려고 시도하고 가능한 한 많이 삽입을 시도하는 것입니다.

내가 최근에 실험하고 있었던 것의 구체적인 예. 나중에 추가 데이터가 포함 된 레코드를 추가 할 수 있다는 것을 알고 있으면 다음 두 테이블 정의와 같이이를 나타내는 다른 테이블을 만듭니다.

create table myObj (id integer, ...other_data... not null);
create table myObjSuppliment (id integer, myObjId integer, ...more_data... not null);

이 이름들이 구두가 아니라 단지 그 아이디어를 보여주기위한 것이 분명하다.

이것이 데이터 지속성 모델링에 대한 합리적인 접근입니까? 테이블에서 수행되는 업데이트, 특히 레코드가 처음 만들어 졌을 때 존재하지 않을 수있는 데이터에 대해 null을 채우는 것을 제한 할 가치가 있습니까? 이와 같은 접근법이 나중에 심각한 통증을 유발할 수있는 경우가 있습니까?


7
문제가없는 솔루션 인 것 같습니다 ... 업데이트를 피하기 위해 정교한 조정을 만드는 대신 업데이트해야합니다.
Fosco

솔루션에 대한 직관적 인 아이디어를 염두에두고 가능한 한 많은 사람들이이 솔루션을 실행하기를 원하고 이것이 내가 가진 문제에 대한 최상의 솔루션이 아닐 수 있다는 것을 깨닫는 것이 문제라고 생각합니다. 다른 곳에서 찾을 수 없다면 문제에 대해 다른 질문을 열 수 있습니다.
Ed Carrel

1
데이터베이스에서 업데이트를 피해야 할 이유가있을 수 있습니다. 그러나 이러한 이유가 발생하면 이는 최적화 문제 일 뿐이므로 문제가 있다는 증거 없이는 수행해서는 안됩니다.
dietbuddha

6
데이터베이스 내에서 불변성에 대한 강력한 주장이 있다고 생각합니다. 많은 문제를 해결합니다. 나는 부정적인 의견이 열린 사람들로부터 온 것이 아니라고 생각합니다. 전체 업데이트는 많은 문제의 원인입니다. 나는 우리가 모든 것을 거꾸로 가지고 있다고 주장 할 것이다. 전체 업데이트는 더 이상 존재하지 않는 문제에 대한 레거시 솔루션입니다. 저장은 싸다. 왜 그렇습니까? 버전 관리 시스템 인 감사 로그가있는 DB 시스템의 수는 분산 복제가 필요하며, 확장 복제에 대한 대기 시간을 지원하는 기능이 필요합니다. 불변성은이 모든 것을 해결합니다.
cirrus

@Fosco 일부 시스템은 데이터를 절대로 삭제하지 않아야합니다 (을 사용하여 포함 UPDATE). 의사의 의료 기록처럼.
이즈 카타

답변:


25

불변성의 주요 목적은 메모리의 데이터가 유효하지 않은 상태에있는 시간이 없도록 보장하는 것입니다. (다른 하나는 수학적 표기법이 대부분 정적이기 때문에 수정 불가능한 것이 수학적으로 개념화하고 모델링하기가 더 쉽기 때문입니다.) 메모리에서 다른 스레드가 작업하는 동안 데이터를 읽거나 쓰려고하면 손상 될 수 있습니다. 자체가 손상된 상태 일 수 있습니다. 다중 스레드 응용 프로그램에서 개체 필드에 여러 할당 작업이있는 경우 다른 스레드가 중간에 언젠가 작업하려고 시도 할 수 있습니다.

불변성은 먼저 모든 변경 사항을 메모리에 새로운 장소에 쓴 다음 객체에 대한 포인터를 다시 작성하여 새로운 객체를 가리키는 하나의 대체 단계로 최종 할당을 수행하여이를 해결합니다. 모든 CPU에서 원자입니다. 조작.

데이터베이스는 원자 트랜잭션을 사용하여 동일한 작업을 수행합니다. 트랜잭션 을 시작하면 모든 새 업데이트가 디스크의 새 위치에 씁니다. 트랜잭션을 마치면 디스크의 포인터가 새 업데이트가있는 위치로 변경됩니다. 즉, 다른 프로세스가 해당 프로세스를 건드릴 수없는 짧은 순간입니다.

이것은 더 자동적이고 유연한 것을 제외하고는 새 테이블을 만들려는 생각과 정확히 동일합니다.

따라서 귀하의 질문에 대답하기 위해, 불변성은 데이터베이스에서 우수하지만 아닙니다. 그 목적을 위해서만 별도의 테이블을 만들 필요는 없습니다. 데이터베이스 시스템에 사용 가능한 원자 트랜잭션 명령 만 사용할 수 있습니다.


답변 해주셔서 감사합니다. 이 관점은 직관이 두 가지 다른 아이디어를 하나의 패턴으로 결합하려고 혼란스럽게 노력하고 있다는 것을 깨달아야했던 것입니다.
Ed Carrel

8
분위기보다 조금 더 있습니다. 내가 OOP 컨텍스트에서 불변성을 선호하는 것으로 가장 많이 본 주장은 불변 개체는 생성자에서 상태를 한 번만 확인하면된다는 것입니다. 변경 가능한 경우 상태를 변경할 수있는 모든 메소드는 결과 상태가 여전히 유효한지 확인해야하며, 이는 클래스에 상당한 복잡성을 추가 할 수 있습니다. 이 인수는 잠재적으로 데이터베이스에도 적용되지만 db 유효성 검사 규칙은 절차적인 것이 아니라 선언적인 경향이 있으므로 모든 쿼리에 대해 중복 될 필요는 없으므로 훨씬 약합니다.
Dave Sherohman

24

불변성으로 얻을 수있는 이점에 따라 다릅니다. Rei Miyasaka의 대답은 하나 (무효 중간 상태를 피함)를 다루었지만 여기 또 다른 것이 있습니다.

돌연변이는 때때로 파괴적인 업데이트 라고합니다 합니다. 객체를 변형하면 명시 적으로 보존하기 위해 추가 단계를 수행하지 않는 한 이전 상태가 손실됩니다. 반대로, 불변의 데이터의 경우 일부 작업 전후의 상태를 동시에 나타내거나 여러 후속 상태를 나타내는 것은 쉽지 않습니다. 단일 상태 객체를 변경하여 광범위한 우선 검색을 구현하려고한다고 상상해보십시오.

이것은 아마도 데이터베이스 세계에서 가장 자주 시간적 데이터 로 나타날 것입니다 . 지난 달에 기본 요금제를 사용했지만 16 일에 프리미엄 요금제로 전환했다고 가정 해 보겠습니다. 현재 계획이 표시된 일부 입력란을 덮어 쓴 경우 청구가 제대로 이루어지지 않을 수 있습니다. 트렌드를 분석하는 기능을 놓칠 수도 있습니다. (이 지역 광고 캠페인이 한 일을보세요!)

어쨌든 "데이터베이스 디자인의 불변성"이라고 말할 때 이것이 내 마음에 떠 오릅니다.


2
나는 당신의 세 번째 단락에 동의하지 않습니다. 기록 (감사 로그, 계획 변경 로그 등)을 원할 경우이를 위해 별도의 테이블을 만들어야합니다. Customer사용자가 계획을 변경했다는 점을 기억하기 위해 50 개 필드를 모두 복제하면 시간이 지남에 따라 선택 속도가 느려지고 데이터 마이닝 (로그와 비교)이 복잡해지고 낭비되는 공간이 늘어나는 것 외에는 아무 것도 가져 오지 않습니다.
Arseni Mourzenko

6
@MainMa : 아마도 "시간 데이터베이스에 대해 읽어라"고 말했을 것입니다. 나의 예는 시간적 데이터가 무엇인지에 대한 스케치로 의도되었다. 나는 그것이 변화하는 데이터를 나타내는 가장 좋은 방법이라고 항상 주장하지는 않습니다. 다른 한편으로, 시간 데이터에 대한 지원은 현재 상당히 좋지 않지만, 변경 로그와 같은 "제 2 클래스"표현으로 강요하기보다는 데이터베이스 자체에서 시간 데이터를 수용하는 경향이있을 것으로 기대합니다.
Ryan Culpepper

감사 테이블의 변경 기록을 유지하는 경우 (예 :이 기능의 예로 스프링 부트 및 최대 절전 모드)?
Mohammad Najar

14

데이터베이스의 불변성 또는 불변성의 환상을 제공하는 데이터베이스에서 얻을 수있는 이점에 관심이있는 경우 Datomic을 확인하십시오.

Datomic 은 Rich Hickey가 Think Relevance와 제휴하여 발명 한 데이터베이스로, 아키텍처, 목표, 데이터 모델을 설명하는 비디오가 많이 있습니다. 검색 infoq, 특히 제목은 Datomic, Database as a Value 입니다. confreaks에서는 2012 년 Euroclojure 컨퍼런스에서 Rich Hickey가 기조 연설을 할 수 있습니다.

vimeo.com/53162418에는보다 개발 지향적 인 강연이 있습니다.

stuart halloway at.pscdn.net/008/00102/videoplatform/kv/121105techconf_close.html의 또 다른 내용은 다음과 같습니다.

  • 데이토 믹 (Datomic)은 데이텀 (Datums)이라고하는 사실에 대한 데이터베이스입니다.
    • E 엔티티 ID
    • 엔티티 속성 이름 (네임 스페이스를 가질 수 있음)
    • V 속성 값
    • T 거래 ID, 시간 개념이 있습니다.
    • O 단정 (현재 또는 현재 값), 거부 (과거 값)의 한 작업;
  • EDN (Extensible Data Notation)이라는 자체 데이터 형식을 사용합니다.
  • 거래는 ACID입니다
  • 데이터 로그를 쿼리 언어로 사용하며 SQL + 재귀 쿼리로 선언적입니다. 쿼리는 데이터 구조로 표시되며 jvm 언어로 확장되므로 클로저를 사용할 필요가 없습니다.
  • 데이터베이스는 3 개의 개별 서비스 (프로세스, 머신)에서 분리됩니다.
    • 트랜잭션
    • 저장
    • 쿼리 엔진.
  • 각 서비스를 개별적으로 확장 할 수 있습니다.
  • 오픈 소스는 아니지만 Datomic의 무료 (맥주에서와 같이) 버전이 있습니다.
  • 유연한 스키마를 명시 할 수 있습니다.
    • 속성 세트가 열려 있습니다
    • 언제든지 새로운 속성을 추가
    • 정의 또는 쿼리에 강성이 없음

이제 정보가 제 시간에 사실로 저장되기 때문에 :

  • 사실은 데이터베이스에 사실을 추가하고 삭제하지 않습니다 (법에 의해 요구되는 경우 제외)
  • 모든 것을 영원히 캐시 할 수 있습니다. Query Engine, 애플리케이션 서버에 메모리 데이터베이스로 존재합니다 (jvm 언어의 경우 비 jvm 언어는 REST API를 통해 액세스 할 수 있음).
  • 과거의 시간에 쿼리 할 수 ​​있습니다.

데이터베이스는 쿼리 엔진에 대한 값이며 매개 변수이며 QE는 연결 및 캐싱을 관리합니다. db를 값으로, 메모리에서 변경할 수없는 데이터 구조로 볼 수 있으므로 "미래의"값으로 작성된 다른 데이터 구조와 병합하여 실제 데이터베이스를 변경하지 않고 미래의 값으로 QE 및 쿼리에 전달할 수 있습니다. .

Rich Hickey의 codeq 라는 오픈 소스 프로젝트가 있으며 github Datomic / codeq에서 찾을 수 있습니다 .github Datomic / codeq는 git 모델을 확장하고 git 객체에 대한 참조를 datomic-free 데이터베이스에 저장하고 코드를 쿼리합니다. 데이토 믹스 사용법 의 를 볼 수 있습니다 .

데이텀을 ACID NoSQL로 생각할 수 있으며 데이텀을 사용하면 테이블 또는 문서 또는 Kv 저장소 또는 그래프를 모델링 할 수 있습니다.


7

업데이트를 피하고 삽입을 선호한다는 아이디어는 데이터 스토리지를 이벤트 소스로 구축하는 데 대한 생각 중 하나이며, 종종 CQRS와 함께 사용됩니다. 이벤트 소스 모델에는 업데이트가 없습니다. 집계는 "변환"(이벤트) 시퀀스로 표시되므로 스토리지는 추가 전용입니다.
이 사이트 에는 CQRS 및 이벤트 소싱에 대한 흥미로운 토론 포함되어 있습니다.


CQRS 및 이벤트 소싱이 요즘 강조되고 있습니다.
Gulshan

6

이는 데이터웨어 하우징 세계에서 "천천히 변화하는 차원"및 다른 도메인의 "임시"또는 "이중"테이블과 매우 밀접한 관계가 있습니다.

기본 구성은 다음과 같습니다.

  1. 항상 생성 된 서로 게이트 키를 기본 키로 사용하십시오.
  2. 당신이 묘사하고있는 것의 유일한 식별자는 "논리 키"가됩니다.
  3. 각 행에는 최소한 "ValidFrom"타임 스탬프와 선택적으로 "ValidTo"타임 스탬프와 더 선택적으로 "최신 버전"플래그가 있어야합니다.
  4. 논리 엔터티의 "생성"에서 현재 타임 스탬프의 "Valid From"을 사용하여 새 행을 삽입합니다. 선택적 ValidTo는 "forever"(9999-12-31 23:59:59)로 설정되고 Last Version은 "True"로 설정됩니다.
  5. 논리적 엔터티의 후속 업데이트 최소한 위와 같이 새 행을 삽입하십시오. 이전 버전의 ValidTo를 "now ()-1 초"로 조정하고 최신 버전을 "False"로 조정해야 할 수도 있습니다.
    1. 논리적 삭제시 (이것은 ValidTo 타임 스탬프에서만 작동합니다!) 현재 행의 ValidTo 플래그를 "now () -1 second"로 설정합니다.

이 체계의 장점은 언제든지 논리 엔티티의 "상태"를 재현 할 수 있고, 시간이 지남에 따라 엔티티의 히스토리가 있으며 "논리 엔티티"가 많이 사용되는 경우 경합을 최소화한다는 것입니다.

단점은 더 많은 데이터를 저장하고 더 많은 인덱스를 유지해야한다는 것입니다 (최소한 논리 키 + ValidFrom + ValidTo에서). 논리 키 + 최신 버전에 대한 색인은 대부분의 쿼리 속도를 크게 향상시킵니다. 또한 SQL을 복잡하게 만듭니다!

실제로 기록을 유지할 필요가없고 특정 시점에 엔티티 상태를 다시 작성해야하는 경우가 아니라면 이것이 가치가 있는지 여부는 사용자에게 달려 있습니다.


1

변경 불가능한 데이터베이스를 갖는 또 다른 가능한 이유는 더 나은 병렬 처리를 지원하기위한 것입니다. 고장난 업데이트는 데이터를 영구적으로 엉망으로 만들 수 있으므로이를 방지하기 위해 잠금이 발생하여 병렬 성능이 손상됩니다. 많은 이벤트 삽입이 임의의 순서로 진행될 수 있으며 , 모든 이벤트가 결국 처리 되는 한 상태는 적어도 결국 은 올 바릅니다. 그러나 이것은 데이터베이스 업데이트를 수행하는 것과 비교하여 실제로 작업하기가 너무 어렵 기 때문에 이러한 방식으로 작업을 수행하려면 실제로 많은 병렬 처리가 필요 합니다. 권장 하지 않습니다 .


0

면책 조항 : 나는 DB에서 거의 새로운 사람입니다 : p

그러나 이러한 데이터 위성 화 방식은 성능에 즉각적인 영향을 미칩니다.

  • 좋은 차 테이블에 트래픽이 적게
  • 좋은 차 테이블에 작은 행
  • 나쁜 위성 데이터를 필요로하는 다른 룩업이 필요 의미
  • 나쁜 모든 객체가 두 테이블에 존재하는 경우 더 많은 공간이 점유

귀하의 요구 사항에 따라 이것을 환영하거나 환영하지 않을 수도 있지만 반드시 고려해야 할 사항입니다.


-1

나는 당신의 계획이 어떻게 "불변"이라고 불릴 수 있는지 모르겠다.

보충 테이블에 저장된 값이 변경되면 어떻게됩니까? 해당 테이블에서 업데이트를 수행해야 할 것 같습니다.

데이터베이스를 실제로 변경할 수 없게하려면 "INSERTS"만으로 유지 관리해야합니다. 이를 위해 "현재"행을 식별하는 방법이 필요합니다. 이것은 거의 항상 비효율적입니다. 이전의 변경되지 않은 모든 값을 복사하거나 쿼리 할 때 여러 레코드에서 현재 상태를 함께 모아야합니다. 현재 행을 선택하려면 일반적으로 ( where updTime = (SELECT max(updTime) from myTab where id = ?) 와 같이 끔찍한 지저분한 SQL이 필요합니다 .

이 문제점은 시간이 지남에 따라 데이터 히스토리를 유지하고 특정 시점의 상태를 선택할 수있는 DataWarehousing에서 많이 발생합니다. 해결책은 일반적으로 "차원"테이블입니다. 그러나 그들은 DW "작년 1 월 영업 담당자"문제를 해결했습니다. Java 불변 클래스가 갖는 이점을 제공하지 않습니다.

보다 철학적 인 메모에서; 데이터베이스는 "상태"(은행 잔고, 전력 소비, StackOverflow의 브라우니 포인트 등)를 저장하기 위해 존재하며 "상태 비 저장"데이터베이스를 만들려고 시도하는 것은 무의미한 운동으로 보입니다.


단일 레코드의 경우 WHERE id = {} ORDER BY updTime DESC LIMIT 1일반적으로 너무 비효율적이지 않습니다.
이즈 카타

@ Izkata-세 테이블 조인의 중간에 넣어보십시오 :-)
James Anderson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.