현업이 종종 특정 데이터를 엑셀로 뽑아달라는 요청을 합니다.
iBATIS 는 기본적으로 해당 쿼리에 대한 메타데이터(필드, 타입 등)를 캐시한다고 합니다.
Alto ventos est coeptis utque fecit. Phoebe sine circumfuso arce. Tanto aliis. Matutinis cornua origo formaeque animal mundo. Chaos: fabricator. Natura mundo caesa addidit. Cuncta habendum meis omni ille formaeque emicuit septemque et. Lege fecit aethere porrexerat gentes horrifer formas.
예전에 한번 Maria DB에서 테스트로 사용했었던 핸들러 소켓
어느 멋진 DBA에 의해 아주 잠깐? 경험해보았다…
소켓으로 DB와 연결하고 고속의 입출력 기능을 지원해주는 뭐 그런 내용이었다…
물론 인메모리 정도까지는 아니더라도, RDB에 이런 기능이 있다는게 놀라웠다..오오 !
역시 마리아~~~~
나중에 기회가 된다면 써보시길, RDB 고속 입출력 handlersocket
https://mariadb.com/kb/en/mariadb/handlersocket-installation/
HandlerSocket gives you direct access to InnoDB/XtraDB and SPIDER. It was included in MariaDB 5.3 as a ready-to use plugin.
HandlerSocket is a NoSQL plugin for MySQL/MariaDB. It works as a daemon inside the mysqld process, accepting TCP connections, and executing requests from clients. HandlerSocket does not support SQL queries. Instead, it supports simple CRUD operations on tables.
HandlerSocket can be much faster than mysqld/libmysql in some cases because it has lower CPU, disk, and network overhead:
파일 업로드 방식에 많이 쓰이는 MultipartFile 에 이미지 사이즈 크기를 알아보자
의외로 간단하다..
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
MultipartFile multipartFile = multipartHttpServletRequest.getFile("recruiter.logo.file");
BufferedImage image = ImageIO.read(multipartFile.getInputStream());
Integer width = image.getWidth();
Integer height = image.getHeight();
spring에서 제공하는 RedisTemplate을 함께 사용하는 방법이 있다.
RedisTemplate을 사용하면 redis client 라이브러리의 종류에 상관없이 사용할 수 있어 좋고,
redis가 지원하는 자료구조를 사용하기 좋게 랩핑을 해놓아 좋다.
jedis를 그냥 써도 좋지만, byte[] 타입으로 컨버팅 노가다를 해야 한다.
어려운 길을 가지말고 RedisTemplate을 사용하기로 해보자.
|
1
2
3
4
5
6
7
8
9
10
11
12
|
<!-- jedis --><dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.2.1</version> </dependency><!-- spring data redis --><dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.2.0.RELEASE</version></dependency> |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<?xml version="1.0" encoding="UTF-8"?> xsi:schemaLocation="http://www.springframework.org/schema/beans > <!-- Root Context: defines shared resources visible to all other web components --> <mvc:annotation-driven conversion-service="conversionService" /> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true" p:host-name="172.20.44.209" p:port="6379" /> <!-- redis template definition --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory" /></beans> |
1. 가장 쉽게 사용해 보기
사용할 타입을 RedisTemplate과 자료구조에 지정하여 사용
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/** * Handles requests for the application home page. */@Controller@RequestMapping(value="/home")public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); @Autowired RedisTemplate<String, String> redisTemplate; @Resource(name="redisTemplate") private ValueOperations<String, String> valueOps; @RequestMapping(value="/jedis2") public String jedis2(){ valueOps.set("foo", "bar"); logger.info("Welcome jedis user {}.",valueOps.get("foo")); return "home"; }} |
2. Custom class 이용해 보기
Serializable 를 구현한 User class를 넣어보자
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public class User implements Serializable { @NotNull String name; @Min(0) @Max(100) int id; Level level; public void setLevel(Level _level) { level = _level; } public Level getLevel() { return level; } public void setId(int _id) { id = _id; } public int getId() { return id; } public void setName(String _name) { name = _name; } public String getName() { return name; }} |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
/** * Handles requests for the application home page. */@Controller@RequestMapping(value="/home")public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); @Autowired RedisTemplate<String, User> redisTemplate; @Resource(name="redisTemplate") private ValueOperations<String, User> valueOps; @RequestMapping(value="/jedis2") public String jedis2(){ User user = new User(); user.setId(4); user.setName("warwick"); user.setLevel(Level.GOLD); valueOps.set("foo", user); return "home"; } @RequestMapping(value="/jedis3") public String jedis3(){ User savedUser = valueOps.get("foo"); logger.info("Welcome jedis user " + savedUser.id + " name : " + savedUser.name + " level : " + savedUser.level); return "home"; }} |
결과
INFO : com.yakolla.mvctest.HomeController – Welcome jedis user 4 name : warwick level : GOLD
3. valueOperations 없이 사용해보기
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); @Autowired RedisTemplate<String, Object> redisTemplate; @RequestMapping(value="/jedis2") public String jedis2(){ User user = new User(); user.setId(6); user.setName("warwick2"); user.setLevel(Level.SILVER); redisTemplate.opsForValue().set("foo", user); return "home"; } @RequestMapping(value="/jedis3") public String jedis3(){ User savedUser = (User)redisTemplate.opsForValue().get("foo"); logger.info("Welcome jedis user " + savedUser.id + " name : " + savedUser.name + " level : " + savedUser.level); return "home"; }} |
결과
INFO : com.yakolla.mvctest.HomeController – Welcome jedis user 6 name : warwick2 level : SILVER
set
Key와 Value를 입력한다.
set key “value”
mset
여러개의 Key와 Value를 한번에 입력한다.
set key1 “value1” key2 “value2”
setex
Key와 Value, Expires(sec) 설정을 입력한다.
(입력된 시간이후에 소멸한다.)
setex key “value” 3
get
Key에 해당하는 Value를 가져온다.
mget
여러개의 Key값을 입력하여 Value를 동시에 리턴받는다.
mget key1 key2 key3
incr
integer 데이터에 한해서 숫자를 1씩 증가시킨다.
rpush
List에 오른쪽으로 데이터를 추가시킨다.
rpush Key “value”
lrange
List의 지정한 범위 내의 값을 리턴한다.
lrange Key 0 1(-1은 무한)
lindex
선택된 인덱스의 값을 리턴한다.
lindex Key 1
llen
선택된 List의 길이를 리턴한다.
llen Key
save
현재 데이터를 모두 저장한다.(서버 종료시에 save시점으로 부터 데이터 복구)
del
선택된 데이터를 제거 한다.
del Key
Keys *
현재 DB의 전체 키 리스트를 리턴해준다.
keys *
Redis는 기본적으로 binary 파일, dump.rdb에 dataset의 snapshot을 저장한다.dataset에 최소 M번 변경된 경우에 매 N초마다 dataset을 저장할수 있게 Redis를 조절할 수 있다. 또는 당신이 SAVE, BGSAVE 명령어를 이용하여 수동으로 조절할수있다.
key값들이 최소 1000번 변경된 경우에 60초마다 Redis가 자동으로 dataset을 disk에
백업하는 예는 다음과 같다.
save 60 1000
이 전략은 아시다시피 snapshotting. 라고 한다.
다음과 상황이 발생하면 Redis는 dataset을 디스크에 백업을 하게된다.
이 기능은 copy-on-write에서 얻을수있는 이점을 가져왔다.(copy-on-write라는거…처음알았다…)
Snapshotting은 내구성이 대단히 좋지는 않다. 만약에 당신의 컴퓨터가 Redis 중단을 실행하던가, 전원이 나갔다거나, 당신이 실수로 kill -9를 실행했을때 마지막 data는 잃어버릴것이다. 이것은 다른 어플리케이션에서는 큰 문제는 되지 않지만, Redis는 완전한 내구성에 영향을 미치고, Redis가 죽을수도 있다.
그래서 Redis가 완전한 내구성의 대안으로 나온것이 append-only file이다. 버젼 1.1부터 사용할수 있게 되었다.
당신은 설정파일에서 AOF옵션을 활성화 시킬수 있다.
appendonly yes
설정 이후부터는 매시간 Redis는 명령어를 받아 dataset이 변경될때마다(e.g. SET) AOF에 추가 될것이다.
Redis를 재시작하면, AOF을 이용하여 상태를 재구성 할것이다.
당신이 추측 할수있듯이, AOF는 쓰기연산을 수행할때마다 계속 커진다. 만약 당신이 100번 카운터를 증가했다면, 끝날때는 dataset에는 single key와 최종값이 저장되어있겠지만, AOF에는 100개의 정보가 있다.
99개의 정보는 현재 상태에서는 필요하지 않는것인데도 말이다.
그래서 Redis는 재밌는 기능을 지원한다: 클라이언트에게 인터럽트 요청없이 백그라운드에서 AOF를 재조정 할수있다. 언제든지 BGREWRITEAOF 사용하게되면 Redis는 현재 메모리에 dataset를 재구성하기위해 작은 단위로 명령어를 순서대로 쓰기 시작할 것이다.
당신이 AOF를 사용한다면, 때론 BGREWRITEAOF 실행하는게 필요할것이다.
Redis가 disk에 있는 data와 fsync를 몇번할지 정할수 있다. 여기 옵션이 있다.
(지금옵션보니까 appendfsync [no, everysec, always]로 되어있다.)
fsync every time, 새 명령어를 AOF에 추가한다. 엄청 느리지만, 매우 안전하다.fsync every second, 충분히 빠르다. 자연재해가 발생한다면 당신은 1초의 data를 잃어버릴수도있다.fsync, data를 저장하는것은 OS에 맡기는것이다. 빠르지만 안전하지 않은 방법이다.추천하는(그리고 default로 설정되어있는)건 fsync every second로 설정하는것이다. 매우 빠르면서 매우 안전하다always설정은(위에보면 every time인것같다.) 매우 느리다(Redis 2.0에서 성능향상이 있었지만 아직도 느리다) 더 빠르게 하는 방법은 없다.
서버가 AOF파일에 쓰는도중 충돌 나는건 충분히 가능한 얘기이다.충돌난 파일은 Redis에서 더 이상 사용하지않는다. 충돌이 났을경우 당신은 아래와 같은 방법으로 문제를 해결할 수 있다.
redis-check-aof tool을 사용하여 원본 파일을 고친다
$ redis-check-aof --fix <filename>
diff -u 명령을 사용하여 백업파일과 원본 파일의 차이를 점검한다.이미 snapshotting에서 사용했던것처럼 로그 다시쓰기도 copy-on-write방법을 사용한다.
다음과 같이 진행한다.
Redis2.0과 Redis2.2가 절차가 다릅니다. Redis2.2가 간단하고, 재시작할 필요가 없습니다.
Redis 2.2
첫번째 CONFIG 명령은 AOF 기능을 활성화 한다. Redis는 초기 dump파일을 생성하기위해서 차단될 것이다.
그리고 기록할 파일을 열고, 다음 쿼리부터 이어쓸것이다.
두번째 CONFIG 명령은 snapshotting persistence을 끌때 사용한다. 이것은 선택사항이다.
IMPORTANT: redis.conf를 수정할때는 AOF기능을 키고 수정해야한다. 그렇지 않고 서버를 재시작하면 변경된 설정은 잃어버릴수도 있다. 그리고 변경전 설정파일로 서버는 시작할것이다.
Redis 2.0
레디스에서 AOF랑 RDB를 동시에 사용하고 싶은데, 잘 안되서 공식 사이트를 열심히 보면서 정리하다가 보니 번역을 하고 있더군요. 허헛..;;
첨에는 혼자 정리하는거라 마구 적다가, 후반부에는 지쳐서 대충 한부분도 없지않은것 같습니다. 혼자 보는 것도 좋은데 누군가에겐 도움이 되지 싶어 올립니다.
의견이 있으시면 알려주시어요~^^
PostgreSQL같은 데이터 안정성을 원한다면 말야. 둘 다 쓰는거임. ㅎㅎㅎ 너가 데이터에 신경을 곤두세우고는 있지만, 그냥 단순히 RDB만 쓰고 있으면, 재난이든 머든 단 몇분일지 몰라도 데이터가 날아가는 걸 볼 수 있을 꺼야. OTL 그리고 AOF만 쓰는 유저들도 많은데, 우리는 이것을 권장하지 않아. 왜냐면 데이터 베이스 백업으로는 RDB만한게 없으니깐.
노트 : 머 이런 이유들로 나중에 우리는 AOF랑 RDB랑 합칠꺼야(롱~~~~텀 플랜임.)
디폴트로 레디스는 데이터 셋의 스냅샷을 디스크로 남겨. dump.rdb라는 바이너리 파일로 말이지. 이건 설정 파일에서 설정 가능해. 데이터셋의 최소 N초동안 최소 M의 변화가 있을 때 저장하도록 말야. 물론 수동으로 SAVE 나 BGSAVE 명령어를 날려서 저장하는 것도 가능.
save 60 1000
이 전략은 스냅샷찍기라고 알려져 있어.
레디스가 데이터셋을 디스크로 덤프해야할 필요가 있을 때, 무슨일이 일어나냐면:
이 방법은 copy-on-write semantics 에서 가져왔는데 redis가 득 좀 봤지.ㅋ
스냅샷만 찍는건 매우 베리 안정적이지 않아. Redis를 정지 시켜버리거나, 파워 라인이 끊어지거나, 니가 실수로 kill -9 를 사용하거나 해서 최신의 데이터가 그대로 날아가 버리고 말지. 이런 상황이 별거 아닌 어플리케이션이 있는 반면, 100% 안정적이어야 하는 경우도 있지. 근데 100% 안정적인 옵션은 레디스에 없어.
append-only file은 최대-안정화 전력을 위한 레디스의 대안이야. 1.1버전부터 있었지. 설정 파일에서 AOF를 켤 수 있어.
appendonly yes
이제부터, 데이터 셋을 바꾸는 모든 명령어를 AOF에 붙이게 될꺼야. 만약에 니가 재시작을 하게되면, Redis는 AOF를 다시 실행해 볼꺼고 상태를 재조정 하게 될꺼야.
아마도 예측이 되겠지만, 많은 명령이 수행 되면 될수록 AOF도 점점 커질꺼야. 너가 하나의 카운트를 100번 증가시키면, 너는 데이터 셋의 단지 하나의 키만 데이터 셋에 유지하는 것이지만, 너의 AOF에는 100개의 엔트리가 들어가게 되지. 99개의 엔트리는 필요 없는것이야. 현재의 상태로 다시 되돌릴때에는 말이지.
그래서 레디스는 재밌는 기능을 제공해주고 있는데, AOF를 클라이언트에 대한 서비스의 중지없이 백그라운드로 재구축할 수 있어. 너가 BGREWRITEAOF를 실행하면, 레디스는 현재의 메모리에 있는 데이터셋으로 재구축 할 수 있게끔하는 최단의 명령어열을 AOF에 쓰게 될거야. 만약에 니가 redis2.2버전에서 AOF를 사용하고 있다면 BGREWRITEAOF를 시시때때로 사용해야돼. 2.4부터는 트리거를 걸 수 있어서 자동으로 할 수 있어.
얼마나 자주 fsync를 할지 설정할 수 있어. 여기에는 3가지 옵션이 있지.
추천 하는 정책은(디폴트) 1초마다 fsysnc하는거야. 빠르기도하고 꽤 안전하지. 매번 fsync하는 건 실제사용하기엔 너무 느려. (2.0에서 개선되긴 했지만 말여.) – fsync보다 빠르거나 그걸 빠르게 하는 방법은 없더라고.
AOF파일에 쓰는중에 서버에서 문제가 생기면, 파일이 깨지고 그건 redis에서 읽을 수 가 없지. 그 때는 아래에 나와 있는대로 하면 고칠 수 있을 거야.
$ redis-check-aof --fix
diff -u 를 사용해서 어디가 잘 못된건지 찾아본다.로그 덧붙여 쓰기(log rewriting)는 스냅샷찍기에서 사용한 copy-on-write 트릭을 동일하게 사용함.
2.0하고 2.2가 해야하는 절차가 다르다. 니가 생각한대로 2.2가 좀 더 간단하고 재시작이 필요읍따.
redis-cli config set appendonly yesredis-cli config set save ""첫번째 CONFIG 명령어는 Append Only File을 사용가능하게 해줌. 그리고 AOF를 사용하기 위해 Redis는 dump를 초기화 시키는 것을 막을 것이다. 그리고 나서 파일을 열고 들어오는 쿼리들을 기록할 것이다.
두번째 CONFIG 명령어는 스냅샷을 찍는것을 끄는 명령이다. 이것은 옵션으로서, 원한다면 둘다 켜놓을 수 있다.
중요 redis.conf를 수정해야 된다는걸 꼭 기억해라. 안그러면 서버를 재시작 했을 때 원래의 설정으로 대돌아간다.
Redis >= 2.4 에서는 RDB 스냅샷이 작동 중일때 또는 BGSSAVE가 작동 중일때 에는 AOF rewrite 트리거를 피하도록 했다. 2개의 무거운 백드라운드가 동시에 실행되는 것을 막기 위함임. 스냅샷을 남기는 도중에 유저가 BGREWRITEAOF커맨드를 실행했을 때 서버는 OK라는 상태코드-작업이 스케줄링 되었다는 뜻-로 응답할것이고, rewrite는 스냅샷 찍는게 완료된후 실행될 것이다. AOF랑 RDB둘다 유효로 되어 있는 경우, 레디스가 재시작되면 AOF파일이 재구축 된다.
이 섹션을 시작하기 전에, 다음의 문장을 정확히 읽고 확인해보라.
디스크가 깨지거나, 클라우드에 있는 인스턴스가 없어지거나, 기타등등..: 백업이 없다는게 의미하는건 데이터가 /dev/null 로 사라지게 될 큰 위험을 지고 있다는 것이야! 레디스는 데이터 백업에 베리 친절해. 데이터 베이스가 작동중에도 너는 RDB로 스냅샷을 뜰 수 있지:RDB는 한번 만들어지면 절대 수정되지 않아, 그리고 만드는 중에는 임의의 이름을 사용하다가 스냅샷이 끝나고 나서야 마침내 이름을 변경하지!
이것이 의미하는것은 이거야. RDB파일을 카피하는 것은 완전 안전하다는 것이지. 서버가 운용중일 때에도 말이지! 다음에 나오는 건 우리가 추천하는 것들이야:
재해 복구는 레디스에서는 기본적으로 백업이랑 같은 스토리야. 여기서 플러스로 각각의 백업들을 많은 다른 외부 데이터 센터로 전송시키는 것이 필요하지. 이 방법을 사용하면 대재앙이 일어나서 레디스가 돌고 있는 메인 데이터 센터에 영향이 갈때에도 안전하다고! 스타트업에 있거나 돈이 별로 없는 수많은 레디스 유저들을 위해서 우리는 매우 흥미로운 재해 복구 테크닉을 알려줄거야. 그건 돈 별로 안들어..ㅋ
시스템은 쉽게 고장난 다는것을 이해는건 참으로 중요해. 최소한 전송이 끝났는지는 파일 사이즈를 체크하던지 해서 절대적으로 확인해야해! 그리고 가능하다면 SHA1암호화 방식을 사용해 너의 VPS에 말이지. 너는 독립된 경고 시스템이 필요할 지도 모르겠어. 어떤 이유로 최신 백업이 제대로 동작 하지 않을 때를 위해서 말이지.
Redis 서버 설정을 위해서 작성하는 redis.conf 파일에 대해서 정리한다.
오역 및 잘못된 내용이 있을 수 있습니다. 참고 용로도만 사용해 주세요.
대상 파일: https://raw.github.com/antirez/redis/2.4.15/redis.conf
Redis는 기본적으로 daemon으로 실행하지 않는다. 만약 Daemon으로 실행하고 싶다면 ‘yes’를 사용해라.
Redis는 daemon으로 실행될 때 ‘/var/run/redis.pid’ 파일에 pid를 기록할 것이다.
예) daemonize no
daemon으로 실행 시 pid가 기록될 파일 위치를 설정한다. 값이 설정되지 않으면 ‘/var/run/redis.pid’에 pid를 기록한다.
예) pidfile /ver/run/redis.pid
Connection을 허용할 Port를 지정한다. 기본값은 6379이다.
만약 port 값을 0으로 지정하면, Redis는 어떤 TCP socket에 대해서도 listen하지 않을 것이다.
예) port 6379
Redis를 bind 할 특정 interface(랜카드)를 지정할 수 있다. 만약 명시하지 않으면, 모든 interface로부터 들어오는 요청들을 listen할 것이다.
예) bind 127.0.0.1
들어오는 요청을 listen할 unix socket의 결로를 지정한다. 이 설정에는 기본 값이 없다. 따라서 값이 지정되지 않으면 unix socket에 대해서는 listen하지 않을 것이다.
예) unixscoket /tmp/redis.sock
예) unixsocketperm 755
client의 idle이 N 초 동안 지속되면 connection이 닫힌다. (0으로 지정하면 connection이 계속 유지된다.)
예) timeout 0
logl evel 을 지정한다. Log level 에는 아래 4가지 중 하나를 지정할 수 있다.
| loglevel | 설 명 |
|---|---|
| debug | 엄청나게 많은 정보를 기록한다. 개발과 테스테 시 유용하다. |
| verbose | 유용하지 않은 많은 양의 정보를 기록한다. 하지만 ‘debug level’만큼 많지는 않다. |
| notice | 제품을 운영하기에 적당한 양의 로그가 남는다. |
| warning | 매우 중요하거나 심각한 내용만 남는다. |
예) loglevel verbose
로그 파일을 명시한다.’stdout’로 Redis가 the standard output에 로그를 기록하도록 할 수 있다. 만약 ‘stdout’를 명시했으나 Redis가 daemon으로 동작한다면 로그는 /dev/null logfile stdout 로 보내질 것이다. 따라서 로그가 남지 않을 것이다.
예) logfile stdout
system logger를 사용해서 logging 할 수 있게 한다. 단지 ‘yes’로 설정하고 추가적 설정을 위해서 다른 syslog parameter들을 설정할 수 있다.
예) syslog-enabled no
syslog identity를 지정한다.
예) syslog-ident redis
syslog facility(시설)을 지정한다. 반드시 USER 또는 LOCAL0 – LOCAL7 사이 값이 사용되어야 한다.
예) syslog-facility local0
dababase들의 숫자를 설정한다. 기본 dababase는 DB 0이다. 물론 connecton당 ‘SELECT <dbid>’ 사용해서 다른 database를 선택할 수 있다. dbid는 0 과 ‘database 수 – 1’사이의 수이다.
예) databases 16
disk에 DB를 저장한다. DB에서 주어진 값인 seconds와 changes를 모두 만족시키면 DB를 저장 할 것이다.
이 설정은 여러번 설정할 수 있다.
아래는 예제를 설명한 것이다.
900초(15)분 동안에 1개 이상의 key 변경이 발생했다면 DB를 저장한다.
300초(5)분 동안에 10개 이상의 key 변경이 발생했다면 DB를 저장한다.
60초(1)분 동안에 10000개 이상의 key 변경이 발생했다면 DB를 저장한다.
실제 서비스에서는 너무 자주 동기화가 일어나게 설정하면 안된다.
주목: DB를 저장하고 싶지 않으면 모든 save 라인들을 주석 처리한다.
예) save 900 1
예) save 300 10
예) save 60 10000
.rbd database를 덤플 할 때 LZF를 사용해서 문자열 부분을 압축할지 설정한다.
압축하는 것은 대부분의 경우 좋기 때문에 기본값은 ‘yes’이다.
만약 child set을 저장할 때 CPU 사용을 절약하고 싶다면 ‘no’로 설정한다. 하지만 values 또는 keys가 압축이 가능했다면, dataset은 보다 커질 것이다.
예) rdbcompression yes
DB가 dump될 파일을 설정한다.
예) dbfilename dump.rdb
DB가 기록될 디렉토리를 설정한다. DB dump파일은 위의 dbfilename과 함께 최종적으로 파일이 기록될 곳이 지정된다.
Append Only File 또한 이 디렉토리에 파일을 생성할 것이다.
반드시 여기에 디렉토리를 설정해야 한다. file name에 설정하면 안 된다.
예) dir ./
Master-Slave replication을 구성하기 위해서 slaveof를 사용한다. 이것은 Redis instance가 다른 Redis server의 복사본이 되게 한다.
slave에 대한 설정은 local에 위치한다. 따라서 slave에서는 내부적으로 따로 DB를 저장 하거나, 다른 port를 listen하는 등 slave만의 설정이 가능하다.
예) slaveof 127.0.0.1 6379
만약 master에 password가 설정되어 있다면 replication synchronization(복제 동기화) 과정이 시작되기 전에 slave의 인증이 가능하다. (이것은 “requirepass” 설정으로 가능하다). 만약 password가 틀리다면 slave의 요청은 거절될 것이다.
예) masterauth password1234
만약 slave가 master와의 connection이 끊어 졌거나, replication이 진행 중일 때는 아래의 2가지 행동을 취할 수 있다.
예) slave-server-stale-data yes
Salve가 내부적으로 미리 정의된 server로 지정된 시간마다 PING을 보내도록 설정한다.
예) repl-ping-slave-perid 10
‘bulk transfer I/O(대량 전송 I/O) timeout’과 ‘master에 대한 data또는 ping response timeout’을 설정한다.
이 값은 ‘repl-ping-slave-period’ 값 보다 항상 크도록 설정되어야 한다. 그렇지 않으면 master와 slave간 작은 traffic이 발생할 때 마다 timeout이 인지될 것이다.
예) repl-timeout 60
client에게 다른 command들을 수행하기 전에 password를 요구하도록 설정한다. 이것은 redis-server에 접근하는 client들을 믿을 수 없는 환경일 때 유용한다.
반대로 개방적으로 사용하기 위해서는 주석 처리 되어야 한다. 왜냐하면 대부분의 사람들은 auth가 필요하지 않기 때문이다.
주의: Redis는 매우 빠르기 때문엔 좋은 환경에서는 외부 사용자가 1초 당 15만개의 password를 시도할 수 있다. 따라서 매우 강력한 password를 설정해야 한다.
예) requirepass foobared
Commnad renaming
공유되는 환경에서는 위험한 command들의 이름을 변경할 수 있다. 예를 들어서 CONFIG command를 추측하기 어려운 다른 값으로 변경할 수 있다. 물론 internal-use tool로는 해당 명령어가 사용하지만, 일반적인 외부 client들에 대해서는 불가능하다.
예) rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
또한 command를 공백으로 rename해서 완전히 사용 불가능하게 할 수도 있다.
예) rename-command CONFIG “”
한번에 연결될 수 있는 최대 client 수를 설정한다. 기본값은 제한이 없으나, Redis process의 file descriptor의 숫자만큼 연결가능하다. 특별히 ‘0’ 값은 제한 없음을 의미한다.
limit에 도달했을 때 새로운 connection들에 대해서는 ‘max number of clients reached’ error를 전송한고 connection을 close 한다.
예) maxclients 128
명시한 bytes의 양보다 많은 메모리를 사용하지 말아라.
memory가 limit에 도달했을 때 Redis는 선택된 eviction policy(제거 정책)(maxmemory-policy)에 따라서 key들을 제거할 것이다.
만약 Redis가 eviction policy에 의해서 key를 제거하지 못하거나 polict가 ‘noeviction’으로 설정되어 있다면, SET, LPUSH와 같이 메모리를 사용하는 command들에 대ㅐ서는 error를 반환하고, 오직 GET 같은 read-only command들에 대해서만 응답할 것이다.
주의: maxmoery 설정 된 instance에 slave가 존재 할 때, slave에게 data를 제공하기위해서 사용되는 output buffer의 size는 used memory count에서 제외된다. 왜냐하면 network 문제나 재동기화가 keys들이 제거된 loop에 대한 trigger를 발생시키지 않게 하기 위해서이다. loop가 발생하면 slave의 output buffer가 제거된 key들의 DEL 명령으로 가득 찰 것이다. 그리고 이것은 database가 완전히 빌 때 까지 지속된다. (좀 더 정확히 알아볼 필요가 있다. 확실히 이해가 안 됨)
간단히 말하면, 만약 slave를 가진다면, slave output buffer를 위해서 system에 약간의 free RAM이 존재시키기 위해서, maxmoery에 약간 낮은 limit를 설정하는 것이 추천된다. (하지만 policy가 ‘noeviction’이면 필요하지 않다.)
큰 메모리를 표시하기 위해서 아래와 같이 표기가 가능하다.
| 1k | 1,000 bytes |
| 1kb | 1024 bytes |
| 1m | 1,000,000 bytes |
| 1mb | 1024*1024 bytes |
| 1g | 1,000,000,000 bytes |
| 1gb | 1012*1024*1024 bytes |
예) maxmoery 1000
MAXMEMORY POLICY: maxmemory에 도달했을 때 무엇을 삭제 할 것인지 설정한다. 아래 5개 옵션 중 하나를 선택할 수 있다.
| 옵션 | 설 명 |
|---|---|
| volatile-lru | expire가 설정된 key 들 중 LRU algorithm에 의해서 선택된 key를 제거한다. |
| allkeys-lru | 모든 key 들 중 LRU algorithm에 의해서 선택된 key를 제거한다. |
| volatile-random | expire가 설정된 key 들 중 임의의 key를 제거한다. |
| allkeys-random | 모든 key 들 중 인의의 key를 제거한다. |
| volatile-ttl | expire time이 가장 적게 남은 key를 제거한다. (minor TTL) |
| noeviction | 어떤 key도 제거하지 않는다. 단지 쓰기 동작에 대해서 error를 반환한다. |
예) maxmemory-policy volatile-lru
LRU와 minimal TTL algorithms(알고리즘)은 정확한(최적) 알고리즘들은 아니다. 하지만 거의 최적에 가깝다. 따라서 (메모리를 절약하기 위해서) 검사를 위한 샘플의 크기를 선택할 수 있다. 예를 들어, Redis는 기본적으로 3개의 key들은 검사하고 최근에 가장 적게 사용된 key를 선택할 것이다. 하지만 아래 예와 같이 샘플의 크기를 변경할 수 있다.
예) maxmoery-samples 3
Redis는 기본적으로 비동기로 disk에 dataset의 dump를 남긴다. 이 방법은 crash가 발생했을 때 최근의 record를 손실되어도 문제가 없을 때 좋은 방법이다. 하지만 하나의 record도 유실되지 않기를 원한다면 append only mode를 활성화 시키는 것이 좋을 것이다.: 이 mode가 활성화 되면, Redis는 요청받는 모든 write operation들을 appendonly.aof 파일에 기록할 것이다. Redis가 재 시작될 때 memory에 dataset를 rebuild하기 위해서 이 파일을 읽을 것이다.
주목: 원한다면 async dumps와 asppend only file을 동시에 사용할 수 있다. 만약 append only mode가 활성화 되어 있다면, startup 때 dataset의 rebuild를 위해서 append only mode의 log file을 사용하고, dump.rdb 파일은 무시할 것이다.
중요: 순간적으로 write operation이 많을 때 background에서 어떻게 append log file을 rewirte 하는 방법을 확인하기 위해서 BGREWRITEAOF를 확인해라.
예) appendonly no
append only file의 이름을 설정한다.
파일이 저장되는 디렉토리는 SNAPSHOTTING의 dir 속성을 사용한다.
예) appendfilename appendonly.aof
fsync() call 은 Operating System(운영체제)에게 output buffer 내에 보다 많은 데이터들을 기다리지 않고, disk에 실제로 data를 작성하라고 말한다. 어떤 OS 는 실제로 disk에 data를 기록할 것이고, 어떤 OS들은 가능한 빨리 data를 기록하려고 시도할 것이다.
Redis는 3가지의 다른 mode들을 지원한다.
| 옵션 | 설 명 |
|---|---|
| no | fsync()를 호출하지 않는다. data의 flush를 OS에 맡긴다. 빠르다. |
| always | append only log에 wrtie 할 때 마다 fsync()를 호출한다. 느리다, 안전하다. |
| everysec | 매 초 마다 fsync()를 호출한다. 절충안 |
기본값인 ‘everysec’은 일반적으로 속도와 데이터 안정성 사이에서 적절한 절충안이다. 이것은 당신의 관대함(?)에 달려있다. 만약 ‘no’로 설정한다면 OS는 적절한 타이밍에 output buffer를 flush하고 보다 좋은 성능을 낼 것이다. (그러나 만약 data loss가 발생해도 문제가 없다면 기본 persistence(영속성) mode는 snapshotting일 것이다.) 반면에 “always”를 사용하면 매우 느릴 것이다. 하지만 ‘everysec’보다 조금 더 안전할 것이다.
만약 확실하지않다면 ‘everysec’를 사용해라.
예) appendfasync everysec
AOF fsync policy를 always 또는 everysec로 설정했다면, background saving process(background 저장 또는 AOF log backgournd rewriting)은 disk에 대해서 매우 많은 I/O를 발생시킬 것이다. 따라서 어떤 Linux에서 fsync() call은 매우 긴 block를 발생시킬수도 있다.
주목: 현재 이것에 대한 fix(수정)은 없다. 다른 thread에서 fsync를 수행하더라도 동시에 발생하는 wirte call(2)이 block 될 것이다.
이 문제를 완화 시키기 위해서 BGSAVE 또는 BGREWRITEAOF가 수행중인 동안에 메인 process에서 fsync()가 호출되는 것을 막을 수 있는 no-appendfsync-on-rewirte option을 사용하는 것이 가능하다.
이것은 Redis의 영속성을 저장하는 동안에 다른 자식은 “appendfsync none”으로 설정한 것과 동일하다. 쉽게 말하면, (Linux의 기본 설정에 의해서) 최악의 경우 30초 가량의 log가 손실될 수 있다는 것이다. (이해가 잘 안됨)
이로 인해서 잠재적 문제가 있다면 이 옵션을 ‘yes’로 설정해라. 하지만 다른 경우에는 ‘no’로 남겨두는 것이 영속성의 view(?)부터 가장 안전한 선택이다.
예) no-appendfsync-on-rewrite no
Append only file의 Automatic rewrite(자동 재 작성)
Redis는 AOF log의크기가 명시된 퍼센트를 넘어갔을 때, 자동적으로 BGREWRITEAOF를 호출함으로서 log file를 재작성 하는 것이 가능하다. (비교 대상이 무엇인지 모르겠다.해당 Drive의 전체 크기인가?)
작동법: Redis는 최근 rewirte후에 AOF file의 크기( 또는 restart이후 rewirte가 발생하지 않았다면, startup 때 사용된 AOF의 크기)를 기억하고 있다.
이 기본 크기는 현재 크기와 비교되어진다. 만약 현재 크기가 설정된 퍼센트보다 크다면, rewrite가 동작하게된다. 또한 rewritten(재작성되는) AOF 파일의 최소 크기를 지정해 주어야 한다. 이것은 비록 퍼센트 증가가 설정 값에 도달하더라도 여전히 작은 크기일 때 AOF file이 rewriting되는 것은 피하는데 유용하다.
Automatic AOF rewirte을 비활성화 시키기 위해서는 percentage값을 0으로 설정해라.
예) auto-aof-rewrite-percentage 100
예) auto-aof-rewrite-min-size 64mb
Redis Slow Log는 설정된 실행 시간을 초과한 쿼리들의 로그를 남기는 시스템이다. 실행 시간은 talking with the client, sending the reply와 같은 I/O operation들은 포함되지 않는다. 단지 command를 수행하는데 필요한 시간(command 실행을 위해서 threaad가 block되어서 다른 request를 처리할 수 없는 시간)만이 측정된다.
2개의 parameter들를 통해서 slow log를 설정할 수 있다. : 첫번째 parameter는 Redis가 slow log를 기록하기 위해서 어떤 execution time이 느린 것인지 알려주기 위해서 microsencond로 설정한다. 두번째 parameter는 기록될 수 있는 slow log의 길이이다. 새로운 command가 log 되었을 때, 가장 오래전에 기록된 log가 제거된다. FIFO 형태인 것이다.
microsencod값으로 slow execution time를 설정한다. 1000000은 1초와 같다.
주의: 음수를 설정한 경우 slow log를 비활성화 시킨다. 0으로 설정한 경우 모든 command에 대해서 logging 수행된다.
예) slowlog-log-slower-than 10000
길이에는 제한이 없다. 단지 이것은 memory를 소모하는 것은 인지하고 있어야 한다.
SLOWLOG RESET 명령으로 slow log에 의해서 사용된 memory를 반환 시킬 수 있다.
예) slowlog-max-len 128
Virtual Memory는 Redis 2.4에서 제거되었다. vm-enabled no로 설정해서 사용하지 않는다.
예) vm-enabled no
hash들은 elements의 숫자가 설정된 entries(개수)에 도달하고 가장 큰 element가 설정된 threshold(기준치)를 초과하지 않으면 특별한 방법으로(보다 효과적인 메모리 사용법으로) encoded(인코딩)되어진다.
특별한 방법: hashtable 또는 zipmap
예) hash-max-zipmap-entries 512
예) hash-max-zipmap-value 64
hash와 비슷하게 작은 list도 공간을 절약하기 위해서 특별한 방법으로 encode되어진다. 오직 설정된 값 보다 아래에 있을 때 특별한 방법이 사용되어진다.
특별한 방법: linkedlist 또는 ziplist
예) list-max-ziplist-entries 512
예) list-max-ziplist-value 64
Set은 오직 한 가지 경우에만 특별한 방법으로 encoded 되어진다. Set이 오직 string(문자열)로만 구성되었을 때, 64bit signed integer 범위의 radix 10의 integer로 변환된다.
아래 설정은 특별한 메모리 저장 인코딩을 사용하기 위해서 set 의 크기 제한을 설정한다. (정확한 확인 필요, 아마도 아래보다 크지 않을 때 사용하지 않을까 싶다.)
예) set-max-intset-entries 512
hash 및 list와 비슷하게, sorted set도 많은 공간을 절약하기 위해서 특별하게 encoded되어진다. 이 Encoding은 sorted set의 element와 length가 설정치를 초과하지 않을 때 사용되어 진다.
예) zset-max-ziplist-entries 128
예) zset-max-ziplist-value 64
rehashing을 활성화 하면 main Redis has table(최상위 key-value hash table)을 rehashing하는 것을 돕기 위해서 CPU time의 매 100 millisecond 마다 1 millisencond를 사용한다. redis가 사용하는 hash table 구현은 lazy rehashing으로 동작한다. rehashing이 수행되는 hash table에서 많은 operation을 수행 할 수록, 보다 많은 rehashing ‘steps’이 수행된다. 따라서 server가 idle 상태이면 rehashing은 결코 완료되지 않고, 약간의 메모리가 hash table에 의해서 사용되어진다.
기본값은 가능할 메모라는 free하게 만들기 위해서, main dictionary들의 rehashing을 active하는데 매 초마다 10 millisecond를 사용하는 것이다.
만약 힘든 잠재적 요구사항이나 당신의 환경에 적합하지 않아서 확신이 들지 않는다면 ‘activerehashing no’를 사용해라. 이 설정으로 인해서 Redis가 request들에 대해서 reply하는데 2 milliseoncds의 delay(지연)가 발생할 것이다.
만약 어려운 환경이 아니고 가장한 빠르게 메모리는 free하게 하고 싶다면 ‘activerehashing yes’를 사용해라.
예) activerehashing yes
인피니스팬 충분히 해볼만한 가치가 있어보인다…
데이터 그리드 * 분산 인메모리 캐시로 좋을듯해보인다…
| 인피니스팬 | 번역: JBUG
이 글은 The Performance of Open Source Application 도서의 Infinispan 챕터를 번역한 것입니다. 원저자는 Manik Surtani이며 원문은 아래 원문링크를 클릭하시면 보실 수 있습니다. |
인피니스팬(주1)은 오픈소스 데이터그리드 플랫폼이자 분산 인메모리 키/밸류 NoSQL 스토어이다. 소프트웨어 아키텍트는 보통 인피니스팬 등의 데이터그리드를 관계형 데이터베이스 같은 느린 데이터 저장소 앞단에 두어 성능 확장 목적의 분산 인메모리 캐시로 사용하거나 또는 관계형 데이터베이스를 대체하려는 목적으로 분산 NoSQL로써 사용한다. 어떤 경우든지 소프트웨어 아키텍처에서 데이터그리드를 도입하려는 주요 이유는 성능이다. 지연 시간 없이 빠르게 데이터에 접근하고자 하는 필요성은 급속도로 증가하고 있다. 그래서, 성능은 인피니스팬의 유일한 존재 이유이다. 인피니스팬 기반 코드는 극도로 성능 위주로 되어 있다.
인피니스팬을 깊게 다루기 전에 인피니스팬을 일반적으로 어떻게 사용하는지 살펴보자. 인피니스팬은 미들웨어라는 소프트웨어 분류에 속한다. 위키피디아에 따르면 미들웨어는 웹사이트와 운영체제 혹은 데이터베이스 같은 애플리케이션 사이에 있는 서버상의 컴포넌트로 “소프트웨어 접착체로 설명될 수 있다”. 미들웨어는 때때로 애플리케이션 개발자가 더 생산적이고 더 효율적이며 빠르게 유지보수와 테스트가 용이한 애플리케이션을 만들기 위해 사용된다. 이 모든 것들은 모듈화와 컴포넌트 재사용에 의해 이루어진다. 인피니스팬은 특히 애플리케이션이 수행하는 비즈니스 로직과 데이터 스토리지 계층 사이에 주로 위치한다. 데이터 저장과 읽기는 흔한 최대의 병목 구간이고 데이터베이스 앞에 인메모리 데이터그리드를 위치하는 것은 속도를 훨씬 빠르게 한다. 게다가 데이터 저장소는 때때로 잠재된 단일 장애점(a single point of potential failure)이다. 다시 말해, 인피니스팬을 기존의 데이터 저장소 앞에 둔다면 (또는 대체한다면) 애플리케이션은 훌륭한 탄력성(elasticity)과 확장성(scalability)을 갖게 된다.
인피니스팬은 텔레콤, 금융, 전자상거래, 제조, 게임과 모바일 플랫폼 등 많은 산업 분야에서 사용되고 있다. 데이터그리드는 대개 금융산업에서 많이 사용되어 왔는데 이는 개별 장비의 장애로부터 영향받지 않으면서 대용량 데이터에 대한 극도로 빠른 접근이라는 엄격한 요구사항 때문이었다. 이런 요구사항은 다른 산업으로 퍼졌고 인피니스팬이 다양한 분야의 애플리케이션에서 대중화 되는 데 일조했다.

인피니스팬은 자바(그리고 약간의 스칼라)로 구현되었고 두 가지 다른 방법으로 사용된다. 첫째는 라이브러리로 사용하는 것이다. 애플리케이션에 인피니스팬 JAR 파일을 포함하여 인피니스팬 컴포넌트를 프로그램 안에서 참조하고 인스턴스화하여 사용하게 된다. 이 경우에 인피니스팬 컴포넌트가 애플리케이션과 같은 JVM 위에 있고 애플리케이션 힙 메모리의 한 부분이 데이터그리드 노드에 할당된다.

그림1 인피니스팬을 라이브러리로 사용
두 번째로 여러 개의 인피니스팬 인스턴스를 시작하고 클러스터를 만들어서 원격 데이터그리드로써 사용할 수 있다. 클라이언트 라이브러리들이 이미 많이 존재하고 클라이언트는 소켓 연결로 클러스터에 접속하게 된다. 이 방법은 각각의 인피니스팬 노드가 자신의 독립된 JVM 위에 존재하고 전체 JVM 힙 메모리를 자신이 직접 사용하게 된다.
그림2 인피니스팬을 원격 데이터그리드로 사용
위의 두 가지 모두 인피니스팬 인스턴스는 네트워크 너머 서로를 감지하고 클러스터를 구성하며 클러스터 내 모든 서버들 위에 투명하게 펼쳐진 인메모리 데이터 구조를 애플리케이션에 제공하여 데이터를 공유하게 된다. 클러스터에 노드를 추가하면 총 용량이 증가하며 이는 애플리케이션이 이론적으로는 무한대의 인메모리 저장소를 가지게 됨을 의미한다.
인피니스팬은 피어투피어 기술을 사용하며 클러스터 내 각 인스턴스는 다른 모든 인스턴스와 동등하다. 이것은 단일 장애점이 없다는 것과 단일 병목 구간이 없다는 것을 의미한다. 가장 중요한 것은 여러 인스턴스들을 추가함에 따라 수평적으로 확장(scale out)하는 탄력적인 데이터 구조를 제공한다는 것이다. 그리고 몇몇 인스턴스를 중단하는 축소(scale back in)도 역시 가능한데, 이 모든 것이 기능 손실 없이 애플리케이션이 지속적으로 동작하는 중에 이루어진다.
인피니스팬 같은 분산 데이터 구조를 벤치마킹하는 데 있어서 가장 큰 문제는 도구들을 다루는 것이다. 확장(scale out) 또는 축소(scale back in)되는 동안 데이터를 저장하고 가져오는 성능을 측정하는 도구는 극 소수이다. 그리고 설정과 클러스터 크기가 다를 때 성능을 측정하거나 비교를 가능케 하는 비교 분석을 제공하는 것은 없다. 이를 위해 레이더건이 만들어졌다.
레이더건은 다음 링크(http://aosabook.org/en/posa/infinispan.html#posa.infinispan.radargun)에서 자세한 설명을 볼 수 있다. 야후 클라우드 서빙 벤치마크(Yahoo Cloud Serving Benchmark), 그라인더(The Grinder), 아파치의 제이미터(Apache JMeter) 같은 다른 도구들은 인피니스팬을 벤치마킹하는 데 여전히 매우 중요하지만 많이 다루지는 않을 것이다. 이미 이들에 대한 설명이 인터넷에 많이 있기 때문이다.
레이더건(주2)은 오픈소스 벤치마킹 프레임워크로써 밴치마크 수행(comparative and competitive), 확장성 측정, 데이터가 수집될 때부터의 보고서 생성을 위해 설계되었다. 레이더건은 특별히 인피니스팬 같은 분산 데이터 구조를 타깃으로 했고 인피니스팬 개발 시 병목 부분을 확인하고 해결하는 데 많이 쓰였다. 레이더건을 위한 더 많은 정보는 다음 링크(http://aosabook.org/en/posa/infinispan.html#posa.infinispan.radargun)를 확인하면 된다.
야후 클라우드 서빙 벤치마크(The Yahoo Cloud Serving Benchmark, YCSB)(주3)는 오픈소스 도구이고 원격 데이터 스토어가 다양한 크기의 데이터를 읽고 쓰려고 통신할 때 그 레이턴시를 테스트 하기위해 만들어졌다. YCSB는 모든 데이터 스토어를 단일한 원격 지점으로 다룬다, 그래서 노드가 클러스터에 추가되거나 지워지는 것과 같은 확장을 측정하지는 않는다. YCSB는 분산 데이터 구조에 대한 개념이 없고 클라이언트/서버 모드의 인피니스팬을 벤치마크할 때만 유용하다.
그라인더(The Grinder)(주4)와 아파치 제이미터(Apache JMeter)(주5)는 각각 단순한 오픈소스 부하 생성기이며 소켓을 열고 기다리는 서버들을 테스트하는 데 사용한다. 이들은 스크립트로 사용 가능하며 YCSB와 같이 인피니스팬이 클라이언트/서버 모드로 사용될 경우를 벤치마킹하기 위해 쓴다.
초기
인피니스팬 코어 개발팀에 의해 만들어진 레이더건은 캐시 벤치마킹 프레임워크(Cache Benchmarking Framework)(주6)라 불리는 소스포지(Sourceforge)의 프로젝트로 출발하였고 원래는 여러 가지 모드와 다양한 설정으로 실행하고 있는 내장 자바 캐시를 벤치마크하기 위한 목적으로 설계되었다. 비교도 가능하도록 만들어졌기 때문에 다양한 캐시 라이브러리에 대해 같은 벤치마크를 실행하거나 성능 회귀테스트를 위해 동일한 라이브러리의 여러 버전에 대해 테스트를 자동적으로 실행하곤 한다. 만들어지고 나서 레이더건이라는 새로운 이름을 얻었고 깃허브에 새로운 둥지(http://github.com/radargun)를 틀었다.
레이더건은 곧 분산 데이터 구조를 다루도록 확장되었다. 여전히 내장 라이브러리 테스트에 중점을 두었으며 분산 캐시 라이브러리의 인스턴스들이 올라갈 여러 서버에 복수개의 인스턴스를 띄울 수 있었다. 벤치마크는 클러스터의 각 노드에서 병렬적으로 실행되었다. 결과가 수집되고 레이더건 컨트롤러에 의해 보고서가 생성되었다. 확장성을 테스트하기 위해 자동으로 노드를 올리고 내리는 기능은 필수적인데, 수백 개 심지어 수천 개의 노드의 다양한 크기의 클러스터를 벤치마크하기 위해 수동으로 실행, 재실행하는 것은 불가능하고 비현실적이다.

그림3 레이더건
그 이후에 레이더건은 벤치마크의 각 단계를 실행하기 전후에 상태 확인을 수행하여 클러스터가 여전히 유효한 상태인지 확인하는 기능을 얻는다. 이것은 잘못된 결과를 미리 감지하게하고 많은 시간이 걸릴 수 있는 하나의 테스트 실행이 끝났을 때 수동 처리를 기다리지 않고 벤치마크를 재수행하도록 한다.
레이더건은 또한 프로파일링을 위한 인스턴스를 시작하여 각 데이터그리드 노드에 붙일 수 있고 프로파일러 스냅샷을 생성하여 부하 아래에서 각각의 노드가 어떠한지 살펴보게 한다.
레이더건은 또한 각노드의 메모리 소비 상태와 메모리 성능을 측정할 수 있는 기능이 있다. 인메모리 데이터 스토어에서 성능은 얼마나 빠르게 데이터를 읽고 쓰느냐만이 아니라 얼마나 잘 메모리 소비를 고려하면서 수행하는가도 포함된다. 이는 자바기반 시스템에서 실제로 중요한데 가비지 컬렉션이 시스템 응답도에 불리한 영향을 끼칠 수 있기 때문이다. 가비지 컬렉션은 뒤에 더 자세히 다룬다.
레이더건은 성능을 초당 트랜잭션 수로 측정한다. 이는 각 노드에서 포착되고 나서 컨트롤러에서 합해진다. 읽기와 쓰기 모두 측정되고 그 둘이 동시적으로 수행되지만 차트에는 별도로 표시된다. 레이더건은 읽기/쓰기 트랜잭션에 대한 평균값, 중앙값, 표준편차, 최대값, 최소값도 얻는다. 이들은 차트에 표시되지 않더라도 로그에는 남는다. 메모리 성능 역시 측정되는데 이는 주어진 순회(iteration)의 풋프린트를 통해 이루어진다.
레이더건은 확장 가능한 프레임워크이다. 고유의 데이터 엑세스 패턴, 데이터 타입과 크기를 끼워 넣을 수 있다. 나아가서 테스트하고자 하는 어떤 데이터 구조, 캐시 라이브러리나 NoSQL 데이터베이스에 대한 어댑터를 추가하는 것도 허용한다.
성능 병목을 가져오는 주요 의심점들이라 여겨지고 그래서 밀접한 조사와 잠재적으로 최적화의 후보가 되는 몇 가지 인피니스팬 서브시스템이 있다. 차례로 살펴보자.
네트워크 통신은 인피니스팬 최고의 비용이 드는 부분으로 피어간 또는 클라이언트와 그리드 사이의 통신하는 데 사용된다.
인피니스팬은 노드 간 통신을 위해 오픈소스 피어투피어 그룹 통신 라이브러리인 제이그룹스(JGroups)(주7)를 사용한다. 제이그룹스는 TCP 또는 UDP 네트워크 프로토콜과 UDP 멀티캐스트를 이용하며 메시지 전달 보증, 재전송, 메시지 순서 배열과 같은 고수준 기능을 제공하며 이를 UDP같은 비신뢰적인 프로토콜 위에서도 가능케 한다.
제이그룹스를 네트워크와 애플리케이션의 성격에 부합하도록 time-to-live, 버퍼 크기, 스레드 풀 크기 등을 올바르게 설정을 조정하는 것은 결정적으로 중요하다. 또한 대량의 메시지가 여러 개의 작은 네트워크 패킷으로 쪼개질 때 제이그룹스가 번들링(bundling, 여러 작은 메시지를 단일 네트워크 패킷으로 조합하는 것)이나 프래그맨테이션(fragmentation, 역번들링)을 수행하는 방법을 아는 것도 또한 중요하다.
각 운영체제나 네트웍 장비(스위치, 라우터)의 네트워크 스택은 이런 설정에 맞게 조정되어야 한다. 운영체제의 TCP 송수신 버퍼 크기, 프레임 크기, 점보 프레임 등 이 모두가 데이터그리드 내의 최고 비용 컴포넌트가 효율적으로 동작하도록 하기 위해 각자의 역할을 한다.
netstat나 wireshark 같은 도구도 패킷 분석에 도움이 된다. 레이더건은 그리드에 부하를 걸 수 있다. 레이더건은 또한 병목 지점을 찾기 위해 인피니스팬의 제이그룹스 계층을 프로파일하는 데도 쓰인다.
인피니스팬은 서버소켓을 생성하고 관리하기 위해 잘 알려진 네티 프레임워크(Netty)(주8)를 이용한다. 네티는 비동기 자바 NIO 프레임워크를 사용하며 운영체제의 비동기 네트웍 I/O 기능을 이용한다. 이는 몇몇 컨텍스트 스위칭 비용을 위해 리소스를 효율적으로 사용하게 한다. 보통 이는 부하가 걸리는 상황에서 매우 잘 동작한다.
네티는 최적 성능을 내기 위해 여러 계층의 튜닝점을 제공한다. 버퍼 크기, 스레드 풀 같은 것을 포함한 이 값들은 운영체제의 TCP 송수신 버퍼와 잘 맞추어져야 한다.
네트워크로 데이터를 전송하기 전에 애플리케이션 객체는 바이트로 직렬화(serialization)되고, 그리고 나서 데이터그리드로, 또 네트워크 너머 다른 노드로 보내진다. 바이트는 애플리케이션에 읽혀질 때 다시 객체로 역직렬화된다. 가장 일반적인 설정에서 약 20%의 시간이 요청을 직렬화와 역직렬화하는 데 사용된다.
기본 자바 직렬화(역직렬화)는 악명 높을 정도로 느리고 변환된 바이트도 때때로 불필요하게 큰데 이는 네트워크로 더 많은 데이터를 보내야 한다는 말이다.
인피니스팬은 자체 직렬화를 사용하는 데 모든 클래스 정의가 스트림으로 쓰이지 않는다. 알려진 타입에 대해 각 타입이 한 바이트로 대표되는 마법의 숫자를 사용한다. 이는 직렬화, 역직렬화의 속도를 빠르게 할 뿐 아니라 네트워크 간 전송될 바이트 스트림을 훨씬 작게 한다. 하나의 Externalizer가 각각의 알려진 데이터 타입과 그 마법 숫자에 대해 등록되는데 이 Externalizer는 객체를 바이트로 또는 역으로 전환하는 로직을 포함한다.
이 테크닉은 각 노드끼리 교환되는 인피니스팬 내부 객체 같이 알려진 타입들에 대해 매우 잘 동작한다. 커맨드(commands)나 엔벨로프(envelopes) 등의 내부 객체는 externalizer와 대응되는 고유한 마법 숫자를 가지고 있다. 애플리케이션 객체는 어떨까. 기본적으로 인피니스팬이 알지 못하는 객체 타입을 마주친다면 그때는 기본적인 자바 직렬화를 사용하게 된다. 이것은 알려지지 않은 애플리케이션 객체 타입을 다루는 방법에 있어 약간 비효율적이긴 하지만 인피니스팬이 설치 후 별다른 설정 없이 바로 사용 가능하도록 한다.
이 문제를 피하기 위해 인피니스팬은 애플리케이션 개발자가 애플리케이션 데이터 타입을 위해서 externalizer를 등록하는 것을 허용한다. 애플리케이션 개발자가 각 애플리케이션 객체 타입에 대해 externalizer를 구현하고 등록할 경우, 강력하고 빠르고 효율적인 애플리케이션 객체 직렬화를 가능하게 한다.
이 externalizer 코드는 JBoss Marshalling(주9)이라는 별도의 재사용 가능한 라이브러리로 발표되었다. 이 라이브러리는 인피니스팬에 통합되어 있고 함께 배포되며 직렬화 성능을 높이기 위한 다른 여러 오픈소스 프로젝트에 의해 쓰이기도 한다.
인피니스팬은 메모리 위의 데이터 구조이기도 하지만 선택적으로 디스크에 영속화도 한다.
영속성(persistence)은 내구성(durability)을 위하거나─메모리에 있는 모든 데이터가 디스크에 역시 존재하므로 노드의 재시작이나 장애에도 살아남음─ 또는 메모리가 가득 찼을 때 넘치도록 설정된다. 마치 운영체제가 디스크로 페이징하는 것과 비슷한 방법으로 행동한다. 후자에서, 데이터는 메모리 공간을 확보하고자 데이터가 메모리로부터 제거될 때 디스크에 쓰여진다.
내구성을 위해 영속성을 사용할 때, 영속성은 온라인(애플리케이션 스레드는 데이터가 디스크에 안전하게 쓰여질 때까지 대기한다) 또는 오프라인(데이터가 주기적/비동기적으로 디스크로 보내진다)이다. 후자의 경우, 애플리케이션 스레드는 영속성 처리를 위해 대기하지는 않지만 데이터가 디스크에 완전히 성공적으로 저장되었는지 확실하지 않다는 반대 급부가 있다.
인피니스팬은 여러 개의 캐시스토어(데이터를 디스크 또는 다른 2차 저장소에 저장할 수 있는 어댑터)를 추가하는 것이 가능하다. 현재 기본 구현체는 간단한 해시 저장 공간과 링크드리스트 구현체인데 각 해시 저장 공간은 파일시스템의 파일이다. 사용하기도 설정하기도 쉬운 반면에 최고 성능의 구현체는 아니다.
두 가지 고성능의 파일시스템 네이티브 캐시스토어 구현체가 로드맵 상에 있다. 둘 다 C로 쓰여지고 유닉스 시스템 위에서와 같이 가능한 경우에는 다이렉트 I/O를 사용하여 커널 버퍼와 캐시를 우회한다. 구현체 중 하나는 페이징 시스템 같은 용도에 최적화 될 것이며 랜덤 엑세스와 아마도 b-tree 구조를 갖게 될 것이다. 다른 하나는 내구성 있는 저장소로 최적화될 것이다 메모리에 저장된 것을 복제하게 될 것이다. 그래서 추가 전용 구조로 빠른 쓰기가 필요하지만 빠른 읽기와 찾기는 필요 없는 상황에 맞게 설계될 예정이다.
대부분의 기업용 미들웨어가 그렇듯이 인피니스팬도 현대의 멀티코어 시스템에 많은 관심을 두고 있다. 멀티코어의 SMP 시스템 내의 다수의 하드웨어 스레드뿐만 아니라 네트워크나 디스크와 통신 시 비블로킹, 비동기 I/O를 가능하도록 한 병행성을 이용하기 위해 인피니스팬의 핵심 데이터 구조는 공유 데이터 동시 접근을 위한 소프트웨어 트랜잭션 메모리 테크닉을 사용한다. 이는 명시적 잠금, 뮤텍스와 다른 형식의 동기화, 공유 데이터를 갱신 시 정확성을 체크하기 위해 비교하고 저장하는 연산 같은 테크닉을 최소화하게 한다. 그런 기술들은 멀티코어 SMP 시스템에서 CPU 사용률을 향상시킨다는 것이 증명되었고 코드의 복잡성이 증가함에도 부하 시의 성능에 기대했던 성과를 올렸다.
소프트웨어 트랜잭션 메모리 접근을 사용하는 장점에 더해서 이는 하드웨어 트랜잭션 메모리 접근을 지원하는 CPU가 나왔을 때 최소한의 인피니스팬 설계 변경만으로 동기화를 더 강화할 수 있다는 점에서 미래 경쟁력이 된다.
인피니스팬이 사용하는 몇 가지 데이터 구조는 학술 논문에서 바로 나왔다. 사실, 인피니스팬의 비블로킹, 비잠금 데크(lock-free dequeue)(주10)는 그 최초의 자바 구현체이다. lock amortization(주11)과 adaptive eviction policies(주12)도 비슷한 예이다.
인피니스팬의 여러 서브시스템은 별도 스레드에서 비동기로 동작한다. 예를 들어 제이그룹스는 네트워크 소켓을 모니터하는 스레드를 만들어 메시지를 디코드하고 그것들을 메시지 전달 스레드로 보낸다. 이는 역시 비동기적이고 별도 스레드를 사용하게 되는 디스크 캐시스토어에 데이터를 저장하는 시도가 되기도 한다. 리스너는 변경에 대한 통지를 받게 되고 이 역시 비동기적으로 설정된다.
이런 비동기 작업을 위한 스레드 풀을 다룰 때는 항상 문맥 전환(context switch) 오버헤드가 있다. 스레드는 적은 비용의 리소스가 아니라는 것은 언급할 가치가 있다. 잘 설정되고 적절한 크기의 스레드 풀은 인피니스팬의 비동기 기능 어떤 것을 사용하든지 매우 중요하다.
특별히 주의해서 봐야할 영역은 (비동기 통신을 사용한다면) 비동기 전송 스레드풀이고, 최소한 각 노드가 동시에 처리할 것으로 예상하는 업데이트 수만큼의 스레드 풀을 확보하는 것이다. 비슷하게 제이그룹스를 튜닝할 때 OOB(주13)와 incoming 스레드 풀은 적어도 동시적으로 업데이트가 예상되는 수만큼은 커야 한다.

그림4 인피니스팬에서 스레드 사용
JVM 가비지 컬렉터에 관해 일반적으로 좋은 습관을 가지는 것은 모든 자바 기반 소프트웨어에 중요한 고려사항이고 인피니스팬도 예외가 아니다. 예외가 있다면 그것은 데이터그리드에 더 중요하다. 특정 동작 또는 트랜잭션 시에 일시적인 객체가 많이 생성되는 오랜 기간 동안 컨테이너 객체는 살아있어야 할 것이다. 그리고, 가비지 컬렉션의 정지 시간은 분산 데이터 구조에는 부정적인 효과를 갖게 되어서 노드가 응답하지 않고 장애 상태로 여겨지게 한다.
인피니스팬을 설계하고 만들 때 이것을 고려했다. 하지만 동시에 인피니스팬을 실행하도록 JVM을 설정할 때에도 많은 고려사항이 있다. 각 JVM은 다르다. 인피니스팬이 실행될 때 어떤 JVM에 어떤 최적값들을 설정할지 몇몇 분석이 있다. 예를들면 만약 OpenJDK(주15) 또는 오라클의 HotSpot JVM(주16)을 사용하고, JVM Concurrent Mark와 Sweep collector(주17)를 대용량의 페이지(주18)와 함께 사용할 때 각 JVM에 대해 12G 힙메모리가 가장 효율적인 것으로 보인다.
Azul’s Zing JVM(주20)에서 사용되는 C4(주19)같은 무정지 가비지 컬렉터를 사용하는 것도 가비지 컬렉터로 인한 정지가 큰 이슈가 되는 곳에서는 고려해볼 만하다.
인피니스팬 같은 성능 중심의 미들웨어는 모든 단계에서 성능을 고려하여 설계, 개발, 구성되어야 한다. 최선의 비블로킹, 비잠금 알고리즘을 사용하는 것으로부터, 만들어지는 가비지들의 성격을 이해하고 JVM 문맥 변경에 대한 적절한 설정을 알아내고 필요할 때는 (예를들어 네이티브 영속 저장소를 작성하는 것과 같이) JVM 밖에서 문제를 해결할 수 있는 정도까지, 모두 인피니스팬을 개발할 때 필요한 중요한 사고방식이다. 게다가 벤치마킹, 프로파일링 외에도 지속적인 통합 상황에서 벤치마킹을 수행할 수 있는 적절한 도구는 기능 추가에 따른 성능 희생을 만들지 않도록 도움을 준다.
1. Procedure 리스트 & 상태 보기
mysql> SHOW PROCEDURE STATUS WHERE Db = ‘디비 명’;
2. Procedure script(내용 보기)
mysql> use 디비 명
mysql> SHOW CREATE PROCEDURE 프로시저명 ;
다만 한글 주석이 안보일뿐..