[DataBase] CS 스터디 13주차
Table Full Scan, Index Range Scan에 대해 설명해 주세요.
테이블로의 액세스 방법은 Full Scan과 Range Scan 두 가지가 있습니다.
- Full Scan
- 테이블에 포함된 레코드를 처음부터 끝까지 전부 읽어 들이는 방식입니다.
- Table Full Scan이라고 합니다.
- Range Scan
- 테이블의 일부 레코드에만 액세스하는 방식입니다.
- Index Range Scan이라고 합니다.
구현에 따라 다소 변형의 차이가 있지만, 관계형 데이터베이스에서 테이블로의 액세스 방법은 기본적으로 2가지 패턴밖에 없습니다.
- 이 2가지 패턴은 테이블의 책으로 생각하면 이해하기 쉽습니다.
- Full Scan: 처음부터 마지막 페이지까지 1페이지씩 넘기면서 모조리 읽는 방법
- Range Scan: 색인(인덱스)에서 찾는 단어가 있는 페이지만을 선택해 읽는 방법
가끔은 인덱스를 타는 쿼리임에도 Table Full Scan 방식으로 동작하는 경우가 있습니다. 왜 그럴까요?
- 데이터 분포도 기반 판단
- 조회하려는 데이터가 전체 테이블의 20-25% 이상을 차지할 경우, 옵티마이저는 인덱스 스캔보다 Full Scan이 더 효율적이라고 판단합니다.
- IN 연산자 사용 시 포함된 데이터의 비율이 높다면 Table Full Scan을 선택합니다.
- 인덱스 사용이 비효율적인 경우
- 인덱스 컬럼을 변형하여 사용하는 경우 (예:
TO_CHAR(column_name)
) - LIKE 검색 시 와일드카드(%)가 앞에 위치하는 경우 (
LIKE '%값'
) - 복합 인덱스의 순서를 올바르게 사용하지 않은 경우
- 인덱스 컬럼을 변형하여 사용하는 경우 (예:
- 시스템 환경 요인
- 테이블의 크기가 매우 작아서
DB_FILE_MULTIBLOCK_READ_COUNT
이내의 블록으로 구성된 경우 - 병렬 처리가 필요한 경우 (병렬처리는 Full Table Scan이 더 효율적)
- 테이블의 크기가 매우 작아서
COUNT (개수를 세는 쿼리) 는 어떻게 동작하나요? COUNT(1)
, COUNT(*)
, COUNT(column)
의 동작 과정에는 차이가 있나요?
COUNT는 기본적으로 행의 개수를 세는 집계 함수입니다.
COUNT(*)
동작 방식
- 테이블의 모든 행을 카운트하며, NULL 값도 포함합니다.
- 실제로 데이터를 읽지 않고 레코드 수만 불러오기 때문에 가장 빠른 성능을 보입니다.
- 옵티마이저가 가장 효율적인 Execution Plan을 가질 수 있습니다.
- 일반적인 행 카운트는
COUNT(*)
를 사용하는 것이 가장 효율적입니다.
COUNT(1)
동작 방식
COUNT(*)
와 동일한 방식으로 동작합니다.- 쿼리 결과의 모든 레코드를 1로 대체한 후 카운트합니다.
- 성능상
COUNT(*)
와 차이가 없습니다. - 그렇기 때문에 사실상
COUNT(*)
을 사용하지 않고COUNT(1)
을 사용할 이점은 없습니다.
COUNT(column)
동작 방식
- 지정된 컬럼의 NULL이 아닌 값만 카운트합니다.
- 실제 데이터를 읽어야 하므로
COUNT(*)
보다 성능이 떨어집니다. - NULL 체크 로직이 추가되어 처리 과정이 더 복잡합니다.
- NULL을 제외한 카운트가 필요한 경우에만
COUNT(column)
을 사용해야 합니다
SQL Injection에 대해 설명해 주세요.
SQL Injection은 웹 애플리케이션의 보안 취약점을 이용하여 악의적인 SQL 쿼리를 주입하는 공격 기법입니다. 데이터베이스의 중요 정보를 탈취하거나 조작할 수 있어 매우 위험합니다.
주요 공격 유형
- Error based SQL Injection
- 데이터베이스 에러를 유발시켜 정보를 획득하는 방식입니다.
- Union based SQL Injection
- SQL의 UNION 연산자를 사용하여 원래 쿼리의 결과에 추가 데이터를 결합 후 임의의 테이블 정보를 조회하는 방식입니다.
- 두 쿼리의 컬럼 수와 데이터 타입이 일치해야 합니다.
- Blind SQL Injection
- 데이터베이스의 true/false 응답만으로 정보를 유추하는 방식입니다.
- 직접적인 데이터 노출이 없을 때 사용됩니다.
방어 전략
- 입력값 검증
- 사용자 입력에 대한 철저한 검증을 수행해야 합니다.
- 특수문자 및 SQL 예약어를 필터링합니다.
- Prepared Statement 사용
- 쿼리문과 파라미터를 분리하여 처리합니다.
- ex) 우리가 코드 짤 때 항상 하는 파라미터를 바인딩하는 것을 생각하면 됨
- SQL Injection을 원천적으로 방지할 수 있습니다.
- 쿼리문과 파라미터를 분리하여 처리합니다.
- 에러 메시지 제한
- 데이터베이스 에러 메시지를 사용자에게 직접 노출하지 않습니다.
- 공격자가 데이터베이스 구조를 파악하지 못하도록 합니다.
그렇다면, 우리가 서버 개발 과정에서 사용하는 수많은 DB 라이브러리들은 이 문제를 어떻게 해결할까요?
ORM 프레임워크를 예로 들어 보겠습니다.
방어 전략
- Prepared Statement를 자동으로 사용합니다.
- 특수 문자를 자동으로 이스케이프 처리합니다.
- ex) “\“를 특수 문자 앞에 붙여서 해당 문자를 일반 문자로 취급하도록 만듦
- 타입 캐스팅을 강제하여 입력값을 검증합니다.
- ex) 문자열 형태의 “2024-10-29”가 들어왔을 때 이걸 날짜 타입으로 변환
주의사항
- 동적 쿼리 생성 시에는 여전히 SQL Injection 위험이 있습니다.
- ORM만으로는 완벽한 방어가 어려우므로 추가적인 보안 계층이 필요합니다.
- 추가적인 보안 계층
- 입력값 검증
- 서버 사이드에서 입력값 검증을 수행해야 합니다.
- 허용된 입력만 받아들이는 화이트리스트 방식의 검증이 권장됩니다.
- 데이터베이스 권한 제한
- 애플리케이션에서 사용하는 DB 계정의 권한을 최소화합니다.
- 필요한 테이블과 작업에 대해서만 접근 권한을 부여합니다.
- 입력값 검증
- 추가적인 보안 계층
Leave a comment