날짜 |
작성/수정 |
내용 |
2012-06-12 |
김광훈 |
주제 생성 |
Summary
- 이 문서는 predis 를 사용 복수개의 명령을 순차적으로 실행하는 경우와 Class Predis\Pipeline\PipelineContext 를 이용한 경우의 실행 속도에 대한 비교 테스트 이다.
- 접속은 "단일 redis 서버 접속" 인 경우와 "3대의 redis 서버로 이루어진 cluster" 에 접속한 경우를 각각 측정
- 테스트 항목은 접속 방법 별로 아래 3가지를 각각 5회씩 수행.
- 단일 key 업데이트 테스트 : 단일 key 에 대하여 10000회의 incrby 실행
- 복수 key 에 set 테스트 : 10000개의 새로운 key 에 set 하기
- 복수 key 업데이트 테스트 : 3개의 key 의 값들을 구한후 변경하여 다시 set 하기 를 10000회 반복
- 각 테스트 시작 전에는 flushdb 를 실행하여 db 를 비웠다.
- 시간 측정은 php 의 microtime() 를 사용하였고 redis 서버 접속 직후 부터 시간을 측정했다.
pipeline 프로세스
- predis 에서 지원하는 pipeline 은 다음과 같이 구현된다.
- 클러스터를 이루는 redis 서버들 중에서 해당하는 key 들에 필요한 서버들에 모두 접속을 한다.( class Predis\Network\ConnectionBase 의 함수인 tcpStreamInitializer() 에서 실제 접속 처리를 한다. )
- 모든 명령들을 php 의 배열 변수에 저장한다. ( class Predis\Pipeline\PipelineContext 의 멤버변수인 $pipeline 에 저장됨)
- class Predis\Pipeline\PipelineContext 의 함수 flushPipeline 에 의해 redis 명령 실행기에 클러스터 접속 정보와 배열 $pipeline 을 전달한다.
- 여기서 명령 실행기는 class Predis\Pipeline\StandardExecutor 의 객체가 된다. 이 객체의 함수 execute 에 의해 배열의 명령들이 실행되게 된다.
- 위에서 살펴본 실행 구조상, 배열에 있는 명령들 실행중에 웹서버가 죽게되면 그 전까지의 명령들만 실행이 되고, 나머지 명령들은 실행이 되지 않게 된다.
단일 key 업데이트 테스트
// pipeline 사용
$pipe = $m->predis->pipeline();
for($i=0;$i<10000;$i++) {
$pipe->incrby('counter', 10);
}
$pipe->execute();
// 순차적 실행
for($i=0;$i<10000;$i++) {
$m->predis->incrby('counter', 10);
}
회차 |
1 |
2 |
3 |
4 |
5 |
pipeline 사용(단독접속) |
0.6191 |
0.6413 |
0.8552 |
0.8497 |
0.6428 |
순차적 실행(단독접속) |
4.7250 |
4.6734 |
4.7344 |
4.6071 |
4.6769 |
pipeline 사용(멀티접속) |
0.7360 |
0.6427 |
0.7284 |
0.7145 |
0.6962 |
순차적 실행(멀티접속) |
4.8469 |
4.9195 |
4.8138 |
4.8543 |
4.8286 |
복수 key 에 set 테스트
// pipeline 사용
$pipe = $m->predis->pipeline();
for($i=0;$i<10000;$i++) {
$pipe->set("testkey:" . $i, $i);
}
$pipe->execute();
// 순차적 실행
for($i=0;$i<10000;$i++) {
$m->predis->set("testkey:" . $i, $i);
}
회차 |
1 |
2 |
3 |
4 |
5 |
pipeline 사용(단독접속) |
0.5373 |
0.4848 |
0.4698 |
0.4656 |
0.4815 |
순차적 실행(단독접속) |
4.7094 |
4.7027 |
4.6585 |
5.2510 |
4.7227 |
pipeline 사용(멀티접속) |
1.6480 |
1.9329 |
1.8257 |
1.8132 |
1.8297 |
순차적 실행(멀티접속) |
4.9077 |
5.0084 |
4.9053 |
4.8194 |
4.9204 |
복수 key 업데이트 테스트
// pipeline 사용
$m->predis->set("testkey:0", 10);
$m->predis->set("testkey:1", 10);
$m->predis->set("testkey:2", 10);
for($i=0;$i<10000;$i++) {
$pipe = $m->predis->pipeline();
$pipe->get("testkey:0");
$pipe->get("testkey:1");
$pipe->get("testkey:2");
$replies = $pipe->execute();
$testkey0 = $replies[0] + 5;
$testkey1 = $replies[1] + 5;
$testkey2 = $replies[2] + 5;
$pipe->set("testkey:0", $testkey0);
$pipe->set("testkey:1", $testkey1);
$pipe->set("testkey:2", $testkey2);
$pipe->execute();
}
// 순차적 실행
$m->predis->set("testkey:0", 10);
$m->predis->set("testkey:1", 10);
$m->predis->set("testkey:2", 10);
for($i=0;$i<10000;$i++) {
$testkey0 = $m->predis->get("testkey:0");
$testkey1 = $m->predis->get("testkey:1");
$testkey2 = $m->predis->get("testkey:2");
$testkey0 += 5;
$testkey1 += 5;
$testkey2 += 5;
$m->predis->set("testkey:0", $testkey0);
$m->predis->set("testkey:1", $testkey1);
$m->predis->set("testkey:2", $testkey2);
}
회차 |
1 |
2 |
3 |
4 |
5 |
pipeline 사용(단독접속) |
14.0392 |
15.1118 |
15.2008 |
14.6502 |
14.8247 |
순차적 실행(단독접속) |
28.8113 |
29.9251 |
28.4175 |
28.7168 |
28.8193 |
pipeline 사용(멀티접속) |
19.4469 |
19.8281 |
19.5141 |
19.1568 |
18.7124 |
순차적 실행(멀티접속) |
29.7012 |
29.5887 |
29.4990 |
29.7207 |
29.7363 |
정리
- predis 에서 제공하는 pipeline 은 다음과 같이 이루어진다.
- php 단에서 배열변수에 redis 명령들을 저장한다.
- redis 명령들의 저장이 끝나면, 소켓을 통해 명령을 순서대로 서버에 보내 실행한다.
- redis 명령들의 실행이 끝나면, 소켓을 통해 결과를 순서대로 읽어온다.
- 즉, 명령이 모이는 곳은 웹서버(PHP) 단 이므로 배열에 저장된 명령들이 실행되는 중간에 웹서버가 crash 되버리거나 웹서버와 redis 서버와의 접속이 끊어지면 아직 실행되지 않은 명령들은 유실되어 버린다.
- 복수개의 redis 명령들을 pipeline 을 사용하여 실행하는 방법의 장단점은 (절차적으로 실행하는 경우에 비교할 때) 다음과 같다.
- 장점 : 명령실행 속도가 조금이라도 빠르며, 특히 명령이 아주 많은 경우는 압도적으로 빨라진다. 이에 서비스 용으로 보다는 DB 관리등의 목적으로 쓰는것이 더 유용하다.
- 단점 : 결과를 나중에 한꺼번에 받아오기 때문에 중간에 실행된 redis 명령의 결과를 이용하여 redis 명령을 실행하는 것은 불가능하다. 명령의 결과를 이용하기 위해서는 일단 pipeline 의 버퍼에 쌓인 명령들을 다 처리한 후 다시 pipeline 을 사용하던가 해야 한다.
- 약간 억지로 효율 계산
- 홍콩 웹서버 5대에서 12/Jun/2012 하루 처리한 명령의 수가 대략 1,570,000 건
- redis 멀티서버 접속으로 "복수 key 업데이트 테스트" 의 효율을 적용하면 API 명령 10,000 건당 10초의 시간을 아낄수 있음
- API명령 10,000 건 : 10 sec = 1,570,000 : ? 하면 대충 1,570 sec 즉 26분 정도의 시간이 나옴
- 파이프라인 사용시 (절자척 명령 실행에 비해) 일일 웹서버들 의 시간을 26분 정도 벌어줄수 있다는 계산이 나옴(조금 애매한 수치)