백만명의 Employee가 존재하는 테이블을 생성해보자. (sql의 길이가 길어서 sql문을 제공해줄 수 없기 때문에, 아래의 Dummy Data 생성 방법에 대한 설명을 읽고 직접 만들어보기 바란다.)
① million_insert_quries.sql이라는 이름으로 sql문을 저장한다.
② cmd에서 sql파일이 존재하는 디렉토리로 이동한 후 mysql을 실행한다. 아래의 명령을 입력해 백만 개의 데이터가 존재하는 Employee 테이블을 생성한다.
cd Desktop/db/Database
mysql -u root -p
source ./million_insert_quries.sql
③ DB에 데이터가 잘 들어갔는지 확인해보자.
select count(*) from Employee;
④ 쿼리 실행에 앞서 explain 명령을 사용하여 Query Execution Plan을 확인해보자.
explain select count(Fname) from Employee where Lname="Lee";
⑤ 이제 쿼리를 실행하여 인덱스가 없는 상태에서의 실행 시간을 확인해보자.
select count(Fname) from Employee where Lname="Lee";
select count(Fname) from Employee where Lname="Kim";
select count(Fname) from Employee where Lname="Park";
select count(Fname) from Employee where Lname="Brown";
⑥ 탐색 시간을 단축하기 위해선 인덱스를 적용해야 한다. 아래의 명령을 입력하여 Index를 적용해보자.
create index employee_on_Lname_Sex_Dno
on Employee(Lname, Sex, Dno) using BTree;
⑦ 만약 Index를 제거하고 싶다면 아래의 명령을 입력하면 된다.
drop index employee_on_Lname_Sex_Dno on Employee;
⑧ 적용된 Index를 확인해보고 싶으면 아래의 명령을 입력하면 된다.
show index from Employee;
⑨ 이제 Index가 적용된 상태에서의 Query Execution Plan을 확인해보자.
explain select count(Fname) from Employee where Lname="Lee";
⑩ 다시 위 쿼리를 실행하여 실행시간을 비교해보자.
select count(Fname) from Employee where Lname="Lee";
select count(Fname) from Employee where Lname="Kim";
select count(Fname) from Employee where Lname="Park";
select count(Fname) from Employee where Lname="Brown";
이와 같이 Index를 적용하면, 보다 효율적으로 레코드를 탐색할 수 있다.
① db 폴더 > Database > week12 폴더를 생성한다.
② cmd에서 week12 디렉토리로 이동한 후 mysql을 열어 아래의 명령을 입력한다.
create database week12;
use week12;
③ VSCode에서 week12 폴더를 연다.
④ 터미널을 열고, 아래의 명령을 입력하여 Faker 모듈과 pymysql 모듈을 설치한다.
python -m pip install Faker
python -m pip install pymysql
⑤ week12.py라는 이름으로 파일을 생성한 후 아래의 내용을 입력한다.
from faker import Faker
import pymysql
fake = Faker()
conn = pymysql.connect(
host='localhost',
user='root',
port=3306,
password='{비밀번호}',
database='week5'
)
cursor = conn.cursor()
max_id = 10 # 기존에 존재하는 id
for _ in range(200000):
max_id += 1
name = fake.name()
email = fake.email()
phone_number = fake.phone_number()
department_id = fake.random_int(min=1, max=5)
# 학생 데이터 삽입
cursor.execute('''
INSERT IGNORE INTO student (id, name, email, phone_number, department_id)
VALUES (%s, %s, %s, %s, %s)
''', (max_id, name, email, phone_number, department_id))
# 변경사항 커밋
conn.commit()
# 연결 종료
conn.close()
⑥ 위 파일을 실행시킨다. cmd에 아래의 명령을 입력하여 DB에 데이터가 잘 입력되었는지 확인하자.
select count(*) from student;
⑦ 정확히 10만개의 데이터가 요구되는 상황에서는 아래의 코드를 사용하면 된다.
from faker import Faker
import pymysql
fake = Faker()
conn = pymysql.connect(
host='localhost',
user='root',
port=3306,
password='{비밀번호}',
database='week5'
)
cursor = conn.cursor()
max_id = 10 # 시작 id를 10으로 초기화
inserted_count = 0 # 삽입된 데이터 수를 카운트하는 변수 추가
while inserted_count < 100000: # 10만개의 데이터가 삽입될 때까지 반복
max_id += 1
name = fake.name()
email = fake.email()
phone_number = fake.phone_number()
department_id = fake.random_int(min=1, max=5)
# 생성된 이메일이 이미 데이터베이스에 존재하는지 확인
cursor.execute('SELECT COUNT(*) FROM student WHERE email = %s', (email,))
existing_count = cursor.fetchone()[0]
if existing_count == 0:
# 학생 데이터 삽입
cursor.execute('''
INSERT INTO student (id, name, email, phone_number, department_id)
VALUES (%s, %s, %s, %s, %s)
''', (max_id, name, email, phone_number, department_id))
inserted_count += 1 # 데이터 삽입 성공 시에만 카운트 증가
# 변경사항 커밋
conn.commit()
# 연결 종료
conn.close()
Index를 사용하는 것이 항상 좋은 것은 아니다. 그렇다면 Index는 언제 사용해야 하는 것이며, 어떻게 해야 효율을 최대화할 수 있을까? 11주차 이론에서 이미 다룬 내용이지만, 중요한 내용이므로 한번 더 다루도록 하겠다.
Index의 성능은 Show Index의 Cardinality와 Explain의 Rows, Filtered로 평가될 수 있다.
Index의 Cardinality란, 해당 Index가 가리키는 Column의 고유한 값들의 개수를 의미한다. Cardinality의 값이 커질수록 인덱스의 사용 효율은 높아지는데, 이는 인덱스가 다양한 값을 가지고 있을수록 원하는 값을 빠르게 찾을 수 있기 때문이다. primary key와 unique key에 대해서는 자동으로 Index가 생성되는 이유가 바로 이것이다. 기본 키와 Unique Key는 모두 높은 카디널리티를 갖기 때문에 자동으로 Index를 적용해주는 것이다. (참고로, foreign key를 인덱싱하는 이유는 조인 성능 향상시키기 위함이다.)
또한, Explain에서 Rows는 쿼리에서 검색해야 할 것으로 예측되는 행의 수를 의미하고, Filtered는 Where이나 Join 등에서 얼마나 많은 행이 실제로 선택되었는지를 % 단위로 나타낸 것이다. 즉, Filtered 값이 100에 가까울수록 조건에 부합하는 행의 비율이 높다(Selectivity가 낮다). 따라서 Rows가 매우 큰 값일 때 Filtered 값이 100에 가깝다면, 해당 컬럼에는 인덱스를 적용하지 않는 편이 좋은 것이다.
① cmd에 아래의 명령을 입력해, student Table에 존재하는 Index의 목록을 확인할 수 있다.
show index from student;
② 이번에는 Explain의 Rows, Filtered를 분석해보자.
explain select count(name) from student where name="Danny Tucker";
③ 인덱스가 없는 상태에서 쿼리의 실행시간을 확인해보자.
select count(name) from student where name="Danny Tucker";
④ 이제 Index를 적용한다.
create index student_on_name_email_department_id on Student (name, email, department_id) using BTree;
⑤ Index가 잘 적용되었는지 확인해보자.
show index from student;
⑥ 이번에는 Explain 명령을 이용해 Query Execution Plan을 확인해보자.
explain select count(name) from student where name="Danny Tucker";
⑦ Index 적용 후의 쿼리 실행 시간을 확인해보자.
select count(name) from student where name="Danny Tucker";
아래의 5개의 Index를 추가로 적용해보고 적용 전과 성능을 비교해보자.
① department_name 인덱스
create index department_name on Department (name) using BTree;
② student_on_email_name 인덱스
create index student_on_email_name on Student (email, name) using BTree;
③ student_on_name_phone_number 인덱스
create index student_on_name_phone_number on Student (name, phone_number) using BTree;
④ student_on_name_email_phone_number 인덱스
create index student_on_name_email_phone_number on Student (name, email, phone_number) using BTree;
⑤ student_on_name 인덱스
create index student_on_name on Student (name) using BTree;
지금까지 적용한 총 5개의 인덱스를 확인해보자.
show index from student;
show index from department;
① department_name 인덱스를 이용한 쿼리
select count(*) from Student s, Department d where s.department_id=d.id and d.name='BusinessAdministration';
Index 적용 전
Index 적용 후
② student_on_email_name 인덱스를 이용한 쿼리
select count(*) from student where email like'%jo%' and name like '%an%';
Index 적용 전
Index 적용 후
③ student_on_name_phone_number 인덱스를 이용한 쿼리
select count(*) from student where name='hyunseop' and phone_number='010-1111-1234';
④ student_on_name_email_phone_number 인덱스를 이용한 쿼리
select count(*) from student where name='hyunseop' and email='hyunseop123@naver.com';
Index 적용 전
Index 적용 후
⑤ student_on_name 인덱스를 이용한 쿼리
select count(*) from student where name='Michael Smith';
지금까지 살펴본 내용을 토대로 Index의 성능을 평가하면 아래와 같다.
① Unique Key 또는 Primary Key에 해당하는 Column을 이용하는 Index는 별도로 등록할 필요가 없다.
② Index는 Like 연산 성능에는 전혀 도움이 되지 않는다.
③ Unique 하지는 않지만 Cardinality가 높고, 조건문에 자주 사용되는 Column이라면, Index를 별도로 등록하는 것이 좋다.