목록 조회가 느렸다. 접속하는 사람마다 같은 쿼리가 도는데 내용은 자주 안 바뀌면서 매번 DB를 친다.
Table of contents
Open Table of contents
캐시를 붙였더니 옛것이 나왔다
memcached 를 앞에 뒀다. 요청이 오면 get() 으로 보고 있으면 그것을 주고 없으면 MySQL 을 조회해서 넣고 준다. 두 번째 요청부터는 MySQL 을 안 치니 효과가 바로 보였다.
며칠 뒤에 문제가 생겼다. 글을 수정했는데 목록에 옛 제목이 나오고 새로고침해도 그대로다. 생각해 보면 당연했다. MySQL 은 바뀌었는데 memcached 에 넣어 둔 것을 안 지웠으니 옛 내용을 계속 돌려준다.
시간 만료와 변경 시 삭제
지우는 방법이 두 가지였다. 넣을 때 TTL 을 줘서 시간이 지나면 사라지게 하거나, 글이 바뀔 때 관련 키를 delete() 로 지우는 것이다. 앞쪽은 간단한 대신 그 시간 동안은 옛것이 나오고, 뒤쪽은 정확한 대신 어디를 지울지 알아야 한다.
먼저 유효 기간 5분으로 뒀다. 그런데 운영 쪽에서 수정했는데 왜 안 바뀌느냐는 말이 나왔다. 5분이 생각보다 길었다. 30초로 줄이니 불만은 줄었는데 캐시 효과도 같이 줄었다. TTL 을 짧게 할수록 신선하지만 MySQL 부하는 덜 줄고, 길게 할수록 반대다. 둘이 서로 반대로 움직여서 시간만으로는 어느 쪽도 만족스럽지 않았다.
어디를 지울지가 문제였다
변경 시 지우는 쪽으로 바꿨다. 글을 수정한 뒤에 그 게시판 목록 캐시를 지우면 즉시 반영된다.
// 글 수정 후
$cache->delete('list_' . $boardId);
그런데 같은 글이 여러 목록에 나온다는 게 문제였다. 게시판 목록과 최신글 목록과 인기글 목록과 검색 결과에 전부 걸려 있다. 처음에 게시판 목록만 지웠더니 최신글 목록에 옛 제목이 남았다.
그래서 관련 키를 한 곳에 모아 두고 수정할 때 그 목록을 돌면서 지우게 했다. 다만 이게 완전하지는 않다. 새 목록 화면을 만들면 여기에도 추가해야 하고, 잊으면 또 옛것이 남는다.
둘을 섞고 키 이름 규칙을 정했다
결국 둘을 같이 썼다. 아는 것은 수정할 때 지우고, 놓친 것은 유효 기간으로 언젠가 사라지게 두는 것이다. 주 수단이 지우기가 되니 유효 기간은 길게 둬도 됐다. 지우기 없이 기간만 쓸 때는 짧아야 했는데 이제 안전망 역할만 남는다.
작업하면서 알게 된 것은 키 이름을 잘 지어야 지울 수 있다는 것이다. cache_1 같은 이름은 무엇인지 알 수 없어서 지울 대상을 고를 수 없다. 종류와 대상과 조건을 이어 붙이는 규칙을 정하니 어떤 키가 있을지 짐작이 됐다.
마지막으로 걸린 것은 빈 결과였다. 조회 결과가 없을 때 빈 배열을 넣으면 다음 요청에서 그것이 빈 값인지 캐시에 없는 것인지 구분이 안 된다. get() 이 둘 다 거짓을 돌려주기 때문이다. 없다고 판단해서 매번 MySQL 을 치게 되고 결과가 없는 조회일수록 캐시가 안 먹었다.
있는지 보는 함수가 따로 있을 줄 알았는데 Memcached 에는 없었다. 대신 getResultCode() 가 직전 결과를 알려 준다. 키가 없으면 RES_NOTFOUND 이고 빈 값이 담겨 있으면 RES_SUCCESS 다.
$v = $cache->get($key);
if ($v !== false || $cache->getResultCode() === Memcached::RES_SUCCESS) {
return $v;
}
정리
- 같은 쿼리가 반복되면 앞에 캐시를 둔다. 두 번째 요청부터 DB를 안 친다
- 넣기만 하고 안 지우면 DB만 바뀌고 캐시는 옛 내용을 계속 돌려준다
- 지우는 방법이
TTL만료와 변경 시delete()둘이다 - 시간만 쓰면 신선도와 캐시 효과가 서로 반대로 움직인다
- 변경 시 삭제는 정확한 대신 어디를 지울지 알아야 한다
- 같은 글이 여러 목록에 걸려 있어 하나를 놓치기 쉽다
- 둘을 같이 쓰면 놓친 것도 유효 기간으로 언젠가 지워진다
- 키 이름에 규칙이 있어야 지울 대상을 고를 수 있다
Memcached에는 있는지 보는 함수가 없고getResultCode()로 가른다- 빈 결과와 없음을 구분하지 않으면 그 조회에는 캐시가 안 먹는다