FeGwan-Training / FeGwan

0 stars 0 forks source link

[업무에 바로 쓰는 SQL 튜닝] 2장 SQL 튜닝 용어를 직관적으로 이해하기 #70

Open MyeoungDev opened 7 months ago

MyeoungDev commented 7 months ago

Discussed in https://github.com/FeGwan-Training/FeGwan/discussions/69

Originally posted by **MyeoungDev** January 26, 2024 # 2장 SQL 튜닝 용어를 직관적으로 이해하기 ## 2.1.1 물리 엔진과 오브젝트 용어 MySQL이라는 DBMS는 데이터를 저장하고, 저장된 데이터를 가공하는 연산을 수행한다. 스크린샷 2024-01-26 오후 1 13 32 1. 실행된 SQL 문을 다양한 문법 및 구문으로 검사(Parser) 2. 사용자가 요청한 데이터를 빠르고 효율적으로 찾아가는 전략적 계획 수립(Optimizer) 3. 스토리지 엔진에 위한 데이터까지 찾아간 뒤 해당 데이터를 MySQL 엔진으로 전달. 4. MySQL 엔진은 전달된 데이터에서 불필요한 부분을 필터링하고 필요한 연산을 수행한 뒤 최종 결과 전달. ### 스토리지 엔진 (InnoDB, MyISAM, Memory 등) **스토리지 엔진**은 **사용자가 요청한 SQL 문을 토대로** DB에 저장된 디스크나 메모리에 **필요한 데이터를 가져오는 역할을 수행**. 일반적으로 온라인상의 트랜잭션 발생으로 데이터를 처리하는 **OLTP(Online Transaction Processing)** 환경이 대다수인 만큼 **주로 InnoDB 엔진을 사용**한다. **대용량 쓰기 트랜잭션이 발생**하면 **MyISAM 엔진**, **메모리 데이터를 로드**하여 빠르게 읽기 위하면 **Memory 엔진**을 사용하는 식으로 응용이 가능하다. ```sql -- MySQL DBMS 사용중인 엔진 확인 법 SELECT ENGINE, TRANSACTIONS, COMMENT FROM information_Schema.engines; ``` ## 2.1.2 SQL 프로세스 용어 1. SQL 문을 실행하면, **파서(Parser)**는 MySQL이 **이해할 수 있는 최소 단위로 구성요소를 분리**하여 구성요소 **트리로 만든다.** 2. 트리를 만드는 과정에서 문법 오류 검토, **트리의 최소 단위**는 >, <, = 의 **기호** 혹은 **SQL 키워드** 3. **전처리기(PreProcessor)**는 생성된 **트리 결과를 토대**로, 이미 만들어진 테이블이나 뷰 등으로 구성되지 않는지, 존재하지 않은 열을 포함하지는 않는지, 조회 권한이 없는 테이블을 조회하는지 등의 **유효성 검증.** 4. **옵티마이저(Optimizer)**는 트리를 구성하는 오브젝트의 **데이터를 효율적으로 가져오기 위한** **실행 계획 수립** 5. **엔진 실행기(Engin Executor)**는 **수립된 실행 계획**으로 **스토리지 엔진을 호출**해 **데이터 가져오기.** 6. 가져온 데이터 **엔진 실행기**가 **불필요한 데이터 필터링**하여 **사용자에게 전달.** ### 옵티마이저 **DBMS의두뇌**라고 불리는 가장 핵심적인 역할. **파서 트리를 토대**로 **필요하지 않은 조건 제거**, **연산 과정 단순화**, **테이블 접근 순서 제어**, **인덱스 사용 여부 결정**, **임시 테이블 사용 여부 결정** 등의 **실행 계획 수립.** 단, 실행 계획으로 **도출할 수 있는 경우의 수가 지나치게 많을 경우** **모든 실행 계획을 판단하지 않는다.** 이것은 **옵티마이저가 선택한 결과**가 **최상의 실행 결과는 아닐 수 있다**는 것이다. ### 엔진 실행기 **옵티마이저의 실행 계획**을 통해 **스토리지 엔진**에서 **데이터를 가져오는 역할**. **읽어온 데이터**를 **정렬**, **조인**, **불필요한 데이터 필터링** 처리. MySQL **엔진의 부하를 줄이려면** 스토리지 엔진에서 **가져오는 데이터양을 줄이는게 매우 중요!** ## 2.1.3 DB 오브젝트 용어 MySQL은 2차원 배열 형태로 테이블을 관리한다. 테이블은 로우(행)과 컬럼(열)을 통해서 데이터를 관리하고 표현한다. ### 기본키 Primary Key (주 키) **특정 행을 대표하는 열을 가르키는** 용어 MySQL에서 **기본 키**는 **클러스터형 인덱스(Clustered Index)**로 작동한다. 이는 **기본 키의 구성 열 순서를 기준**으로 **물리적인 스토리지에 데이터가 쌓인다**는 뜻이다. 즉, **비슷한 기본 키 값들**이 **근거리에 적재**되므로 **기본 키를 활용**하여 **인덱스 스캔을 수행**하며 **더 빠르게 데이터 접근**이 가능하다. > 인덱스의 주의사항 **기본 키와 똑같은 인덱스를 생성**하면 인덱스가 저장되는 **물리적 공간이 낭비**되는 한편 **데이터의 삽입, 삭제, 수정에 따른** **인덱스 정렬의 오버헤드가 발생**한다. 예시 CREATE TABLE 학생 ( 학번 INT(11) NOT NULL, 이름 VARCHAR(14) NOT NULL, … PRIMARY KEY (학번), INDEX I_학번 (학번) ) 이러한 인덱스의 경우 공간 낭비 & 오버헤드 발생으로 비용증가 → 삭제하자. > ### 외래 키 Foreign Key 외래 키는 **외부에 있는 테이블을 항상 참조**하면서, **외부 테이블의 데이터가 변경**되면 **함께 영향을 받는 관계를 설정**하는 키. 연관관계가 필요한 **테이블에서의 변경사항이 발생할 때마다** **외래 키 설정조건을 항상 검증**하므로 **데이터 정합성 향상을 위해**서라도 **외래 키를 설정**해야 한다. ### 인덱스 Index 인덱스는 데이터베이스에서 키 값으로 실제 **데이터 위치를 식별하고 데이터 접근속도를 높이고자 생성되는**, **키 기준으로 정렬된 오브젝트이다.** ### 고유 인덱스 Unique Index **인덱스를 구성하는 열들의 데이터가 유일**하다는 의미이다. 차례로 정렬되는 인덱스 열의 **데이터는 서로 중복되지 않고 유일성을 유지**한다. ```sql ALTER TABLE 학생 ADD UNIQUE INDEX 연락처_인덱스(연락처); ``` > 기본 키와 고유 인덱스의 차이점 **기본 키와 고유 인덱스** 모두 **데이터의 유일성을 보장해야 하는 특성**과 **효율적인 데이터 접근을 위한 인덱스의 수단으로 사용** 다만, **기본 키에는 NULL을 입력할 수 없지만** **고유 인덱스**에는 **얼마든지 입력할 수 있다**는 차이점이 존재. > ### 비고유 인덱스 Non-unique Index 비고유 인덱스는 고유 인덱스에서 데이터의 유일한 속성만 제외한 키이다. 인덱스가 재정렬되더라도 **인덱스 열의 중복 체크를 거치지 않는다.** ```sql ALTER TABLE 학생 ADD INDEX 이름_인덱스 (이름); ``` ### 뷰 View 뷰 == 가상 테이블 **제한된 정보만을 제공**할 수 있다. **시스템을 안전하게 운영**하고 **개발**할 수 있는 환경을 제공하는 만큼 **보안성 측면**에서 뷰의 **가치가 부각**될 수 있다. > 뷰를 사용하는 이유 **일부 데이터**에 대해서만 데이터를 **공개**하고, 노출에 민감한 **데이터**에 대해서는 **제약을 설정**할 수 있는 **보안성** 때문. 또한, 여러 개의 테이블을 병합해서 활용할 대는 **성능**을 고려한 **최적화된 뷰를 생성함**으로써 **일관된 성능 제공** > ## 2.2.1 서브쿼리 위치에 따른 SQL 용어 서브쿼리(subquery)란 쿼리 안의 보조쿼리를 가리키는 용어이다. 가장 바깥쪽의 `SELECT` 문인 메인 쿼리(main query)를 기준으로 안쪽에 작성된 `SELECT` 에 따라 불리는 이름이 다르다. ```sql SELECT (SELECT ... FROM ...) -- SELECT 절: 스칼라 서브쿼리 (scalar subquery) FROM (SELECT ... FROM ...) -- FROM 절: 인라인 뷰(inline view) WHERE COLUMN_NAME IN (SELECT ... FROM ...) -- WHERE 절: 중첩 서브쿼리(nested subquery) ``` ### 스칼라 서브쿼리 `**SELECT` 절에서 사용되는 서브쿼리**를 **스칼라 서브쿼리**라고 한다. 스칼라 서브쿼리는 `SELECT` 절 뿐만 아니라 `FROM` , `WHERE` 절 안에서도 사용이 가능하다. **스칼라 서브쿼리의 결괏값은 1행 1열의 구조로 출력되어야 한다.** 만약 스칼라 서브쿼리의 결과값이 2개 이상 나온다면 에러가 발생할 것이다. ### 인라인 뷰 `**FROM` 절에서 사용되는 서브쿼리**를 **인라인 뷰**라고 한다. `**FROM` 절 내부**에서 **일시적으로 뷰를 생성하는 방식**이므로 인라인 뷰라고 불린다. **인라인 뷰의 결과**는 내부적으로 **메모리 또는 디스크에** **임시 테이블을 생성하여 활용**한다. ### 중첩 서브쿼리 `**WHERE` 절에서 사용되는 서브쿼리를 중첩 서브쿼리**라고 한다. 중첩 서브쿼리는 보통 비교연산자(=, <, >, ≤, ≥, <>, ≠)를 비롯해 `IN`, `EXISTS`, `NOT EXIST` 문에 많이 사용한다. ## 2.2.2 메인쿼리와의 관계성에 따른 SQL 용어 ### 비상관 서브쿼리 Non Correlated Subquery 비상관 서브쿼리는 **메인쿼리와 서브쿼리 간에 관계성이 없음**을 말한다. 즉, **서브쿼리가 독자적으로 실행된 뒤 메인쿼리에게 그 결과를 던져주는 형태**인 것이다. ```sql SELECT ... FROM 학생 WHERE ... IN ( SELECT ... FROM 지도교수 -- 비상관 쿼리 -> 메인 쿼리와 연관성 없음 ) -- 실행순서: 서브쿼리 -> 메인쿼리 ``` ### 상관 서브 쿼리 Correlated Subquery 상관 서브쿼리는 **메인쿼리와 서브쿼리 간에 관계성이 있음**을 의미한다. **서브쿼리가 실행되려면 메인쿼리의 값을 받아야 함**으로 서로 관계가 깊게 유지된다. ```sql SELECT ... FROM 학생 WHERE ... IN ( SELECT ... FROM 지도교수 WHERE 학생.학번 = ... -- 상관 쿼리 -> 메인 쿼리에 영향을 받음 학생.학번 ) -- 실행 순서: 메인쿼리 -> 서브쿼리 -> 메인쿼리 ``` ### 두둥 옵티마이저 등장! 우리는 의도하는 결과를 얻기위해 위와 같이 서브쿼리들을 사용해서 쿼리를 작성할 것이다. 그러나 우리가 작성한 쿼리는 결국 옵티마이저에 의해 실행계획이 결정된다. 그렇기 때문에 **DB 버전 혹은 옵티마이저에 따라** **서브쿼리가 제거**되고 **하나의 메인쿼리로 통합**되는 **뷰 병합(View Merging), 즉 SQL 재작성(rewrite)이 작동할 수 있다.** ## 2.2.3 반환 결과에 따른 SQL 용어 ### 단일행 서브쿼리 Single-Row Subquery **서브쿼리 결과**가 **1건의 행으로 반환**되는 쿼리이다. **주로 스칼라 서브쿼리**라고 생각하면 된다. ### 다중행 서브쿼리 Multiple-Row Subquery **서브쿼리 결과가 여러 건의 행으로 반환**되는 쿼리이다. ```sql SELECT ... FROM ... WHERE 학번 IN ( SELECT MAX(학번) FROM 학생 GROUP BY 전공코드 ) -- 다중행 서브쿼리 ``` ### 다중열 서브쿼리 Multiple-Column Subquery **서브쿼리 결과가 여러 개의 열과 행**으로 반환되는 쿼리이다. ```sql SELECT ... FROM ... WHERE (이름, 전공코드) IN ( SELECT 이름, 전공코드 FROM 학생 WHERE 이름 LIKE '김%' ) -- 다중열 서브쿼리 ``` ## 2.2.4 조인 연산방식 용어 다수의 테이블에 **흩어져 있는 데이터를 결합할 때** **조인(Join) 방식 사용** ### 내부 조인 Inner Join **양쪽에 모두 존재하는 데이터**만 반환 (교집합) ```sql -- 명시적 조인 SELECT 학생.학번, 학생.이름, 지도교수.교수명 FROM 학생 INNER JOIN 지도교수 ON 학생.학번 = 지도교수.학번 -- 암시적 조인 SELECT 학생.학번, 학생.이름, 지도교수.교수명 FROM 학생, 지도교수 WHERE 학생.학번 = 지도교수.학번 ``` ### 왼쪽 외부 조인 Left Outer Join 왼쪽 테이블을 기준으로 **조건과 일치하지 않더라도** 왼**쪽 테이블의 결과는 최종 결과에 포함**된다. ```sql SELECT 학생.학번, 학생.이름, 지도교수.교수명 FROM 학생 LEFT OUTER JOIN 또는 LEFT JOIN 지도교수 ON 학생.학번 = 지도교수.학번 ``` ### 오른쪽 외부 조인 Right Outer Join **조인 조건과 일치하지 않더라도** **오른쪽 테이블의 결과는 최종 결과에 포함**된다. ```sql SELECT 지도교수.학번, 학생.이름, 지도교수.교수명 FROM 학생 RIGHT OUTER JOIN 또는 RIGHT JOIN 지도교수 ON 학생.학번 = 지도교수.학번 ``` > LEFT JOIN vs RIGTH JOIN 사람의 인지적 특성상 보통 왼쪽 → 오른쪽을 정방향으로 인식. 따라서, LEFT JOIN을 주로 사용하게 됨. 일관된 조인문을 사용함으로써 유지보수나 관리편의성 측면에서 이점을 얻자 > > FULL OUTER JOIN은? FULL OUTER JOIN의 경우 LEFT JOIN 과 RIGTH JOIN이 통합된 방식으로 MySQL과 MariaDB에서는 지원하지 않는다고 한다. > ### 교차 조인 Cross Join **교차 조인**은 **데카르트 곱(cartesian product) 곱집합** 개념, **조인에 참여**하는 테이블에서 **발생할 수 있는 모든 조합을 찾아내어 반환.** ```sql -- 명시적 조인 SELECT 학생.학번, 학생.이름, 지도교수.학번, 지도교수.교수명 FROM 학생 CROSS JOIN 지도교수 -- 암시적 조인 SELECT 학생.학번, 학생.이름, 지도교수.학번, 지도교수.교수명 FROM 학생, 지도교수 -- 암시적 조인의 경우 WHERE 절의 조인 조건문이나 -- JOIN 키워드를 명시하지 않고 작성하면 CROSS JOIN이 된다. ``` ### 자연 조인 Natural Join 2개 테이블에 **동일한 Column 이름이 있을 때 조건을 따로 작성하지 않아도 조인**이 되는 방식. ```sql SELECT 학생.*, 지도교수.* FROM 학생 NATURAL JOIN 지도교수 ``` 단, 동일한 Column 이름이 존재하지 않을경우 Cross Join발생. ### 그럼 무슨 조인 사용할까? 명시적 `**INNER JOIN**` 혹은 `**LEFT OUTER JOIN**` 무조건이다. 가독성과 유지보수성이 넘사. ## 2.2.5 조인 알고리즘 용어 ### 드라이빙 테이블(Driving table)과 드리븐 테이블(Driven table) 드라이빙 테이블 == 먼저 접근하는 테이블 드리븐 테이블 == 뒤 늦게 접근하는 테이블 ```sql SELECT 학생.학번, 학생.이름, 비상연락망.관계, 비상연락망.연락처 FROM 학생 JOIN 비상연락망 ON 학생.학번 = 비상연락망. 학번 WHERE 학생.학번 IN(1, 100) -- 드라이빙 테이블 == 학생 -- 드리븐 테이블 == 비상연락망 ``` 가능하면 **적은 결과가 반환될 것**으로 **예상되는 테이블**을 **드라이빙 테이블**로 선정. **조인 조건절의** **열**이 **인덱스로 설정**되도록 구성. ### 중첩 루프 조인 Nested Loop Join, NL Join **중첩 루프 조인**은 **드라이빙 테이블**의 **데이터 1건당** **드리븐 테이블**을 **반복**해 검색하며 최종적으로 양쪽 테이블에 공통된 데이터를 출력한다. (드라이빙 테이블 N * 드리븐 테이블 M) ```sql SELECT ... FROM 학생 JOIN 비상연락망 ON 학생.학번 = 비상연락망.학번 WHERE 학생.학번 IN(1, 100) -- -- 드라이빙 테이블 결과는 1과 100 2개 -- 드리븐 테이블 비상연락망의 테이블이 만약 1000건이라고 하면 -- 조건에 맞는 데이터를 가져오기 위해 최악 2000건의 데이터 접근이 일어나게 됨. -- 만약, 조인 조건절이 모두 인덱스가 걸려있다면 == 학생 테이블 학번 컬럼 인덱스, 비상연락망 학번 컬럼 인덱스 -- 드리븐 테이블 비상연락망 테이블에서 조건에 맞는 인덱스만 접근. -- 데이터 접근 횟수 감소. ``` 인덱스는 **인덱스로 정의된 열 기준으로 순차 정렬**되지만, 해당 인덱스가 **비고유 인덱스일 경우** 인덱스를 이용해 테이블의 데이터를 찾아가는 과정에서 **임의 접근 방식인 랜덤 액세스(Random Access)가 발생**한다. 따라서, 랜덤 액세스를 줄일 수 있도록 **데이터의 액세스 범위를 좁히는 방향**으로 **인덱스를 설계**하고 **조건절을 작성**해야 한다. **기본 키**는 **클러스터형 인덱스**이므로 기본 키의 순서대로 테이블의 데이터가 적재되어 있어 **조회 효율이 매우 높다.** ### 블록 중첩 루프 조인 Block Nested Loop Join, BNL Join **인덱스가 걸려있지 않는 상황**의 **중첩 루프 조인의 성능을 개선하기 위해** 등장. 1. **조인 버퍼(Join Buffer)**에 **드라이빙 테이블의 결과**를 조인 버퍼가 가득 찰때까지 **적재.** 2. **조인 버퍼와 드리븐 테이블 데이터를 조인**하는 식으로 **반복**. 해당 과정은 **드리븐 테이블의 풀 스캔을 줄이는 게 목적**이다. 드라이빙 테이블 Full Scan → 드리븐 테이블 Full Scan 반복을 최소화 시킬 수 있다. ### 배치 키 엑세스 조인 Batched Key Access Join(BKA 조인) **중첩 루프 조인 방식의 인덱스의 랜덤 엑세스의 단점을 해결하고자,** **접근할 데이터를 미리 예상하고 가져오는**데 착안된 조인 알고리즘. 블록 중첩 루프 조인 방식의 **조인 버퍼 개념을 사용**하고, **드리븐 테이블**의 **데이터를 예측**하고 **정렬된 상태**로 담는 **랜덤 버퍼의 개념이 추가**되었다. 이때, **드리븐 테이블의 데이터를 예측하고 정렬된 상태로 버퍼에 적재하는 기능**을 **다중 범위 읽기(Multi Range Read, MRR)**라고 한다. **미리 예측, 정렬된 데이터를 액세스** 하기 때문에 랜덤 액세스가 아닌 **시퀀셜 액세스가 일어나게 된다.** ### 해시 조인 Hash Join MySQL 8.0.18 버전 부터 지원되는 방식.(상용 DMBS 오라클 같은 애들은 이미 지원하고 있었다고 한다.) 블록 중첩 루프 조인과 배치 키 액세스 조인의 한계를 탈피하기 위함. **조인에 참여하는 각 테이블의 데이터**를 내부적으로 **해시값으로 만들어 내부 조인을 수행**한다. **내부 조인 수행 결과**는 **조인 버퍼에 저장**되므로 **조인열의 인덱스를 필수로 요구하지 않아도 된다.** ## 2.3.1 기초 용어 - 오브젝트 스캔 유형 ### 테이블 풀 스캔 Table Full Scan 테이블 풀 스캔은 **인덱스를 거치지 않고** **테이블로 바로 직행**하여 **처음부터 끝까지 데이터를 훑어보는 방식.** 테이블 풀 스캔은 **인덱스 없이 사용하는 유일한 방식**. **성능 측면에서는 부정적**. ### 인덱스 범위 스캔 Index Range Scan 인덱스 범위 스캔은 **인덱스를 범위 기준으로 스캔**한 뒤 **스캔 결과를 토대로** **테이블의 데이터를 찾아가는 방식.** 좁**은 범위를 스캔**할 때 **매우 효율적**, 넓은 범위의 경우 비효율적인 방식 SQL의 `BETWEEN ~ AND`, `<`, `>`, `LIKE` 구문 등에 사용됨. ### 인덱스 풀 스캔 Index Full Scan 인덱스 풀 스캔은 **인덱스를 처음부터 끝까지 스캔**하는 방식. 단, **테이블에 접근하지 않고** **인덱스로 구성된 열 정보만 요구하는 SQL 문에서 인덱스 풀 스캔이 수행됨.** 테이블 풀 스캔보다는 성능 좋음. 그러나, 최대한 검색 범위를 줄이는 방향으로 SQL 튜닝 권장. ### 인덱스 고유 스캔 Index Unique Scan 인덱스 고유 스캔은 **기본 키나 고유 인덱스로 테이블에 접근하는 방식.** 인덱스를 사용하는 스캔 방식 중 **가장 효율적인 스캔 방법.** `**WHERE 기본 키 혹은 고유 인덱스 컬럼 = 조건**` 으로 되어 있을 경우 활용됨. ### 인덱스 루스 스캔 Index Loose Scan 인덱스 루스 스캔은 **인덱스의 필요한 부분들만 골라 스캔하는 방식.** **필요**한 데이터와 불필요한 **데이터를 구분**한 뒤 불필요한 인덱스 키는 무시. `GROUP BY` , `MAX()`, `MIN()` 함수의 경우 작동. ### 인덱스 병합 스캔 Index Merge Scan 인덱스 병합 스캔은 **테이블 내에 생성된 인덱스들을 통합해서 스캔하는 방식.** **옵티마이저**가 **서로 다른 인덱스**를 가져와 **결합(Union)과 교차(Intersection) 방식**을 통해 서로 다른 인덱스를 **각각 실행**하며 원하는 데이터를 찾음. 개별 인덱스를 각각 수행하므로 **인덱스에 접근하는 시간이 몇 배로 걸림**. 따라서, 별개의 인덱스를 **하나의 인덱스로 통합** 혹은 SQL문 자체를 **독립된 하나의 인덱스만 사용**하도록 **변경** ## 2.3.1 기초 용어 - 디스크 접근 방식 ### 시퀀셜 액세스 Sequential Access 인접한 페이지를 차례대로 읽는 **순차 접근** 방식, 테이블 풀 스캔에서 활용됨. ### 랜덤 액세스 Random Access 물리적으로 떨어진 페이지들에 **임의 접근** 방식. 물리적으로 떨어진 페이지에 접근하기 때문에 **디스크 헤더가 움직이는 비용이 보다 큼.** **접근 범위를 줄이고** **인덱스를 활용할 수 있도록 튜닝**해야 함. ## 2.3.1 기초 용어 - 조건 유형 ### 엑세스 조건 Access Condition **디스크에 있는 데이터에 어떻게 접근할 것인지**를 다루는 **SQL 튜닝의 핵심**. **옵티마이저**는 `**WHERE` 절의 특정 조건문을 이용**해 **소량의 데이터를 가져**오고, **인덱스**를 통해 **시간 낭비를 줄이는 조건절을 선택**하여, 스토리지 엔진에 접근하고 MySQL 엔진으로 데이터를 가져온다. ```sql SELECT * FROM TAB WHERE ID = 1 AND CODE ='A'; -- ID 컬럼 인덱스 O, CODE 컬럼 인덱스 X ``` 옵티마이저는 위와 같은 경우 인덱스가 존재하는 `ID` 컬럼을 통해 디스크에 접근한다. 이게 엑세스 조건이다. ### 필터 조건 Filter Condition 엑세스 조건을 이용해 MySQL 엔진으로 **가져온 데이터**를 기준으로 추가로 **불필요한 데이터를 제거**하거나 가공하는 **조건.** 필터 조건으로 **필터링되어 제거된 데이터가 다수 존재 할수록 효율이 좋지 않다.** **스토리지에서 MySQL 엔진**으로 넘기는 데이터의 **오버헤드가 발생**하기 때문. **제거될 데이터**라면 **스토리지 엔진에 접근하는 과정에서 같이 제외**되는 편이 **효율적** ```sql SELECT * FROM TAB WHERE ID = 1 AND CODE ='A'; -- CODE = 'A' 이게 필터 조건임. ``` ## 2.3.2 응용 용어 ### 선택도 Selectivity 테이블의 **특정 열을 기준**으로 해당 열의 **조건절에 따라** **선택되는 데이터 비율** **낮은 선택도를 가지는 열**은 **인덱스 생성**할 때 **주요 고려 대상** ```sql 선택도 = 선택한 데이터 건수 / 전체 데이터 건수 변형된 선택도 = 1 / DISTINCT(COUNT COLUMN_NAME) -- 보통 중복이 제거된 데이터의 건수를 활용함. ``` ```sql -- 예시 학번 -- 총 100명의 학생 중 학번을 통해 학생을 찾는 경우 학번 열의 선택도 = 1 / 100 = 0.01 -- 낮은 선택도 -- 예시 성별 -- 총 100명의 학생 중 성별을 통해 학생을 찾는 경우 성별 열의 선택도 = 50 / 100 = 0.5 -- 높은 선택도 ``` ### 카디널리티 Cardinality 사전적 정의는 하나의 데이터 유형으로 정의되는 데이터 행의 개수. 현업에서는 **전체 행에 대한** **특정 열**의 **중복 수치**를 나타내는 **지표**로 사용. **중복**되는 값이 **적**다면 **카디널리티 높음** **중복**되는 값이 **많**으면 **카디널리티 낮음** ```sql 카디널리티 = 전체 데이터 건수 * 선택도 -- 예시 -- 주민등록번호: 카디널리티 높음 -- 이름: 카디널리티 중간 -- 성별: 카디너리티 낮음 ``` ### 힌트 Hint 우리가 데이터베이스에 **데이터를 빨리 찾을 수 있도록** **추가 정보를 전달하는 것** ```sql -- 주석처럼 힌트 명시하는 법 SELECT 학번, 전공코드 FROM 학생 /*! USE INDEX (학생_IDX01) */ WHERE 이름 = '유재석'; -- 쿼리의 일부로 작성 SELECT 학번, 전공코드 FROM 학생 USE INDEX (학생_IDX01) WHERE 이름 = '유재석'; ``` 단, **옵티마이저가 우리의 힌트를 무조건 참고하는건 아니다.** 비효율적이라고 생각되면 얄짤 없다. (과연 내가 옵티마이저보다 뛰어날까?) **힌트를 사용할 경우 별도의 관리가 필요하다.** 어느 **특정 인덱스**를 이용해 **힌트를 관리**하다가 그 **인덱스를 삭제**하게 된다면 **에러를 발생**시키고 **서비스 장애**가 된다. ### 콜레이션 Collation **특정 문자셋**으로 데이터베이스에 저장된 값을 **비교**하거나 **정렬**하는 **규칙.** ```sql -- 예시 utf8_bin: A > B > a > b utf8_general_ci: A > a > B > b ``` > 그럼 캐릭터 셋(Character Set)이랑 뭐가 다름? 캐릭터 셋 == 데이터를 어떻게 저장 할 것인지에 대한 규칙 콜레이션 == 데이터를 어떻게 비교할 것인지에 대한 규칙