Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

성장에 따라 데이터베이스를 더 쉽게 확장할 수 있는 간단한 팁

젊은 프로젝트에서 작업할 때 나중에 확장하기 더 쉽거나 더 어렵게 만드는 결정을 끊임없이 내립니다. 때로는 더 빨리 배송할 수 있도록 약간의 기술적 부채를 쌓기 위해 단기 이익을 선택하는 것이 좋습니다. 그러나 다른 경우에는 대안이 있는지 몰랐기 때문에 기술적인 부채를 떠안게 됩니다.

내가 이렇게 말할 수 있는 이유는 여기 Honeybadger에서 우리가 삶을 원래보다 훨씬 더 힘들게 만드는 몇 가지 일을 했기 때문입니다. 몇 가지 핵심 사항을 이해했다면 스케일링이 훨씬 덜 고통스러웠을 것입니다.

UUID 사용

"기본 키"라고 하면 대부분의 사람들이 자동 증가하는 숫자를 생각합니다. 이것은 소규모 시스템에서 잘 작동하지만 확장함에 따라 큰 문제가 발생합니다.

주어진 순간에 단 하나의 데이터베이스 서버만 기본 키를 생성할 수 있습니다. 이는 모두를 의미합니다. 쓰기는 단일 서버를 거쳐야 합니다. 초당 수천 개의 쓰기 작업을 수행하려는 경우 나쁜 소식입니다.

UUID를 기본 키로 사용하면 이 문제를 피할 수 있습니다. 익숙하지 않은 경우 UUID는 다음과 같은 고유 식별자입니다. 123e4567-e89b-12d3-a456-426655440000 .

Wikipedia에서 설명하는 방법은 다음과 같습니다.

<블록 인용>

표준 방법에 따라 생성될 때 UUID는 실제 목적을 위해 고유하며 중앙 등록 기관이나 생성 당사자 간의 조정이 필요하지 않습니다. UUID가 복제될 확률은 0이 아니지만 무시할 수 있을 정도로 0에 가깝습니다.

따라서 누구나 UUID를 생성하고 이를 사용하여 식별자가 이미 다른 것을 식별하기 위해 생성된 것과 중복되지 않고 미래에 중복되지 않을 것이라는 거의 확신을 가지고 무언가를 식별할 수 있습니다. 따라서 독립 당사자가 UUID로 레이블을 지정한 정보는 식별자 간의 충돌을 해결할 필요 없이 나중에 단일 데이터베이스로 결합되거나 동일한 채널에서 전송될 수 있습니다.

UUID를 기본 키로 사용하면 모든 쓰기가 더 이상 단일 데이터베이스를 통과할 필요가 없습니다. 대신 여러 서버에 배포할 수 있습니다.

또한 이전 레코드의 ID를 생성하는 등의 작업을 유연하게 수행할 수 있습니다. 데이터베이스에 저장됩니다. 이것은 레코드를 캐시나 검색 서버로 보내고 싶지만 데이터베이스 트랜잭션이 완료될 때까지 기다리지 않으려는 경우에 유용할 수 있습니다.

Rails 앱에서 기본적으로 UUID를 활성화하는 것은 쉽습니다. 구성 파일을 수정하기만 하면 됩니다.

# config/application.rb
config.active_record.primary_key = :uuid

Rails 마이그레이션과 함께 개별 테이블에 UUID를 사용할 수 있습니다.

create_table :users, id: :uuid do |t|
  t.string :name
end

개발을 시작할 때 활성화하면 확장을 시도할 때 문제의 세계를 구할 수 있는 간단한 구성 옵션입니다. 추가 보너스로 봇과 악의적인 사용자가 개인 URL을 추측하기가 더 어려워집니다.

카운트 및 카운터

일단 찾기 시작하면 카운트와 카운터가 도처에 있습니다. 이메일 클라이언트는 읽지 않은 이메일의 수를 표시합니다. 블로그에는 총 게시물 수를 사용하여 페이지 수를 계산하는 페이지 매김 바닥글이 있습니다.

카운터에는 두 가지 크기 조정 문제가 있습니다.

  1. select count(*) from users와 같은 데이터베이스 쿼리 본질적으로 느립니다. 문자 그대로 레코드 집합의 각 레코드를 반복하여 결과를 생성합니다. 백만 개의 레코드가 있으면 시간이 걸립니다.
  2. "카운터 캐시"를 사용하여 카운터 속도를 높이려는 시도는 효과가 있지만 많은 데이터베이스 서버에 쓰기를 분산하는 기능을 제한합니다.

가장 쉬운 해결책은 가능한 카운터를 사용하지 않는 것입니다. 이것은 초기 디자인을 할 때 훨씬 쉽습니다.

예를 들어 개수 대신 날짜 범위로 페이지를 선택하도록 선택할 수 있습니다. 나중에 생성할 지옥 같은 약간 유용한 통계를 표시하지 않도록 선택할 수 있습니다. 당신은 아이디어를 얻을.

만료 및 창고 데이터

보유하고 있는 RAM, CPU 및 디스크 IO의 양을 고려할 때 단일 postgres 테이블에 저장할 수 있는 데이터 양에는 상한선이 있습니다. 즉, 기본 테이블에서 오래된 데이터를 이동해야 하는 시점이 올 것입니다.

간단한 경우를 살펴보자. 몇 기가바이트 크기의 데이터베이스에서 1년 이상 된 레코드를 삭제하려고 합니다.

이전에 이 문제를 처리한 적이 없다면 다음과 같이 하고 싶은 유혹을 받을 수 있습니다.

MyRecords.where("created_at < ?", 1.year.ago).destroy

문제는 이 쿼리를 실행하는 데 며칠 또는 몇 주가 걸린다는 것입니다. 데이터베이스가 너무 큽니다.

이것은 특히 고통스러운 문제입니다. 너무 늦을 때까지 자신이 가지고 있다는 사실을 깨닫지 못하는 경우가 많기 때문입니다. 회사가 젊고 데이터베이스에 1,000개의 레코드가 있을 때 데이터 제거 전략에 대해 생각하는 사람은 거의 없습니다.

미리 계획을 세우면 쉬운 해결책이 있습니다. 테이블을 파티션하기만 하면 됩니다. 모든 데이터를 my_records에 쓰는 대신 이번 주 데이터를 my_records_1에 씁니다. 다음 주 데이터를 my_records_2로 . 지난 주를 삭제할 때가 되면 drop table my_records_1하세요. . 삭제와 달리 이 쿼리는 매우 빠르게 완료됩니다.

날짜 이외의 필드로 파티션을 나누는 것도 가능합니다. 귀하의 사용 사례에 의미가 있는 것은 무엇이든 상관없습니다.

모든 세부 사항을 처리하고 코드 줄을 변경하지 않고 데이터베이스를 분할할 수 있는 pg_partman이라는 postgres 확장도 있습니다. 또는 Ruby에서 분할 관리를 선호하는 경우 분할 가능이라는 편리한 보석이 있습니다.

이별의 말

다음 번에 처음부터 프로젝트를 빌드하는 자신을 발견하면 잠시 시간을 내어 확장에 대해 생각해 보시기 바랍니다. 그것에 집착하지 마십시오. HAML을 사용할지 ERB를 사용할지 고민하느라 며칠을 보내지 마세요. 그러나 단순히 미리 계획함으로써 얻을 수 있는 쉬운 승리가 없는지 자문해 보십시오.