Skip to content
isdnetworks
Go back

세션을 DB에 넣었다

서버를 한 대 더 붙이고 나니 로그인이 자꾸 풀렸다.

Table of contents

Open Table of contents

세션이 각 서버의 파일에 있었다

앞단에서 요청을 두 대에 번갈아 보내는데 세션은 각 서버의 session.save_path 아래 파일로 저장되고 있었다. 한 대에서 로그인하면 그 서버에만 파일이 생기고 다음 요청이 다른 대로 가면 로그인이 안 된 상태가 된다.

한 대일 때는 문제가 없던 구조였다. 파일이 그 서버 안에만 있다는 사실이 대수롭지 않았기 때문이다. 대가 늘면서 그 전제가 깨졌다.

붙여 두는 방법과 공유하는 방법

앞단에서 같은 사람을 같은 서버로 계속 보내는 설정이 있었다. 그렇게 하면 세션이 맞는다. 그런데 한 서버가 죽으면 거기 붙어 있던 사람이 전부 로그아웃된다. 한 대가 죽어도 서비스가 이어지게 하려고 대를 늘렸는데 그 이유가 살지 않는다.

세션을 양쪽이 함께 보는 자리에 두기로 하고 sessions 표에 넣었다.

CREATE TABLE sessions (
  id VARCHAR(40) PRIMARY KEY,
  ip_address VARCHAR(45),
  timestamp INT,
  data TEXT
);

CodeIgniter 에 이미 그 기능이 있어서 sess_use_database 한 줄만 켰고 코드는 안 고쳤다. 서버가 둘이어도 로그인이 유지되고 한 대가 죽어도 다른 쪽에서 이어졌다.

DB가 바빠졌다

며칠 뒤 DB 부하가 늘었다. 요청 하나가 SELECTUPDATE 를 한 번씩 하므로 모든 요청이 DB 를 친다.

조회보다 갱신이 컸다. last_activity 를 매 요청마다 쓰는데 읽기는 여럿이 동시에 되지만 쓰기는 행 잠금이 걸린다.

설정을 보니 sess_time_to_update 가 있었다. 기본값이 300 초라 원래도 5분에 한 번만 쓰게 돼 있었는데 우리 쪽에서 그것을 짧게 바꿔 둔 상태였다. 기본값으로 되돌리니 쓰기가 크게 줄었다.

만료된 세션 행이 쌓이는 것도 있었다. COUNT(*) 로 세어 보니 수십만 건이었다.

프레임워크의 정리는 확률로 돈다. gc_probability 가 5 라 요청 100 번 중 5 번쯤 그 자리에서 DELETE 가 도는 구조다. 평소에는 안 보이다가 몰릴 때 응답이 늘어졌다. 그래서 crontab 에 건 배치로 뺐다.

DELETE FROM sessions WHERE timestamp < ? LIMIT 1000;

한 번에 다 지우려니 오래 걸리고 그동안 InnoDB 가 그만큼의 행을 잠갔다. LIMIT 1000 으로 나눠 반복하게 하니 사이에 다른 요청이 들어갈 틈이 생겼다.

미루면서 조건을 적어 뒀다

DB 말고 memcached 에 두는 방법도 있었다. 그쪽이 빠른데 서버를 하나 더 띄워 관리해야 한다. 이 시점에는 그 부담이 커서 DB 로 버티기로 했다.

대신 언제 바꿀지를 적어 뒀다. sessions 쿼리가 전체에서 큰 비중이 되거나 sess_time_to_update 를 늘려도 부하가 안 잡히면 그때 옮긴다.

안 하는 것과 미루는 것은 다르다는 것이 여기서 정리됐다. 조건을 적어 두면 다시 볼 시점이 정해지고 적어 두지 않으면 그냥 잊는다.

정리


Share this post on:

Previous Post
한 테이블에 여러 주체 자료가 섞여 있었다
Next Post
고쳤는데 화면이 안 바뀌었다