목록 화면이 느렸다. 쿼리는 20건만 가져오는데도 느렸다.
Table of contents
Open Table of contents
무엇을 가져오는지 봤다
iBATIS sqlmap 을 열어 보니 SELECT * 였다. DESC 로 보니 본문과 첨부 정보와 미리 그려 둔 HTML 이 같은 행에 들어 있었다.
no int
title varchar(200)
content text ← 본문
attach_meta text ← 첨부 정보
html_cache mediumtext ← 그려 둔 HTML
목록에서는 제목과 날짜만 쓴다. 한 건에 본문이 수만 자면 20건에 수십만 자를 가져온다.
SELECT no, title, reg_date 로 바꾸니 목록이 빨라졌다. SELECT * 는 편한데 컬럼이 늘면 그때부터 느려진다. 지금 당장은 안 드러나는 종류의 문제였다.
큰 것을 따로 뒀다
InnoDB 는 TEXT 의 앞 768바이트를 클러스터드 인덱스 행에 두고 나머지를 오버플로 페이지로 뺀다. 큰 컬럼이 많으면 B-tree 노드에 데이터가 차서 한 페이지에 담기는 행 수가 줄어든다. 그래서 html_cache 를 다른 테이블로 뺐다.
CREATE TABLE board_content (
board_no int NOT NULL,
content text,
html_cache mediumtext,
PRIMARY KEY (board_no)
);
상세 화면에서만 JOIN 하고 목록과 검색은 board 만 본다.
검색이 특히 심했다. LIKE '%키워드%' 로 본문까지 뒤지는데 앞에 % 가 붙으면 인덱스를 못 쓰고 전부 읽는다. 게다가 본문이 크니 읽는 양도 크다. 제목만 먼저 찾게 하고 본문 검색은 조건을 좁힌 뒤에 하게 나눴다. 뒤에만 % 가 붙으면 인덱스를 쓴다.
얼마나 큰지 재 봤다
짐작하지 말고 MySQL 에서 AVG(LENGTH(content)) 와 MAX(LENGTH(content)) 를 같이 뽑았다.
avg_len 4,218
max_len 1,842,003
평균은 4천 자인데 최대가 180만 자다. 긴 순으로 뽑아 보니 이미지를 본문에 통째로 넣은 글이었다. 그 한 건 때문에 목록 전체가 느려질 수 있다.
평균만 봤으면 문제가 없어 보였을 것이다. 최대를 같이 봐야 이런 것이 드러난다.
넣을 때와 자를 때
읽는 쪽만 고쳐서는 계속 감당해야 한다. 넣을 때 본문 길이에 상한을 두고 이미지는 본문에 넣지 말고 파일로 올리게 했다.
화면 쪽도 고칠 것이 있었다. 목록에서 본문 앞 50자를 미리 보여 주고 있었는데, 앞부분만 쓰면서 전부 가져오고 있었다. 자르는 것을 쿼리로 옮기니 가져오는 양 자체가 준다. 화면에서 자르는 것은 이미 가져온 뒤라 늦다.
정리
- 전체 컬럼을 가져오면 안 쓰는 큰 컬럼까지 딸려 온다
- 지금은 안 드러나고 컬럼이 늘면 그때부터 느려진다
- 큰 컬럼은 다른 테이블로 빼고 상세에서만 조인한다
- 앞에 와일드카드가 있는 검색은 인덱스를 못 쓰고 전부 읽는다
- 제목 검색과 본문 검색을 나누고 본문은 조건을 좁힌 뒤에 한다
- 짐작하지 말고 길이를 잰다. 평균과 최대를 같이 봐야 한다
- 한 건이 유독 크면 그것 하나로 전체가 느려진다
- 넣을 때 상한을 둔다. 안 막으면 읽는 쪽이 계속 감당한다
- 자르는 것은 쿼리에서 한다. 화면에서 자르면 이미 가져온 뒤다