미래 지향적인 웹 앱 구축: The Codest의 전문가 팀이 제공하는 인사이트
The Codest가 최첨단 기술로 확장 가능한 대화형 웹 애플리케이션을 제작하고 모든 플랫폼에서 원활한 사용자 경험을 제공하는 데 탁월한 성능을 발휘하는 방법을 알아보세요. Adobe의 전문성이 어떻게 디지털 혁신과 비즈니스를 촉진하는지 알아보세요...
Ruby on Rails 프레임워크로 작업할 때는 보통 MySQL이나 PostgreSQL과 같은 관계형 데이터베이스를 다룹니다. 활성 레코드 마이그레이션을 사용하여 마이그레이션을 정의할 때 소위 인덱스라고 하는 것을 보게 되는데, 초보자들은 인덱스와 인덱스가 어떤 이점을 제공하는지 잘 이해하지 못하는 경우가 많습니다.
Ruby on Rails 프레임워크로 작업할 때는 보통 MySQL이나 PostgreSQL과 같은 관계형 데이터베이스를 다룹니다. 활성 레코드 마이그레이션을 사용하여 마이그레이션을 정의할 때 소위 인덱스라고 하는 것을 보게 되는데, 초보자들은 인덱스와 인덱스가 어떤 이점을 제공하는지 잘 이해하지 못하는 경우가 많습니다.
이 글에서는 인덱스가 무엇인지, 어떤 용도로 사용되는지 설명하고 인덱스를 사용하는 방법에 대한 몇 가지 모범 사례를 제시하고자 합니다.
많은 데이터베이스 엔진이 있으며, 가장 많이 사용되는 엔진 중 하나는 앞서 언급한 MySQL, PostgreSQL, Oracle 또는 Microsoft SQL Server입니다. 이들은 모두 관계형 데이터베이스로, 모든 데이터가 서로 연관되어 있고 테이블에 저장됩니다. 각 테이블 행을 레코드라고 하며, 각 레코드에는 고유한 식별자(ID)가 있습니다. 가장 인기 있는 데이터베이스 엔진의 순위는 https://db-engines.com/en/ranking 에서 확인할 수 있습니다. MongoDB와 같은 일부 비관계형 데이터베이스도 찾을 수 있습니다.
데이터베이스의 테이블에는 몇 개에서 수십 개, 심한 경우 수백 개까지 열이 있을 수 있습니다. 각 테이블에는 행의 개수에 제한이 없다는 점에 유의하세요. 이 숫자는 데이터베이스의 구조에서 직접적으로 나온 것이 아니며, 레코드 수가 계속 증가하여 결과적으로 데이터베이스가 커질 것이라고 항상 가정해야 합니다. 기존 애플리케이션에서 작성된 초기 가정과 쿼리는 레코드 수가 적거나 중간 정도일 때는 적합할 수 있지만 시간이 지나면서 더 많은 데이터가 도착하면 애플리케이션과 데이터베이스의 통신이 더 이상 효율적이지 않게 됩니다.
프로그래머의 역할은 테이블에서 일부 데이터를 검색하는 쿼리를 작성하는 것이지만, 쿼리를 처리하는 최적의 방법은 데이터베이스 엔진에 따라 다릅니다. 데이터베이스 엔진은 디스크에서 메모리로 데이터를 로드한 다음 스캔한다는 점을 기억하세요. 즉, 많은 사용자가 동시에 복잡한 작업을 수행하면 검색을 구현할 리소스가 부족하기 때문에 여러 사용자가 차례를 기다려야 합니다. 그렇기 때문에 관련 인덱스가 매우 중요합니다.
Wiki: 인덱스 - 테이블에서 검색 작업을 수행하는 속도를 높여주는 데이터 구조입니다.
각 인덱스에 대해 테이블에서 레코드를 검색하는 데 사용할 키(하나 또는 여러 열에 대해)를 정의해야 합니다. 인덱스의 데이터는 이전에 정의한 키로 정렬되므로 테이블에서 데이터를 검색하는 속도가 크게 빨라집니다. 일상 생활에서 가장 간단한 예는 사람들이 이름과 성으로 정렬된 전화번호부입니다. 이 경우 인덱스는 이름과 성이 될 것이라고 말할 수 있습니다.
최고의 인덱스 키는 어떻게 선택하나요? 어렵지 않습니다. 몇 가지 규칙만 기억하시면 됩니다. 열을 기반으로 인덱스를 만듭니다:
- 는 문의(WHERE)에 자주 사용됩니다,
- 를 서로 조합하면 고유한 값(즉, 정확히 하나의 행을 나타내는 값)이 됩니다,
- 은 소위 연결 열(JOIN)로 사용됩니다,
- 는 가장 선택적인 키, 즉 쿼리를 작성할 때 가장 적은 줄 수를 반환하는 키를 지정합니다.
테이블에 가장 적합한 키를 이미 알고 있다면 얼마나 많은 인덱스가 필요한지 스스로에게 물어볼 수도 있습니다. 이 경우 설계 단계에서 테이블을 참조할 쿼리를 이미 알고 있는 것이 가장 좋습니다.
표시될 특정 쿼리에 대한 인덱스를 만들되 각 열에 대해 인덱스를 작성하지 않도록 하겠습니다. 인덱스는 테이블과 마찬가지로 어딘가에 저장해야 하므로 각 열에 대한 인덱스가 있는 테이블을 만들면 사용되는 공간이 크게 늘어날 수 있다는 점을 고려해야 합니다.
우리가 생각해야 할 또 다른 문제는 고유성입니다. 인덱스가 정말 고유한지 생각해 보는 데 5분 정도 더 투자할 가치가 있습니다. 이렇게 하면 쿼리 최적화 도구에 쿼리 중복을 예상할 필요가 없음을 알릴 수 있습니다. 예를 들어 이메일 주소가 있습니다:
냉동 문자열 문자: true
클래스 CreateUsers < ActiveRecord::Migration[6.0]
def change
createtable :users do |t|
t.string :이메일, null: false
end
addindex :사용자, :이메일, 고유: true
end
end
PostgreSQL 엔진의 예에서는 고유 인덱스가 있는 이메일 열과 인덱스가 없는 이메일 열의 쿼리 속도 차이를 보여 드리겠습니다.
1. 샘플을 사용할 수 있습니다. 코드 스니펫을 사용하여 아래 예제를 테스트할 수 있습니다. 먼저 열이 하나 있는 빈 테이블을 만들어 보겠습니다:
CREATE TABLE users (
이메일 varchar
);
2. 테스트를 위해 10,000개의 레코드를 생성해 보겠습니다:
DO $
BEGIN FOR i IN 1..10000 LOOP
INSERT INTO users values((select 'user' || i || '@example.com'));
END LOOP; END;
$;
데이터베이스에서 특정 사용자를 찾고자 할 때 쿼리가 얼마나 빨리 처리되는지 확인하기 위해 EXPLAIN ANALYSIS를 사용하겠습니다.
분석 설명 이메일 선택 사용자로부터 이메일 = 'user890example.com';
쿼리를 통해 관심 있는 레코드를 찾기 위해 전체 테이블을 강제로 반복했습니다.
이 프로세스를 순차 스캔이라고 합니다. 이 경우 전체 테이블을 읽고 특정 행을 필터링하는 것이 작업을 수행하는 가장 좋은 방법입니다.
PostgreSQL은 불필요한 줄을 걸러내고 관심 있는 줄만 반환합니다. 이 경우에는 이것이 정말 최선의 방법입니다. 순차적 스캔이 항상 나쁜 것은 아니며, 순차적 스캔이 이상적인 경우도 있습니다.
4. 이제 인덱스가 고유한 테이블에서 이미 수행한 쿼리를 확인할 차례입니다. 인덱스를 설정하고 쿼리를 실행해 보겠습니다.
사용자(이메일)에 고유 인덱스 index_email을 입력합니다;
EXPLAIN ANALYZE SELECT email FROM users WHERE email = 'user890example.com';
이번에는 필요한 모든 열이 이미 인덱스에 있기 때문에 PostgreSQL이 인덱스 스캔을 활용했습니다.
인덱스를 사용할 때 몇 줄만 선택하면 매우 효율적입니다. 그러나 더 많은 데이터를 선택하면 인덱스와 테이블을 스캔하는 데 시간이 너무 많이 소요됩니다.
보시다시피 인덱스가 있는 열에 대한 쿼리 실행 시간이 훨씬 짧아졌습니다(표시된 예에서는 1.267ms에서 0.111ms로 감소하여 91.24%까지 단축되었습니다!). 가장 중요한 차이점은 PostgreSQL이 관심 있는 레코드를 검색하는 방식입니다. 첫 번째 경우에는 데이터베이스 엔진이 전체 테이블에서 필요한 레코드를 검색해야 했습니다. 그러나 두 번째 경우에는 인덱스 구조가 정렬되고 고유하기 때문에 엔진이 레코드의 위치를 알고 있어 쿼리 처리 시간이 상당히 빨라졌습니다.
대규모 데이터베이스와 매우 복잡한 쿼리의 경우, 인덱스를 올바르게 설정하면 데이터베이스를 검색하는 컴퓨터의 속도를 높일 필요 없이 애플리케이션의 작업 속도를 크게 높일 수 있습니다.
각 열에 인덱스를 만드는 것은 좋은 습관이 아니라는 점을 기억할 필요가 있습니다. 인덱스가 설정되어 있으면 관심 있는 데이터를 검색할 때 최적화 도구의 작업 속도가 빨라지지만 동시에 새 인덱스를 삽입하고 기존 인덱스를 업데이트하는 속도가 느려집니다.
자세히 읽어보세요:
– Rails 앱에서 일반적인 JS 프레임워크를 사용해야 하나요? Stimulus.js가 대안이 될 수 있습니다.