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


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.