Friday, April 29, 2016

RedisTroubleShooting

Redis 데몬의 쿼리 증가에 따른 응답 지연에 대한 원인 분석


문제 발생 서버 정보

Date

2013/11/29

Server

Physical server

OS 정보

CentOS 6.4 | x86_86 | 2.6.32-358.23.2.el6.x86_64

CPU 정보

Architecture:         x86_64
CPU op-mode(s):       32-bit, 64-bit
Byte Order:           Little Endian
CPU(s):               8
On-line CPU(s) list:  0-7
Thread(s) per core:   2
Core(s) per socket:   4
Socket(s):            1
NUMA node(s):         1
Vendor ID:            GenuineIntel
CPU family:           6
Model:                44
Stepping:             2
CPU MHz:              2399.951
BogoMIPS:             4799.90
Virtualization:       VT-x
L1d cache:            32K
L1i cache:            32K
L2 cache:             256K
L3 cache:             12288K
NUMA node0 CPU(s):    0-7

Memory 정보

49381524KB(47G)

문제 분석 필요 사항

  • Redis 데몬 설정
  • Hardware 구성
  • OS 커널 설정
  • Network 구성

문제의 현상

redis 데몬으로 쿼리 증가시 redis 데몬에서 쿼리에 대한 응답 지연 발생.

문제의 원인

  1. 해당 현상 발생시 물리적 자원 사용량은 한계점에 이르지 않았다고 판단됨.

  2. [1] 번의 배제로 OS, SW 상의 구성을 확인.

  3. Redis 에 질의되는 쿼리 값들을 확인 결과 특정쿼리(keys *)실행시에 다른 쿼리(get)들이 지연되는 현상 확인.

  4. [3] 번의 문제(redis 자체 설계 문제)는 현재 해당 문제 발생 시간과 관련 없음을 확인.

  5. Redis 데몬의 source 확인시 listen() 함수의 backlog 는 고정값 511 이지만 문제 발생시 처리에는 충분하다고 판단됨.
    $ vi redis/src/anet.c

    if (listen(s, 511) == -1) {
  6. 실제 SW 상의 backlog 가 적용 되었는지 커널 설정 확인 결과 한가지 설정(net.core.somaxconn)이 누락 된 것을 확인.(man 2 listen)

  7. [6] 번의 문제로 인하여 기본값(128)으로 backlog 설정이 적용되는 것을 커널 소스를 통해 확인.
    $ vi kernel/net/socket.c

    1470 SYSCALL_DEFINE2(listen, int, fd, int, backlog)
    1471 {
    1472     struct socket *sock;
    1473     int err, fput_needed;
    1474     int somaxconn;
    1475 
    1476     sock = sockfd_lookup_light(fd, &err, &fput_needed);
    1477     if (sock) {
    1478         somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
    1479         if ((unsigned)backlog > somaxconn)
    1480             backlog = somaxconn;
    1481 
    1482         err = security_socket_listen(sock, backlog);
    1483         if (!err)
    1484             err = sock->ops->listen(sock, backlog);
    1485 
    1486         fput_light(sock->file, fput_needed);
    1487     }
    1488     return err;
    1489 }
  8. [7] 번으로 kernel의 backlog 수가 너무 낮다는 결론 유추

문제의 재현

재현 코드 작성
$ vi redis_connect_thread.c

OS kernel backlog 감소후 테스트
$ redis_connect {redis_server} {requests} {thread}

문제의 해결

  1. Redis backlog 관련부분 source 수정후 재배포
    $ diff -urNp redis.org/src/anet.c redis/src/anet.c

    -    if (listen(s, 511) == -1) { /* the magic 511 constant is from nginx */
    +    if (listen(s, 30000) == -1) { /* the magic 511 constant is from nginx */
  2. OS kernel 설정 수정후 재배포
    $ diff -urNp /etc/sysctl.conf.org /etc/sysctl.conf

    + net.core.somaxconn  = 30000
  3. 모니터링 프로그램 코드 등록(slowlog get 등등 이용한 코드 작성)
    $ vi redis-slowlog-to-syslog.pl

  4. [1-3] 적용후 문제 해결 확인

기타

Linux kernel 버전 2.6.14 이후로 socket 함수에 NETLINK_INET_DIAG 옵션으로 소켓 모니터링이 가능하다. 그래서 listen 된 port 의 backlog 가 실시간 확인 가능하다. ss 명령어에 해당 기능이 구현 되어 있다.

$ vi iproute2-2.6.31/misc/ss.c

static int tcp_show_netlink(struct filter *f, FILE *dump_fp, int socktype)
.
.
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0)
.
.

Redis 다중 포트 사용시 MemoryCache Dump file 의 복구 문제


내용

재시작시 마지막 높은 포트의 메모리 DB로 복구되는 현상

원인

Redis의 메모리 Dump는 dbfilename 설정으로 dir 설정 위치에 저장되는데 dbfilename 이 겹치면 포트를 여러개로 올려도 마지막 높은포트 번호의 파일로 복구 되기 때문이다.

해결

해결법은 다수의 포트로 구동시 dbfilename 이름을 아래처럼 개별 포트 이름으로 구동 하면된다.

$ vi /etc/redis/reids-6379.conf

dbfilename dump-6379.rdb

$ vi /etc/redis/reids-6380.conf

dbfilename dump-6380.rdb

$ redis-cli

redis 127.0.0.1:6379> SET key "values"
OK
redis 127.0.0.1:6379> get key
"values"
redis 127.0.0.1:6379> 

$ service redis restart

Stopping redis-server with /etc/redis/redis-6379.conf:          [  OK  ]
Stopping redis-server with /etc/redis/redis-6380.conf:     [  OK  ]
Starting redis-server with /etc/redis/redis-6379.conf:          [  OK  ]
Starting redis-server with /etc/redis/redis-6380.conf:     [  OK  ]

$ redis-cli

redis 127.0.0.1:6379> get key
"values"
redis 127.0.0.1:6379> 

$ (echo -en "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379

+PONG
+PONG
+PONG


Post a Comment