
Velog에게 뒷통수 2번째... 열심히 작성한 내용... 날리지마라.. 저장 좀 잘 해라... DB 스터디 하는데 내용 두 번이나 날라가니까 기분 묘함. 근데 이번엔 그래도 다 날라간 거 아니고,,, 정말.. 한 주제에 일부라.. 봐준다. 진짜 화나게 흐즈므르...😡
✏️ MySQL Scan의 종류에 대해 설명해주세요
MySQL에서 "Scan"이란 테이블에서 데이터를 읽어오는 방식 중 하나이며, 쿼리를 실행할 때 어떻게 데이터를 탐색(검색)하는지를 설명한다. 일반적으로 MySQL 옵티마이저는 가장 효율적인 접근 방식을 선택하려고 하며, 이때 다양한 Scan 방법이 사용된다.
테이블 전체를 순차적으로 스캔하는 방식이다.
✅ 설명
테이블의 모든 행을 처음부터 끝까지 훑는 방식이다. 인덱스가 없거나, 인덱스를 무시하는 조건이 있는 경우 사용된다.
✅ 특징
레코드가 많은 테이블의 경우, Full Table Scan은 성능 저하의 주범이 되는 경우가 많다.
✅ 언제 쓰이나?
WHERE 절이 인덱스를 사용할 수 없을 때✅ 예시
SELECT * FROM users WHERE age + 5 > 30;
위 쿼리는 age + 5처럼 컬럼에 연산이 들어가므로 인덱스를 사용하지 못하고 Full Table Scan 방식을 사용한다.
테이블이 아니라 인덱스를 통해 데이터를 탐색하는 방식이다.
인덱스를 전체 순회하지만 정렬된 순서로 데이터를 가져온다.
주로 ORDER BY와 함께 사용됨
✅ 예시:
SELECT age FROM users ORDER BY age;
인덱스를 통해 정렬된 데이터를 가져오므로 테이블을 정렬할 필요가 없어 성능이 좋습니다.
✏️ DB에서 사용되는 두 가지 Lock과 Deadlock에 대해 설명해주세요
여러 트랜잭션이 동시에 동일 데이터를 액세스할 때 데이터 무결성을 유지하고 충동을 방지하는 매커니즘
📌 Lock의 목적
💡 개념
예제
-- 트랜잭션 A
SELECT * FROM products WHERE id = 1 LOCK IN SHARE MODE;
-- 트랜잭션 B
SELECT * FROM products WHERE id = 3 LOCK IN SHARE MODE;
products.id = 1에 공유 락을 건다.참고
FOR SHARESELECT * FROM 테이블명 WHERE 조건 FOR SHARE;
💡 개념
-- 트랜잭션 A
SELECT * FROM products WHERE id = 1 FOR UPDATE;
products.id = 1에 배타 락을 건다.S-lock도 못 걸고, X-lock도 못 건다.-- Transaction 1: 배타 락 설정
START TRANSACTION;
UPDATE member SET first_name = 'Ga Eun'
WHERE first_name = 'gagle';
-- Transaction 2: 동일 리소스에 대해 배타 락 시도
(대기 상태)
START TRANSACTION;
UPDATE member SET first_name = '가은' WHERE
first_name = 'gagle';
UPDATE, DELETE 구문 사용 시 내부적으로 X-lock이 걸린다.참고
SELECT → 기본적으로 락 ❌SELECT ... FOR SHARE, FOR UPDATE → 명시적 LockUPDATE, DELETE → 내부적으로 X-lock 서로 다른 트랜잭션이 서로의 Lock을 기다리다가 무한 대기 상태에 빠지는 상황으로, 두 트랜잭션 모두가 블로킹 상태에 진입하여 서로의 블로킹을 해결할 수 없는 상태이다.

설명
Coupon 테이블의 데이터에 대해 쓰기 잠금(X-lock)을 건다.Coupon 테이블에 쓰기, 읽기가 불가하다.Member 테이블의 데이터에 대해 쓰기 잠금(X-lock)을 건다.Member 데이터에 읽기 시도를 한다. (S-lock 시도)Coupon 데이터에 읽기 시도를 한다. (S-lock 시도)🚨데드락 발생!
트랜잭션 A, B의 블로킹 상태는 상대 트랜잭션이 종료되어야 해결되는데 서로의 트랜잭션이 블로킹 상태이기 때문에 종료되지 않으므로 데드락 상태가 된다.
데이터베이스의 트랜잭션과 자원 간의 의존성을 그래프로 표현
구성
조건
그래프에 사이클(순환)이 존재하면 데드락이 발생한 것
T1 → T2 → T3 → T1
# T1이 T2가 점유한 자원을 기다리고 있고, T2가 T3이 점유한 자원을 기다리고 있고,,,
→ 순환 구조 (사이클) → Deadlock 발생 !
트랜잭션이 일정 시간 동안 잠금 대기 상태에 머무르면 데드락이 발생했다고 판단하고 자동으로 롤백한다.
innodb_lock_wait_timeout = 5 # MySQL에서 설정 가능
위 설정이면, 트랜잭션이 5초 이상 잠금 대기하면 강제 종료
❗ 단점
진짜 데드락이 아닌 경우에도 단순히 느린 쿼리가 실패할 수 있다.
적절하지 않은 타임아웃 설정은 정상 트랜잭션의 실패 가능성을 증가시킨다.
MySQL InnoDB는 기본적으로 Wait-For Graph 기반 Deadlock 탐지 알고리즘을 사용한다.
즉, 사이클이 생기면 즉시 감지하고, 그 중 하나의 트랜잭션을 자동으로 ROLLBACK 한다.
✏️ MySQL에서 사용되는 스토리지 엔진 수준의 락에 대해 설명해주세요
테이블 레코드 즉, 특정 행에 잠금을 거는 것을 의미한다.
설명
SELECT ... FOR UPDATE, UPDATE, DELETE 등 트랜잭션이 DML 구문을 실행할 때 자동으로 거는 락이다.예시
# member 테이블에서 last_name이'J'로 시작하는 구성원은 300명
SELECT COUNT(*) FROM member WHERE last_name LIKE 'J%';
# 그 중에서 first_name이 MangKyu인 사원은 1명
SELECT COUNT(*) FROM member WHERE last_name LIKE 'J%' AND first_name = 'MangKyu';
# member 테이블에는 last_name 컬럼만으로 구성된 인덱스 KEY idx_last_name(last_name)가 존재한다.
# 해당 구성원의 등록일을 오늘로 변경하는 쿼리를 실행해보자.
UPDATE member SET register_date = NOW() WHERE last_name LIKE 'J%' AND first_name = 'MangKyu';
레코드가 아닌 레코드와 레코드 사이의 간격을 잠금으로써 레코드의 생성, 수정 및 삭제를 제어한다.
설명
예시

📌 즉, 아직 존재하지는 않지만 지정된 범위에 해당하는 인덱스 테이블 공간을 대상으로 거는 잠금이다.
예시
SELECT * FROM users WHERE id = 3 FOR UPDATE;
id=3 라는 유일하고 정확한 인덱스 인덱스 엔트리만 잠근다. id=3.1 이나 id=2.9 같은 값이 들어올 수 없다.SELECT * FROM users WHERE age BETWEEN 20 AND 30 FOR UPDATE;
SELECT * FROM users WHERE age = 30 FOR UPDATE;
age=25 라는 새로운 데이터를 추가할 수도 있다. 레코드 락과 갭 락을 합친 잠금으로, 갭 락은 단독으로 사용되기 보다는 넥스트 키 락의 일부로 함께 사용된다.
REPEATABLE READ에서 기본적으로 사용된다.예시
-- users 테이블, age 인덱스 있음
SELECT * FROM users WHERE age = 25 FOR UPDATE;
age=25의 행뿐만 아니라, 갭 락으로 그 앞뒤 인덱스 사이의 갭도 함께 잠긴다. AUTO_INCREMENT 열을 갖는 테이블에 INSERT할 때 다음 번호 생성을 위한 내부 락
AUTO_INCREMENT 컬럼은 여러 레코드가 동시에 INSERT 되더라도 중복되지 않고 순차적으로 증가하는 일련번호를 제공하기 위해 내부적으로 테이블 수준의 잠금인 자동 증가 락(Auto Increment Lock)을 사용한다.INSERT와 REPLACE와 같이 새로운 레코드를 저장하는 쿼리에서만 사용된다.AUTO_INCREMENT 값을 초기화하고 싶다면 아래의 쿼리를 사용해야 한다.ALTER TABLE tablename AUTO_INCREMENT = 1
✏️ SQL Injection에 대해 설명해주세요
SQL 인젝션은 웹 애플리케이션의 보안 취약점을 이용하는 공격 방식 중 하나이다. 이 공격은 애플리케이션의 데이터베이스 쿼리에 악의적인 SQL 코드를 "주입"하여 데이터베이스를 조작하거나 민감한 정보를 탈취하는 것을 목표로 한다.
🔎 SQL Injection 상황
SELECT USER FROM USER_TABLE WHERE USERNAME= '[ID 입력값]' AND PASSWORD = '[PWD 입력값]';
👨💻 해커가 관리자의 아이디가 Admin인 것을 확인한 후, 다음과 같은 입력값을 보낸다면?
username = Admin`; --
password : random
그럼 다음과 같은 쿼리문이 완성된다.
SELECT USER FROM USER_TABLE WHERE USERNAME = 'Admin'; --' AND PASSWORD = 'random';
주석 처리 --에 의해 USERNAME만 일치하더라도 로그인이 가능하도록 변경되었고, 해당 방식의 공격을 제대로 예방하지 않으면 해커는 관리자 권한을 가진 계정으로 로그인할 수 있게 된다.
가장 기본적인 형태의 SQL 인젝션으로, 사용자 입력이 검증없이 직접 SQL 쿼리에 포함될 때 발생한다.

UNIONSQL 연산자를 사용하여 여러 쿼리의 결과를 결합하는 방식으로, 추가적인 데이터를 조회할 때 사용한다.
Union Injection 성공 조건

UNION 키워드로 USER 테이블의 데이터도 가져올 수 있게 된다.공격자가 데이터베이스 구조를 모르는 상태에서 데이터베이스의 응답을 통해 정보를 추측하며 공격을 수행하는 방식이다. 블라인드 SQL 인젝션은 데이터베이스의 응답이 직접적으로 보이지 않을 때 사용한다.

위의 그림에서는 information_schema.tables에서 DB에 존재하는 모든 테이블의 정보를 가져오고, 여기서 첫 번째 테이블 이름을 조회하게 된다. 그리고, SUBSTR로 테이블 이름의 substring으로 첫 번째 글자만 추출하여 그 문자의 ASCII 값을 구한다.
결과적으로, 해당 조건이 참이될 땐, 쿼리는 성공적인 결과를 반환하고, 조건이 거짓이면 결과가 빈 결과가 되거나 페이지 상태가 다르게 나타난다.
이런식으로 반복적으로 조건이 맞는지 하나씩 물어보는 공격 방식이며, 공격자는 이 프로세스를 자동화 스크립트를 통해 단기간 내에 테이블 명을 알아낼 수 있다.

SLEEP 혹은 BENCHMARK이다. (MySQL 기준)SLEEP(2)도 실행되지 않는다. 공격자가 의도적으로 에러를 발생시키는 SQL 쿼리를 전송하여, 데이터베이스의 에러 메시지를 통해 구조나 데이터를 파악하는 방식이다.

SQL 쿼리를 작성할 때
PreparedStatements를 사용하여 사용자 입력과 쿼리 구조를 분리한다.
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ORM 도구를 활용하여 쿼리 생성을 추상화하고 보안을 강화한다.
@Query("SELECT u from USER u WHERE u.name = ?1") // 안전
@Query("SELECT * FROM users WHERE name = " + name) // 취약
사용자의 입력을 철저하게 검증하여 예상치 못한 SQL 구문이 포함되지 않도록 한다.
데이터베이스 사용자에게 최소한의 권한만 부여하여, 침해 시 피해를 최소화한다.
데이터베이스 에러 메시지를 사용자에게 노출하지 않도록 설정하여 내부 구조가 드러나지 않게 한다.
server.error.include-stacktrace=never 설정하기