Redis 4.0부터 unlink라는 Key 삭제 커맨드가 추가되었습니다.
기존에도 del 이라는 Key 삭제 커맨드가 있는데 차이점은 무엇일까요?
바로 unlink는 async 방식으로 background에서 별도의 Thread에 의해 처리된다는 점입니다.
(del 은 sync 방식으로 main thread에서 처리됨)
따라서 del 로 처리할 때 보다 훨씬 더 빨리 서비스에 영향 없이 처리할 수 있는데요
이번 글에서는 unlink 커맨드에 대해 알아보겠습니다.

* lazyfree-lazy-eviction yes  
=>maxmemory 까지 사용중일 때 maxmemory-policy 에 따라 key를 삭제하는 경우 del 대신 unlink 사용

* lazyfree-lazy-expire yes 
=> EXPIRE 커맨드로 TTL이 설정된 key를 삭제할 때 del 대신 unlink 사용

* lazyfree-lazy-server-del yes 
=> 기존에 있는 key를 set이나 rename 으로 엎어칠때 기존 값을 del 대신 unlink로 삭제함

* replica-lazy-flush yes 
=> replicaof 설정으로 master의 데이터를 처음 복제해올때 기존에 있던 값들을 del 대신 unlink로 삭제함

redis thread 확인

[root@e7900ebd4833 /]# ps -eLf | grep redis
root        19     1    19  0    4 04:59 ?        00:00:11 redis-server 0.0.0.0:6001
root        19     1    20  0    4 04:59 ?        00:00:00 redis-server 0.0.0.0:6001
root        19     1    21  0    4 04:59 ?        00:00:00 redis-server 0.0.0.0:6001
root        19     1    22  0    4 04:59 ?        00:00:00 redis-server 0.0.0.0:6001


(gdb) thread apply all bt

Thread 4 (Thread 0x7f2aa33ff700 (LWP 20)):
#0  0x00007f2aa401a9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x0000000000483d2c in bioProcessBackgroundJobs (arg=0x0) at bio.c:176
#2  0x00007f2aa4016e65 in start_thread () from /lib64/libpthread.so.0
#3  0x00007f2aa3d3f88d in clone () from /lib64/libc.so.6

Thread 3 (Thread 0x7f2aa2bfe700 (LWP 21)):
#0  0x00007f2aa401a9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x0000000000483d2c in bioProcessBackgroundJobs (arg=0x1) at bio.c:176
#2  0x00007f2aa4016e65 in start_thread () from /lib64/libpthread.so.0
#3  0x00007f2aa3d3f88d in clone () from /lib64/libc.so.6

Thread 2 (Thread 0x7f2aa23fd700 (LWP 22)):
#0  0x00007f2aa401a9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x0000000000483d2c in bioProcessBackgroundJobs (arg=0x2) at bio.c:176
#2  0x00007f2aa4016e65 in start_thread () from /lib64/libpthread.so.0
#3  0x00007f2aa3d3f88d in clone () from /lib64/libc.so.6

Thread 1 (Thread 0x7f2aa4b51f80 (LWP 19)):
#0  0x00007f2aa3d3fe63 in epoll_wait () from /lib64/libc.so.6
#1  0x000000000042addf in aeApiPoll (tvp=<optimized out>, eventLoop=0x7f2aa386a0a0) at ae_epoll.c:112
#2  aeProcessEvents (eventLoop=eventLoop@entry=0x7f2aa386a0a0, flags=flags@entry=11) at ae.c:411
#3  0x000000000042b2eb in aeMain (eventLoop=0x7f2aa386a0a0) at ae.c:501
#4  0x0000000000428190 in main (argc=<optimized out>, argv=0x7fffc06dc698) at server.c:4234
(gdb)

=> 한개의 process와 4개의 thread로 이루어져있음
위에서 Thread1은 main thread 이고
bioProcessBackgroundJobs 3개 thread는 아래와 같이 각각의 역할을 담당함

  • BIO_CLOSE_FILE : AOF rewrite 수행 시 기존 aof파일 close하는 역할
  • BIO_AOF_FSYNC : 1초마다 커맨드를 AOF에 로깅하는 역할
  • BIO_LAZY_FREE : unlink, async flushall, flushdb 수행하는 역할
  • rdb 관련 thread는 save,bgsave 수행 시 생겨남
    vi bio.c:176
    
    if (type == BIO_CLOSE_FILE) {
        close((long)job->arg1);
    } else if (type == BIO_AOF_FSYNC) {
        redis_fsync((long)job->arg1);
    } else if (type == BIO_LAZY_FREE) {

테스트 환경

# Memory
used_memory:841383360
used_memory_human:802.41M

# sentinel 구성
master0:name=mymaster,status=ok,address=172.16.0.12:6001,slaves=2,sentinels=3
127.0.0.1:7001> sentinel set mymaster down-after-milliseconds 10000

127.0.0.1:6001> eval "local i = tonumber(ARGV[1]);local res;math.randomseed(tonumber(ARGV[2]));while (i > 0) do res = redis.call('sadd',KEYS[1],math.random());i = i-1;end" 1  testset 10000000 2
127.0.0.1:6001> scard testset
(integer) 9976638
  • sentinel 구성이며 master가 1초이상 응답없을 시 failover 수행함
  • set type으로 약 1000000개의 member를 가진 key 준비
  • memory는 800MB 사용 중

del

127.0.0.1:6001> scard testset
(integer) 9976638
127.0.0.1:6001> del testset

(integer) 1
(67.94s)

# Memory
used_memory:869160
used_memory_human:848.79K

=> del testset 수행하는데 67초나 소요됨
sync방식으로 동작해서 del 동시에 메모리도 회수됨

# Replication
role:slave
master_host:172.16.0.11

127.0.0.1:6001> scard testset
(integer) 9976638

=> del 작업은 main thread에서 수행되기 때문에 del 작업동안 모든 작업이 block됨
sentinel 구성에서 del 작업이 오래 수행되어 down-after-milliseconds를 넘기는 경우
위와 같이 failover도 수행되고 del 작업도 실패하게됨

127.0.0.1:6001> scard testset
(integer) 9976638
127.0.0.1:6001> unlink testset
(integer) 1

# Memory
used_memory:573013768
used_memory_human:546.47M
.
.
# Memory
used_memory:223346032
used_memory_human:213.00M
.
.# Memory
used_memory:869160
used_memory_human:848.79K

127.0.0.1:6001> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.16.0.10,port=6001,state=online,offset=530077384,lag=0
slave1:ip=172.16.0.12,port=6001,state=online,offset=530077384,lag=1

=> del과 달리 다른 작업 block 없이 순식간에 데이터 삭제가능
async방식이라 unlink 수행 후 memory가 바로 회수되지는 않음

참고 문서

http://antirez.com/news/93