프로젝트 진행 목표 및 과정

멘토님께서 Redis라는 큰 오픈소스의 이슈를 해결하기에는 4주라는 기간은 너무 짧다고 판단하여서, 해당 기간 동안 Redis에 대한 학습에 집중하기로 결정하셨습니다.

 

우선, 프로젝트 시작 전에 2주 동안은 오픈소스 커뮤니티에서 제공하는 유튜브 강의를 시청하여 git에 대한 기본적인 이해를 정리하는 시간을 가졌습니다. Git은 협업 및 버전 관리를 위해 필수적인 도구이며, 오픈소스 프로젝트에 기여할 때 필수적인 요소입니다. 따라서, 기본적인 Git의 개념과 사용법을 숙지하고자 이러한 학습을 진행했습니다.

 

다음으로, 나머지 4주 동안은 Redis에 대한 깊은 학습에 집중하였습니다. 이 기간 동안에는 Redis의 주요 기능과 용도를 학습하고, 실제로 어떻게 사용되는지에 대한 심도 있는 이해를 갖도록 노력하고 있습니다.

또한, Redis의 소스 코드를 분석하고 멘토님께서 주시는 과제를 수행하면서 레디스의 코드를 실제 수정하고 디벨롭하면서 실전 경험을 쌓고있습니다. 이를 통해 오픈소스 프로젝트에 기여하는 데 필요한 기술과 지식을 획득하고, 더 나아가 Redis와 관련된 이슈를 해결하는 데 기여할 수 있는 역량을 키우는 것이 목표입니다.

이러한 과정을 통해 Redis에 대한 전문 지식을 쌓고, 오픈소스에 기여할 수 있는 능력을 키우는 것이 이 프로젝트의 목표입니다. 더불어, Git을 활용한 협업 및 버전 관리에 대한 이해도 함께 높이고자 합니다. 이를 통해 프로젝트 진행 과정에서 필요한 기술과 역량을 갖추어, 효과적으로 오픈소스 프로젝트에 기여할 수 있는 개발자로 성장하는 것이 최종 목표입니다.

온⸱오프라인 모임을 통한 기여 및 활동 내역

Redis의 개념을 잘 모르기에 멘토님이 말씀해주시는 것을 기록하고 그 이외의 파생해서 조금 더 공부가 필요한 것은 따로 공부해서 정리하였습니다.

레디스 캐싱전략

💡
2주차 온라인 미팅: https://dev-hiro.tistory.com/13
NoSQL: https://dev-hiro.tistory.com/10
과제3: https://dev-hiro.tistory.com/9
과제3 해설: https://dev-hiro.tistory.com/11
과제4: https://dev-hiro.tistory.com/14

활동소감

이 강의를 통해 DB와 같은 개념은 이론적으로만 알고 있었던 저에게 실제 코드 레벨에서의 이해와 적용 방법을 배울 수 있어서 매우 유익했습니다.

 

레디스는 많은 기업에서 사용되는 인메모리 데이터 구조 저장소로서, 그 유연성과 뛰어난 성능으로 유명합니다. 그런데도 실제로 그 내부 동작 메커니즘을 알고 코드 레벨에서 어떻게 활용할 수 있는지에 대해 배우는 것은 새로운 경험이었습니다. 이를 통해 레디스가 어떻게 작동하는지에 대한 이해가 더 깊어졌고, 이를 통해 개발 프로젝트에서 레디스를 보다 효율적으로 활용할 수 있을 것 같습니다.

 

특히, 이러한 강의를 통해 오픈소스에 대한 기여에 대한 관심이 더 커졌습니다. 다음에는 Redis에 대해 더 깊게 공부하고, 커뮤니티에 기여하는 것이 목표입니다. 오픈소스 프로젝트에 참여함으로써 다른 사람들과 지식을 공유하고 성장할 수 있는 기회를 얻을 수 있을 것으로 기대됩니다.

또한, 이를 통해 제 개인적인 기술 스택을 향상시키고, 전반적인 개발 커리어에 도움이 될 것으로 기대됩니다.

앞으로 Redis를 더 깊게 공부하고 기여하는 과정에서 어려움이 있을지도 모르지만, 그 과정에서 더 많은 것을 배우고 성장할 수 있는 기회라는 것을 인식하고 있습니다. 다양한 프로젝트와 커뮤니티에서의 경험을 통해 개발자로서의 역량을 향상시키고, 다른 이들과 함께 협업하며 성공을 이끌어 나갈 수 있기를 기대합니다.

 

이러한 강의를 열어주신 Open Up에 감사드리며, Redis 강사님이신 강대명 멘토님께도 감사드립니다.

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

과제 4 설명


echo2 abc라고 보내면 abc가 응답이 아닌 echo2_abc가 응답으로 오도록

  • bulk 단위
    • Respv2 기준 하나의 명령을 나타내는 block
    • ex) $4\r\npong\r\n
  • echo abc 라고 명령을 입력했을 때
    • c → argc = 2 =>명령어의 길이
    • c → argv[0] = “echo” => 첫번째 명령어
    • c → argb[1] = “abc” => 두번째 명령어

robj(server object)

  • type: 4bit
    • 2^4 ⇒ 16개의 타입 가능
  • encoding: 4bit
  • lru: 24bit
    • lru, lfu 쓸때 모두 사용.
  • type + encoding + lru 총 32bit/4byte
  • refcount: 똑같은거 시키면 +1, 0이 되면 지워짐
  • void * ptr ⇒ 8byte
  • 총 16byte의 메모리 공간을 할당 받게됨.

argc, argv가 만들어지는 곳

  • processMultibulk…에서 생성
  • createStringObject() 또는 createObject()에서 만듦

SDS

이전 포스팅에서도 작성했지만 한번 더 작성.

  • 서버 시스템에서는 메모리를 아끼기 위해.
  • 동적으로 길이를 사용하기에 변수에 따라서 스트링의 길이를 조절하기 위해.
  • redis에서 사용하고 있는 구조체
  • sds의 사이즈를 가변적으로 가르키기위해.

c에서의 메모리의 값을 읽는 스킬

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct memory_header {
    unsigned long size;
    char ptr[];
}memheader;

char *STRING = "hello, OSSCA!";
void test(char *ptr) {
    printf("%s\n", ptr);
    memheader *mh = (memheader *)(ptr-sizeof(memheader));
    printf("size: %d\n", mh->size);
}

int main(int argc, char *argv[]) {
    printf("%d\n", sizeof(memheader));

    memheader *mh = (memheader *)malloc(sizeof(memheader) + 16);
    mh->size = strlen(STRING);
    memcpy(mh->ptr, STRING, mh->size);
    mh->ptr[mh->size] = 0;

    test(mh->ptr);
    return 0;
}
8
hello, OSSCA!
size: 13
  • 뒷 부분만 전달했음에도 size를 읽어올 수 있음.
  • 메모리를 다루는 언어인 C라서 가능한 스킬.
  • sdsHdrSize()는 실제 메모리의 사이즈를 알려주는데, 이를 통해서 값을 가져올 수 있음.

과제 4 수행


실제 과제 풀이 해답은 여러개가 있을 수 있다.

  1. robj를 변경하는 방법
  2. addreplyBulkSds()를 사용하는 방법
  3. addReplySds()를 사용하는 방법

이 있는데 2, 3번에 대해서만 보이면

addReplySds()를 사용

void echo2Command(client *c) {
    sds reply = sdscatfmt(sdsempty(), "echo2_%s", c->argv[1]->ptr);
    addReplyLongLongWithPrefix(c, sdslen(reply), '$');
    addReplySds(c, reply);
    addReplyProto(c, "\r\n", 2);
}
  • client 객체로부터 받은 명령어를 sdscatfmt()를 통해 새로운 문자열을 생성.
    • sdsempty()
      • sds 타입의 새로운 빈 문자열을 생성하는 함수.
      • sdsnewlen()을 통해 sds를 새로 생성
    • sdscatfmt()
      • sds 타입의 변수에 서식 지정된 문자열을 추가하는 함수.
      • 형식 지정자(format specifier)를 사용하여 문자열을 생성하고, 해당 문자열을 SDS에 추가합니다. 서식 지정자는 %s, %d, %f 등과 같이 다양한 형태로 사용될 수 있으며, 이를 통해 다양한 타입의 데이터를 문자열로 변환하여 SDS에 추가 가능.
    • addReplyLongLongWithPrefix()
      • Redis 클라이언트에게 정수 값을 포함하는 응답을 보내느 함수.
      • 주어진 정수 값(sds의 길이)이 작을 경우 프로토콜에서 사용되는 header를 공유 객체로 사용.
        • 이렇게 함으로써 메모리 절약.
        • 공유된 헤더를 사용함으로써 응답을 생성.
      • 생성된 응답을 클라이언트에게 전달하기 위해 addReplyProto() 함수를 호출
    • addReplySds()
      • Redis 클라이언트의 버퍼에 s를 추가하는 함수.
      • PrepareClientToWrite()
        • 클라이언트가 쓰기 작업을 준비할 수 있는 상태인지 확인.
      • _addReplyToBufferOrList()
        • flag와 type를 통해 client와의 상태를 확인.
        • _addReplyToBuffer() 함수를 호출하여 클라이언트의 응답 버퍼(client->buf)에 응답을 추가.
      • 이후 sds는 더이상 사용되지 않으므로 메모리를 해제하여 메모리 leak을 방지
    • addReplyProto()
      • Redis 클라이언트에게 프로토콜 형식의 응답을 보내는 역할. s와 len을 이용하여 클라이언트의 버퍼에 응답을 추가.
      • 여기서는 명령어의 종료 역할.
      • PrepareClientToWrite() 실행.
      • _addReplyToBufferOrList() 실행.

다른 방법

/**
 * sdsnewlen을 통해 생성하는 방법
 */
sds reply = sdsnewlen("echo2_", 6); // "echo2_" 문자열을 포함한 SDS 생성
reply = sdscatfmt(reply, "%s", c->argv[1]->ptr); // 뒤에 인자 추가


/**
 * sdssetlen을 통해 생성하는 방법
 */
sds reply = sdsnewlen(NULL, 6); // 초기 크기가 6인 빈 SDS 생성
memcpy(reply, "echo2_", 6); // "echo2_" 문자열 복사
sdssetlen(reply, 6); // SDS 길이 설정
reply = sdscatfmt(reply, "%s", c->argv[1]->ptr); // 뒤에 인자 추가

이외의 다른 방법으로도 가능하다.

addReplyBulkSds()를 사용

void echo2Command(client *c) {
    sds reply = sdscatfmt(sdsempty(), "echo2_%s", c->argv[1]->ptr);
    addReplyBulkSds(c, reply);   
}
  • 그 이후 생성된 새로운 문자열을 클라이언트에게 반환하게 위해 addReplyBulkSds()를 사용.

addReplyBulkSds()

/* Add sds to reply (takes ownership of sds and frees it) */
void addReplyBulkSds(client *c, sds s)  {
    addReplyLongLongWithPrefix(c,sdslen(s),'$');
    addReplySds(c,s);
    addReplyProto(c,"\r\n",2);
}
  • 실제 위의 구현한 방법과 동일한 flow로 진행된다.
127.0.0.1:6379> 
127.0.0.1:6379> echo 123
"123"
127.0.0.1:6379> echo2 123
"echo2_123"
127.0.0.1:6379>

원하는 대로 결과가 잘 출력된 것을 볼 수 있다.

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

Cache 적용 후 Select Query 비교


DML에 따른 캐시 성능 / 12시경 캐시 적용

  • 캐시는 얼마나 Hit 되는지가 중요한 요소
  • SELECT는 캐시 Hit 이후 평균 시간이 급격하게 감소
  • UPDATE는 캐시 Hit가 있을 수 없으므로 캐시 적용 전과 비슷.

CPU 사용량

  • 서비스에 따라 다르지만 SELECT가 DB에 도착하지 않으므로(캐시를 사용하므로) CPU 사용량이 줄어듦.
    • DB에 레플리카를 만들어서 SELECT
    • WRITE에는 다른 트릭을 적용해서 CPU 사용량을 줄일 수 있음.
💡 Performance Tool
gatling, locust, 엥그라이드, jmeter가 존재. 최근에는 gatling, locust가 많이 사용하는 추세

분산 캐시


  • 데이터가 많다면, 하나의 Cache Server가 아니라 여러 캐시 서버를 구성할 수 있음.
  • ex) Redis With Range, Redis with PreShard, Redis with Consistent Hashing

상황

데이터를 나누는 기준이 중요

  • 서버는 계속 늘어나기에 데이터를 나누는 기준이 필요
    • 데이터가 재분배가 계속되면 안좋음.

  • 전체를 찾는건 서버에 부하

 

1. 모듈러(%2, %3 ... %N)

  • 서버 추가시 데이터 재분배가 일어나야함.

2. Range로 구분하는 방법

  • 초창기에 들어오는 유저는 활동량이 많고 이벤트시 들어오는 유저는 활동량이 없음.
  • 활동적인 유저는 서버 1에만, 이벤트 시 들어오는 유저는 서버 2에만 들어가기에 서비스 분배에 부적합
  • 서버의 부하도가 다름.

 

3. Preshard

  • hash값으로 균등하게

 

 

 

Session Store


Session Store로 저장하는 법

  • Session Store(로그인 토큰 같은 정보 저장)로 Redis를 많이 사용
  • Session을 개별 서버나, 클러스터링이 아닌, 외부 스토리지(Redis)에 저장.
  • Session Store가 죽으면 정보가 사라짐.

Distribution Lock


  • 분산 락으로 동작
    • Optimistic Lock으로 동작
    • Key가 존재하면 대기하는 형태
      • 바로 실패로 구성할지, Spinlock 형태로 동작할 지(Redisson 구현체) 등 구현에 따라 다름.
  • setnx
    • 키(데이터)가 없을때만 쓸 수 있음.
    • 키가 있으면 실패
    • 락을 얻음.
    • 락을 풀기 위해 key를 지우면 됨.
  • t_string.c/setGenericCommand
    • found가 false일 때만 사용할 수 있음
void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
    long long milliseconds = 0; /* initialized to avoid any harmness warning */
    int found = 0;
    int setkey_flags = 0;

    if (expire && getExpireMillisecondsOrReply(c, expire, flags, unit, &milliseconds) != C_OK) {
        return;
    }

    if (flags & OBJ_SET_GET) {
        if (getGenericCommand(c) == C_ERR) return;
    }

    found = (lookupKeyWrite(c->db,key) != NULL);

    if ((flags & OBJ_SET_NX && found) ||
        (flags & OBJ_SET_XX && !found))
    {
        if (!(flags & OBJ_SET_GET)) {
            addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
        }
        return;
    }
    // ...
}
127.0.0.1:6379> setnx ossca 2024
(integer) 1
127.0.0.1:6379> setnx ossca 123
(integer) 0
127.0.0.1:6379> get ossca
"2024"
127.0.0.1:6379> set ossca 4/26
OK
127.0.0.1:6379> get ossca
"4/26"
  • 처음에는 key가 ossca인 데이터가 없으므로 성공적으로 만들어졌기에 1이 반환됨.
  • 두번째는 ossca라는 값이 있으므로 실패를 뜻하는 0이 반환됨.
  • get을 통해 2024 값을 잘 읽어오고
  • set은 lock과 관계 없이 설정이 된다.
  • 프로세스가 지워지면 ttl을 5초 걸어두어 그 전에 접속하면 된다. 그 후에는 key가 사라진다.

Leaderboard


  • Sorted Set(zset)이 score를 저장할 수 있기에 redis로 활용함.
    • score, key 순
    • zrange zkey1 0 -1 하면 오름차순으로 랭킹이 나옴
    • src/t_zset.c
      • double 형태로 인자를 받음
      • zslCreateNode()
      • 근사값이므로 값이 오차가 발생할 수 있음
127.0.0.1:6379> zadd zkey1 100 one
(integer) 1
127.0.0.1:6379> zadd zkey1 1000 two
(integer) 1
127.0.0.1:6379> zrange zkey1 0 -1
1) "one"
2) "two"
127.0.0.1:6379> zadd zkey1 500 three
(integer) 1
127.0.0.1:6379> zrange zkey1 0 -1
1) "one"
2) "three"
3) "two"
127.0.0.1:6379> zrange zkey1 0 -1 withscores
1) "one"
2) "100"
3) "three"
4) "500"
5) "two"
6) "1000"

 

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
2024년 4월 27일 온라인 강의 내용
  • 레디스의 echo 명령: 받은 내용 그대로 전달.
set a 123

/*
 * set a 123이라는 명령이 들어올 때..
 * argv[0]: set
 * argv[1]: a
 * argv[2]: 123
 * argc: 2
 */

Redis 실행 flow

  • ServerCommandArg
  • ServerCommand
  • generate-command-code.py
    • 해당 파일에서 make_cmd를 자동적으로 만들어줌.
    • comand_list에 들어가는 내용과 JSON을 읽어와서 명령어를 추가.
    • Document와 함수를 한번에 처리하기 위해 JSON을 추가하고 python을 실행하면 def 파일이 만들어짐.
  • server.c에서 실제 함수 추가
  • maketelnet 0 6379 접속

make_cmd

  • 받은거 전달. 풀어서 저장하는 것만.
  • 파라미터 함수들이 바뀌면 or 구조체가 바뀌면(순서마저도) 작업을 일일이 바꿔야 하므로 이러한 트릭을 사용.
  • CommandStruct 구조체의 생성자로 serverComandTable[]

populateCommandTable

  • 명령 테이블
  • server.c에 정의
  • serverCommandTable[]을 하나씩 돌면서 커맨드를 만들어서 populateCommanddicAdd server.comand라는 키로 저장
  • 데이터를 키로 찾아오는 것.

lookupCommand

  • 명령 함수 찾아옴.
  • networking.c
    • 레디스 telnet이라 썼을때
      • 한줄로 보낼 수도 있고
      • 여러 줄로 보낼 수도 있음
    • processMultiBulk
      • 함수가 끝나면 명령어가 만들어짐
      • processCommandAndResetClient 함수를 실행하면
      • processCommand 실행
      • lookupCommand 실행
      • lookupCommandLogic 실행
      • 명령이 들어있는 해시 테이블 반환.
      • 명령어를 찾으면 이제 실행.
    • processInlineBuffer
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

NoSQL을 제대로 알기 전에 RDBMS의 특징을 제대로 알고 가보자!

 

RDBMS의 특징

  • Column과 Row가 두개 이상의 테이블
  • Row: 정보를 나타냄
  • Column: 이름, 주소 등 특정한 유형의 정보를 정렬
  • 스키마: 테이블과 필드 타입 간의 관계
  • 관계형 데이터베이스에서 스키마는 정보를 추가하기 전에 명확하게 정의되어 있어야 함.
    • 테이블과 필드의 설계를 확실히하고 데이터를 저장해야함.
    • 데이터 중복을 최소화, 데이터 정합성을 맞춰야 함.
    • 데이터의 유연성이 떨어지는 상황도 발생하기도 함.
  • SQL
    • 데이터베이스 설계자가 관계형 데이터베이스를 설계하는데 사용하는 프로그래밍 언어.
    • 쿼리를 통해 데이터를 생성, 검색, 수정, 삭제 가능.

NoSQL


Not Only SQL
기존 RDBMS 방식을 탈피한 데이터베이스 기존의 관계형 DB가 가지고 있는 특성뿐만 아니라 다른 특성들을 부가적으로 지원. RDBMS가 가지고 있는 한계를 극복하기 위해 데이터 저장소의 새로운 형태로 수평적 확장성을 지님으로써 문서, 그래프, 키 값, 인 메모리, 검색을 포함하여 다양한 데이터 모델을 사용.

NoSQL 특징

  • RDBMS와 달리 데이터 간의 관계를 정의하지 않음.
  • RDBMS에 비해 훨씬 더 대용량의 데이터를 저장 가능.
  • 반정형화, 비정형 데이터에 적합.
  • 분산형 구조이기에 확장성이 뛰어남.
  • 고정되지 않은 테이블 스키마를 가짐.
  • ACID대신 Eventual Consistency를 허용한다.
💡 ACID
ACID는트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질을 가르키는 약어로
원자성(Atomicity), 일관성(Consistency), 독립성(Isolation), 지속성(Durability)이 존재.

💡 Eventual Consistency
다양한 기기에 분산 저장되어 있는 데이터를 Update할 경우 실시간으로 다른 기기에 전파하기에 어려운데
이를 조금 타협하여 최신은 아닐 수 있지만 Update 전까지는 최신의 데이터를 반환.

 

NoSQL 유형

  • Graph Store: 서로 연관된 그래프 형식의 데이터를 저장
    • Node와 Edge에 데이터를 저장
    • Node: 사람, 장소 및 사물에 대한 정보가 저장.
    • Edge: 노드 간의 관계에 대한 정보가 저장.
    • 소셜 네트워크, 사기 탐지, 권장 엔진 같은 패턴을 찾아보기 위해 관계를 상세히 검토해야하는 사례에 적합.
    • Neo4j와 JanusGraph
  • Wide Column Store: Row가 아닌 Column 형태로 데이터를 저장
    • 테이블, 행 및 동적 열에 데이터를 저장
    • 각 행이 동일한 열을 가질 필요가 없다는 점에서 관계형 데이터베이스에 비해 뛰어난 유연성을 제공.
    • 대량의 데이터를 저장해야할 때 적합.
    • 사물인터넷 데이터와 사용자 프로필 데이터를 저장하는데 사용
    • Cassandra, HBASE
  • Document Store: 비정형 대량 데이터를 저장
    • JSON 객체와 비슷한 문서에 데이터를 저장.
    • 각 문서에는 필드와 값의 쌍이 포함
    • 일반적으로 값은 String, Number, Boolean, Array, Object 등 다양한 유형이 가능
    • MongoDB
  • Key-Value Store: 메모리 기반으로 빠르게 데이터를 저장 및 읽기
    • Key-Value 데이터베이스는 각 항목에 키와 값이 포함되어 있는 보다 간단한 유형의 데이터베이스
    • Value은 보통 Key를 참조하는 방식으로만 검색이 가능
    • Key-Value 데이터베이스는 대량의 데이터를 저장해야 하지만 검색을 위해 복잡한 쿼리를 수행할 필요가 없는 사례에 적합.
    • 일반적으로 사용자 선호도 저장 또는 캐싱에 사용
    • Redis, DynanoDB

NoSQL 장점

  • 유연성: 스키마가 없기 때문에 유연한 데이터구조
  • 확장성: 데이터 분산이 용이하며 성능 향상을 위한 Scale-up, Scale-out이 가능
  • 고성능: 특정 데이터 모델 및 액세스 패턴에 대해 최적화 되어 관계형 DB를 통해 유사한 기능을 충족하려 할 때보다 뛰어난 성능
  • 고기능성: 각 데이터 모델에 맞춰 특별히 구축된 뛰어난 기능의 API와 데이터 유형을 제공

NoSQL 단점

  • 데이터 중복 가능: 데이터 변경시 모든 컬렉션에서 수행해야 함.
  • 스키마가 존재하지 않아 명확한 데이터 구조를 보장하지 못함.
  • 제품 지원을 받기 어려움.

사용 요령

  • NoSQL은 데이터의 구조를 알 수 없는 경우 사용하는 것이 좋다.
  • Update가 많이 이뤄지지 않는 시스템이 좋다
  • Scale-out이 가능하다는 장점이 있어 막대한 데이터를 저장해야하는 경우 적합하다.
  • 기존의 정형화된 데이터 뿐만 아니라 메신저 텍스트, 음성 등 반정형화, 비정형화된 데이터도 저장하고 다루어야 하는 경우
  • 대규모 데이터를 다루는데 해당 데이터의 특징이 자주 바뀌는 경우
  • 강력한 일관성보다 성능과 가용성이 더 중요한 서비스인 경우
  • 굳이 ACID 특징이 필요 없는 읽기 전용 데이터를 관리하는 경우

참조

'Database > Redis' 카테고리의 다른 글

[Redis] Redis 고급 및 캐싱전략  (0) 2024.05.16
[Redis] Redis Data Type  (0) 2024.05.16
[Redis] Redis 기본  (0) 2024.05.10
[OSSCA2024] Redis 과제 3  (0) 2024.04.25
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

과제 3

받은 내용을 그대로 다시 돌려주는 echo{영어이름} 명령을 만들어보자


과제 시작 전 배경지식..

Redis에 명령은 어디에 저장되어 있을까?

src/ 디렉토리 밑에 commands 시작하는 파일이 존재.

src/commands.h
src/commands.c
src/commands.def

 

추가: def 파일 정리

 

echo 명령은 어떻게 실행될까?

echo 명령어 확인

기본적으로 echo 명령은 받은 문자를 그대로 반환하는 명령이다. 

과정

// commands.h

// commands.c
#define MAKE_CMD(name,summary,complexity,since,doc_flags,replaced,deprecated,group,group_enum,history,num_history,tips,num_tips,function,arity,flags,acl,key_specs,key_specs_num,get_keys,numargs) name,summary,complexity,since,doc_flags,replaced,deprecated,group_enum,history,num_history,tips,num_tips,function,arity,flags,acl,key_specs,key_specs_num,get_keys,numargs
#define MAKE_ARG(name,type,key_spec_index,token,summary,since,flags,numsubargs,deprecated_since) name,type,key_spec_index,token,summary,since,flags,deprecated_since,numsubargs
#define COMMAND_STRUCT redisCommand
#define COMMAND_ARG redisCommandArg

// commands.def
{MAKE_CMD("echo","Returns the given string.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,ECHO_History,0,ECHO_Tips,0,echoCommand,2,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_CONNECTION,ECHO_Keyspecs,0,NULL,1),.args=ECHO_Args},

// server.h
void echoCommand(client *c);

//server.c
void echoCommand(client *c) {
    addReplyBulk(c,c->argv[1]);
}
  1. commands.h 파일에는 redis의 command에 대한 arg 타입들을 지정.
  2. commands.c 파일에는 함수타입을 지정.
  3. commands.def 파일에는 실제 함수를 지정하는 라인이 존재.
    1. echo 함수는 echoCommand를 실행.
  4. server.h 파일에는 echoCommand() 함수를 정의.
  5. server.c 파일에는 실제 echoCommand() 함수를 구현.

하는 프로세스로 동작을 하였다.

추가: 프로세스 정리

 

과제 수행

나의 이름과 좋아하는 숫자를 조합하여 echoHero17 명령어를 생성.

// commands.def
{MAKE_CMD("echoHERO17","Returns the given string.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,ECHO_History,0,ECHO_Tips,0,echoCommandHero17,2,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_CONNECTION,ECHO_Keyspecs,0,NULL,1),.args=ECHO_Args},

// server.h
void echoCommandHero17(client *c);

// server.c
void echoCommandHero17(client *c) {
    addReplyBulk(c, c->argv[1]);
}
  1. commands.def 파일에는 echoHERO17 명령어를 생성하고 echoHERO17은 echoCommandHero17()를 실행.
  2. server.h 파일에는 echoCommandHero17() 함수를 정의.
  3. server.c 파일에는 실제 echoCommandHero17() 함수를 구현.

실제 실행화면

다음과 같이 echoHERO17 명령어가 잘 실행되는 것을 볼 수 있다.

 

느낀점

레디스의 코드를 볼 수 있어서 좋았지만 아직 모르는 코드와 실행동작이 존재하였다. 과제 이후에도 블로그를 지속적으로 수정하면서 포스팅을 보안해 나가야겠다.

'Database > Redis' 카테고리의 다른 글

[Redis] Redis 고급 및 캐싱전략  (0) 2024.05.16
[Redis] Redis Data Type  (0) 2024.05.16
[Redis] Redis 기본  (0) 2024.05.10
[Redis] NoSQL이란  (0) 2024.04.30

+ Recent posts