하나의 큰 쿼리 또는 많은 작은 쿼리가 더 빠릅니다.


68

나는 다른 회사에서 일하고 있으며, 그들 중 일부는 모든 "상대적"과 함께 테이블을 조인하는 뷰를 선호한다는 것을 알았습니다. 그러나 응용 프로그램에서 때로는 하나의 열만 사용하면됩니다.

따라서 간단한 선택을 한 다음 시스템 코드에서 "결합"하는 것이 더 빠를까요?

시스템은 php, java, asp, 데이터베이스에 연결되는 모든 언어 일 수 있습니다.

그래서 문제는 서버 측 (php, java, asp, ruby, python ...)에서 데이터베이스로가는 것이 무엇입니까? 우리가 필요한 모든 것을 얻거나 서버 측에서 데이터베이스로 이동하여 한 번에 하나의 테이블에서만 열을 가져 오는 쿼리?


2
어떤 'SQL'구현을 사용하고 있습니까? MySQL, Microsoft SQL Server, Oracle, PostgreSQL 등? 태그를 업데이트하십시오.
RLF

1
MySQL과 PostgreSQL
sudo.ie

6
내 경험은 MySQL은 복잡한 쿼리를 좋아하지 않으며 일반적으로 매우 간단한 쿼리 (그러나 더 많은 것)로 더 빠릅니다. Postgres의 쿼리 최적화 프로그램이 훨씬 우수하며 일반적으로 하나의 큰 쿼리를 실행하는 것이 더 효율적입니다.
a_horse_with_no_name

3
@a_horse_with_no_name 특히이 질문의 맥락에서 매우 광범위한 일반화입니다. MySQL 옵티마이 저는 실제로 설계 상 매우 단순하며 PostgreSQL에서 더 빠른 계획을 생성하는 다른 이전 버전의 MySQL에서 조인 및 하위 쿼리에 문제를 일으킬 수 있지만 MySQL은 순수한 OLTP로드에 대해 매우 빠릅니다. 그러나 문제의 맥락에서, 하나의 큰 쿼리는 더 빠를 것입니다. 가능한 최악의 시나리오에서는 프로그래밍 루프 내부의 SELECT (RDBMS 사용 여부에 관계없이)를 가정 해 봅시다.
jynus

2
@jynus : 글쎄, 그 질문 매우 광범위합니다. (또한 : "내 경험에서"-다른 사람들은 다른 경험을 가질 수 있습니다). LOOP 내부의 쿼리는 결코 좋은 생각이 아니며 거의 항상 디자인이 좋지 않거나 관계형 데이터베이스로 작업하는 방법에 대한 이해가 부족한 결과입니다.
a_horse_with_no_name

답변:


68

귀하의 질문을 다루는 것은 주제 분해 참여입니다.

이 책의 209 페이지 에 따르면

고성능 MySQL

다중 테이블 조인 대신 여러 단일 테이블 쿼리를 실행 한 다음 응용 프로그램에서 조인을 수행하여 조인을 분해 할 수 있습니다. 예를 들어,이 단일 쿼리 대신 :

SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql';

다음 쿼리를 실행할 수 있습니다.

SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

왜 지구상에서 이것을 하시겠습니까? 아무것도 보지 않고 쿼리 수를 늘 렸기 때문에 언뜻보기에는 낭비로 보입니다. 그러나 이러한 구조 조정은 실제로 상당한 성능 이점을 제공 할 수 있습니다.

  • 캐싱이 더 효율적일 수 있습니다. 많은 응용 프로그램이 테이블에 직접 매핑되는 "개체"를 캐시합니다. 이 예제에서 태그 mysql가 있는 객체 가 이미 캐시 된 경우 응용 프로그램은 첫 번째 쿼리를 건너 뜁니다. 캐시에서 ID가 123, 567 또는 908 인 게시물을 찾으면 IN()목록 에서 제거 할 수 있습니다 . 쿼리 캐시도이 전략의 이점을 활용할 수 있습니다. 테이블 중 하나만 자주 변경되면 조인을 분해하면 캐시 무효화 수가 줄어들 수 있습니다.
  • 쿼리를 개별적으로 실행하면 잠금 경합이 줄어들 수 있습니다.
  • 응용 프로그램에서 조인을 수행하면 다른 서버에 테이블을 배치하여 데이터베이스를 쉽게 확장 할 수 있습니다.
  • 쿼리 자체가 더 효율적일 수 있습니다. 이 예제에서 IN()조인 대신 목록을 사용하면 MySQL이 조인에서 가능한 것보다 행 ID를 정렬하고 행을보다 최적으로 검색 할 수 있습니다.
  • 중복 행 액세스를 줄일 수 있습니다. 응용 프로그램에서 조인을 수행한다는 것은 각 행을 한 번만 검색하는 것을 의미하지만 쿼리의 조인은 본질적으로 동일한 데이터에 반복적으로 액세스 할 수있는 비정규 화입니다. 같은 이유로 이러한 구조 조정은 전체 네트워크 트래픽 및 메모리 사용량을 줄일 수도 있습니다.
  • 어느 정도까지는이 기술을 MySQL이 조인을 실행하는 데 사용하는 중첩 루프 알고리즘 대신 해시 조인을 수동으로 구현하는 것으로 볼 수 있습니다. 해시 조인이 더 효율적일 수 있습니다.

결과적으로, 이전 쿼리의 많은 데이터를 캐시하고 재사용 할 때, 여러 서버에 데이터를 분배 IN()하거나, 조인을 목록으로 바꾸 거나, 조인이 동일한 테이블을 여러 번 참조 할 때 애플리케이션에서 조인을 수행하는 것이 더 효율적일 수 있습니다 .

관찰, 관측

InnoDB가 쿼리 캐시를 교차 검사 할 때 약간 무겁기 때문에 첫 번째 글 머리 기호가 마음에 듭니다.

마지막 글 머리 기호에 관해서는 2013 년 3 월 11 일 에 중첩 루프 알고리즘을 설명 하는 게시물을 작성했습니다 ( JOIN 조건과 WHERE 조건 사이에 실행 차이가 있습니까? ). 읽은 후에는 조인 분해가 얼마나 좋은지 알 수 있습니다.

책의 다른 모든 요점 과 관련하여 개발자는 실제로 성능을 최우선으로 생각합니다. 일부는 빠른 디스크 사용, 더 많은 CPU / 코어 가져 오기, 스토리지 엔진 조정 및 구성 파일 조정과 같은 성능 향상을 위해 외부 응용 프로그램 (응용 프로그램 외부)에 의존합니다. 다른 사람들은 버클을 짜서 더 나은 코드를 작성합니다. 일부는 저장 프로 시저에서 모든 비즈니스 인텔리전스를 코딩하는 데 의존하지만 조인 분해를 적용하지 않습니다 ( 데이터베이스 계층에서 응용 프로그램 논리를 반대하거나 다른 게시물과 함께 무엇을 주장합니까? 참조). 각 개발자 상점의 문화와 관용에 달려 있습니다.

일부는 성능에 만족하고 더 이상 코드를 건드리지 않을 수 있습니다. 다른 사람들은 컴포지션에 참여하려고 할 때 얻을 수있는 큰 이점이 있다는 것을 깨닫지 못합니다.

기꺼이 개발자를 위해 ...

시도 해봐 !!!


3
3 개의 쿼리로 변경하는 것에 관한 그 링크는 ... Baron, Vadim 및 Peter를 알고 존중하지만이 잘못된 제안에 동의하지 않습니다. 분열에 찬성하는 대부분의 주장은 언급 할 가치가 없을 정도로 드물다. JOIN을 사용하여 단일 쿼리를 고수 한 다음 개선을 위해 노력하겠습니다.
Rick James

2
@RickJames 나는 당신의 의견의 정신에 동의합니다. 수년에 걸쳐, 나는 일부의 분해 작업에 참여하고 다른 이들에게는 실패하는 것을 보았습니다. 적절한 SQL 스킬 셋을 사용하더라도 조인 분해가 올바르게 수행되지 않으면 문제가 발생할 수 있습니다. 현재 고용주에서 많은 부서는 특히 레거시 코드가 포함되어 있고 깊은 주머니가있는 경우 확장 및 축소를 좋아합니다. 캐비어 맛이 있지만 달걀 샐러드 예산을 가진 사람들의 경우 조인 분해는 위험의 가치가 있지만 올바르게 수행해야합니다.
RolandoMySQLDBA

권리와 시간이 있다면 Oracle 환경에서 이것이 어떻게 작동하는지 알고 싶습니다.
Rick Henderson

더 빠른 방법 중 하나는 주문을하는 경우 하나의 큰 목록을 주문하는 것보다 작은 목록을 주문하는 것이 전체 계산이 적다는 것입니다.
Evan Siroky

24

에서 포스트 그레스 (그리고 아마도 비슷한 정도에 대한 RDBMS는 낮은 정도 MySQL은), 적은 수의 쿼리는 거의 항상 빨리.

여러 쿼리를 구문 분석하고 계획하는 오버 헤드는 이미 대부분의 경우 얻을 수있는 것 이상입니다.

클라이언트에서 수행해야 할 추가 작업에 대해 말하지 말고 결과를 결합하면 일반적으로 속도 가 훨씬 느려집니다. RDBMS는 이러한 종류의 작업과 작업이 원본 데이터 형식을 기반으로하는 것을 전문으로합니다. text중간 결과를 캐스트 하거나 클라이언트의 기본 유형으로 변환하지 않아 결과가 덜 정확하거나 잘못 될 수 있습니다. 부동 소수점 숫자를 생각해보십시오 ...

또한 DB 서버와 클라이언트간에 더 많은 데이터를 전송합니다. 이것은 값으로 가득 찬 손에는 무시할 수 있거나 큰 차이를 만듭니다.

여러 쿼리가 데이터베이스 서버에 대한 여러 번의 왕복을 의미하는 경우 네트워크 대기 시간 및 트랜잭션 오버 헤드, 연결 오버 헤드까지 여러 번 수집합니다. 큰 손실.

설정에 따라 네트워크 대기 시간만으로 나머지 모든 것보다 시간이 오래 걸릴 수 있습니다.

SO 관련 질문 :

트랜잭션이 도중에 DB 행에 대한 잠금을 수집하기 때문에 매우 길고 오래 실행되는 쿼리 의 전환점이있을 수 있습니다 . 매우 큰 쿼리는 오랜 시간 동안 많은 잠금을 유지하여 동시 쿼리충돌을 일으킬 수 있습니다 .


그냥 호기심, 당신은 무엇을 생각합니까 매우 큰 ?
Sablefoste

@Sablefoste : 액세스 패턴에 따라 크게 달라집니다. 중요한 점은 동시 트랜잭션이 대기열에서 시작되어 잠금이 해제 될 때까지 기다리거나 충분한 잠금을 누적하여 리소스의 상당 부분을 차지하는 경우입니다. 또는 귀하의 질의가 autovacuum을 방해 할 정도로 오래 실행되는 경우 ...
Erwin Brandstetter

그러나 다소 일반적인 상황 인 경우 외부 조인을 사용하고 "부모"테이블에 대해 많은 중복 데이터를 반환하는 쿼리 인 경우 응용 프로그램 (대부분의 ORM 라이브러리)에 의해 구문 분석되고 정렬되어야합니다. 필요한 모든 ID를 먼저 가져온 다음 외부 조인 대신 IN ()을 사용하여 다른 작은 선택을 가져 오는 작은 선택? 두 번째 접근 방식이 더 효율적이지 않습니까 (DB와 앱이 소비 한 CPU와 통신 대역폭을 모두 고려하면)?
JustAMartin

1
@JustAMartin : 올바른 쿼리를 가정 할 때 RDBMS의 쿼리 플래너가 처리 할 때 거의 확실히 빠른 쿼리처럼 들립니다. 에 관하여 returns lots of redundant data for "parent" table: 당신은 왜 중복 데이터를 반환? 필요한 데이터 만 반환하십시오.
Erwin Brandstetter

1
외부 결합을 사용하면 RDBMS는 결합 된 모든 하위에 대해 중복 된 상위 테이블의 데이터를 리턴합니다. 이는 일부 네트워크 및 메모리 오버 헤드를 의미하며 ORM 도구에서 추가 구문 분석을 수행하여 중복 상위 값을 버리고 하위 하위가 n 인 하위를 하나만 유지합니다. 따라서 단일 쿼리를 사용하면 RDBMS 쿼리 플래너의 효율적인 작업, 적은 네트워크 (또는 로컬 파이프) 요청을 절약 할 수 있지만 불필요한 불필요한 페이로드 및 ORM 라이브러리 내에서 데이터 이동이 손실됩니다. 나는 그것이 항상-최적화하기 전에 측정하는 것 같아요.
JustAMartin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.