프로젝트 진행 목표 및 과정

멘토님께서 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

이상 현상이 뭘까요?

이상현상은 데이터베이스의 테이블을 설계를 잘못하여 데이터를 가공할 때 데이터의 무결성이 깨지는 현상을 말합니다. 그 유형으로는 삽입이상, 갱신이상, 삭제이상으로 구성되어 있습니다.

 이상현상

  • 정규화를 거치지 않은 릴레이션에서 발생할 수 있는 이상 현상.
  • 데이터들이 불필요하게 중복.
  • 속성들의 종속관계를 하나의 릴레이션에 표현하기에 발생.
  • 삽입이상
    • 데이터 삽입 시 의도와 다른 값들도 삽입되는 것.
  • 삭제이상
    • 데이터 삭제 시 의도와 다른 값들도 연쇄 삭제되는 것.
  • 갱신이상
    • 데이터 갱신 시 일부 튜플만 갱신되어 모순 발생.
  • 데이터 정규화를 통해 해결.
    학번 이름 나이 성별 강의코드 강의명 전화번호
    1011 이태호 23 AC1 데이터베이스 개론 010-1234-5678
    1012 강민정 20 AC2 운영체제 010-5325-6913
    1013 김현수 21 AC3 자료구조 010-5830-3291
    1013 김현수 21 AC4 웹 프로그래밍 010-5830-3291
    1014 이병철 26 AC5 알고리즘 010-2348-5892

삽입 이상(Insertion Anomaly)에 대해서 설명해주세요.

삽입이상은 자료를 삽입할 때 의도하지 않은 자료까지 삽입해야만 자료를 테이블에 추가 가능한 현상을 말합니다. 위 테이블을 기준으로 강의를 아직 수강하지 않는 학생에게는 null값이 들어가야하는 문제가 생기는 경우를 의미합니다.

갱신 이상(Update Anomaly)에 대해서 설명해주세요.

갱신이상이란 중복된 데이터 중 일부 데이터만 수정되어 데이터 간의 일관성이 깨지는 현상을 의미합니다. 강의코드 AC3의 김현수의 전화번호를 변경할 때 1013의 김현수의 전화번호가 모두 바뀌어야 하는데 AC3의 데이터에 해당하는 김현수의 전화번호만 바뀌는 현상을 의미합니다.

삭제 이상(Deletion Anomaly)에 대해서 설명해주세요.

삭제이상이란 정보를 삭제할 때 의도하지 않은 정보까지 삭제되어 버리는 현상을 의미합니다. AC1인 데이터베이스 개론의 강의를 삭제하게 되면 이태호 학생의 데이터까지 삭제되는 현상이 발생합니다.

함수 종속성이 무엇인가요?

함수 종속성이란 속성들 간의 관련성을 말합니다. 이를 이용하여 이상현상을 예방할 수 있도록 릴레이션을 설계하는 과정이 정규화입니다.
    • 함수 종속성
      • 릴레이션의 속성 A와 B에 대해서 A값에 의해 B값이 유일하게 정해지는 관계
        (B는 A에 함수 종속이다. A → B, A는 결정자, B는 종속자)
      • 완전 함수 종속
        • 기본키를 구성하는 모든 속성에 종속되는 경우
      • 부분 함수 종속
        • 기본키를 구성하는 속성의 일부에 종속되거나, 기본키가 아닌 다른 속성에 종속되는 경우
      • 이행적 함수 종속
        • A→B, B→C가 종속관계일 때 A→C가 성립하는 경우
        학번 이름 학년 과목번호 성적
                 
      • 위와 같은 릴레이션이 있을 때(기본키: (학번, 과목번호)) 
      • 학생은 이름, 학번만 알아도 유일하게 결정.
      • 성적은 (학번, 과목번호)로 유일하게 결정
      • 학년과 이름은 (학번, 과목번호)에 대해 부분 함수 종속
      • 성적은 완전함수 종속

완전 함수적 종속은 뭔가요?

완전 함수적 종속은 X의 값이 Y의 값에 모두 포함되어 있는 것을 의미합니다

부분 함수적 종속은 뭔가요?

부분 함수적 종속은 X의 부분집합이 Y에 종속되어 있을 때를 의미합니다.

이행적 함수적 종속은 뭔가요?

이행적 함수적 종속은 함수 종속 관계가 X → Y, Y → Z일 때 논리적으로 X → Z가 성립될 때 Z가 X에 이행적으로 함수에 종속되었다고 합니다.
이름 성별 주소 지역
       
  • X(이름, 성별) → Y(주소)
  • Y(주소) → Z(지역)
  • X(이름, 성별) → Z(지역)
  • X → Z

정규화(Normalization)에 대해서 설명해주세요.

정규화는 데이터의 이상현상을 예방하고 효과적인 연산을 하기위한 기법으로, 릴레이션 간의 함수 종속성을 파악하여 기본키와 외래키를 이용하여 릴레이션을 효과적으로 설계하는 과정을 의미합니다.

 정규화

  • 속성간의 종속성으로 인해 발생하는 이상현상을 릴레이션 설계 시에 제약조건을 통해서 이상현상을 없애는 과정.
  • 정규형의 차수가 높아질수록 제약사항이 많아지고 엄격해지지만 이상현상의 발생 가능성이 떨어진다.
  • 릴레이션의 특성에 따라서 적합한 정규형을 선택해야함.

장점

  • 이상현상 해결
  • 속성의 구조 추가로 인해 DB 구조를 확장하는 경우, 구조 변경 최소화

제 1 정규형에 대해서 설명해주세요.

제 1정규형은 릴레이션의 모든 속성의 도메인이 원자 값만으로 구성되어 있어야한다는 특성으로 예를 들어~~

제 1 정규형

  • 제 1 정규형만 지킨다면 데이터 중복 이상현상이 발생할 수 있다.
  • 기본키에 완전 함수 종속되지 못한 칼럼이 있다면 갱신, 삭제, 삽입 이상이 일어날 가능성 존재.
    부분 함수적 종속을 제거하여 제 2 정규형을 만족하도록.
    학번 과목코드
    CS123 B31, A15
    CS345 N04
    학번 과목코드
    CS123 B31
    CS123 A15
    CS345 N04
  • 왼쪽 테이블의 과목코드의 칼럼이 원자 값이 아님.
  • 오른쪽 테이블로 변경해야함.

제 2 정규형에 대해서 설명해주세요.

제 2정규형은 릴레이션이 제1 정규형에 속하고, 기본키가 아닌 모든 속성이 기본키에 완전 함수 종속되어야 한다는 특성으로 예를 들어~~
학번 과목코드 성적 강의실
CS123 B31 80 1공 502
CS123 A15 90 1공 503
CS345 N04 87 클러스터 402

 

  • 테이블의 기본키: (학번, 과목코드)
  • (학번, 과목코드) -> 성적
  • (과목코드) -> 강의실
학번 과목코드 성적
CS123 B31 80
CS123 A15 90
CS345 N04 87
과목코드 강의실
B31 1공 502
A15 1공 503
N04 클러스터 402
  • 테이블을 분리하여 제 2정규형 만족

제 2 정규형

  • 이행적 함수 종속이 존재하면 갱신이상이 가능.
    이행적 함수 종속을 제거하여 제 3정규형을 만족하도록

제 3 정규형에 대해서 설명해주세요.

제 3 정규형은 릴레이션이 제 2 정규형에 속하고, 모든 속성이 기본키에 이행적 함수 종속되지 않아야한다는 특성으로 예를 들어~~
학번 과목코드 수강료
CS123 B31 70000
CS123 A15 45000
CS345 N04 70000

 

  • 기본키: 학번
  • 학번 -> 과목코드
  • 과목코드 -> 수강료
  • 학번 -> 과목코드
학번 과목코드
CS123 B31
CS123 A15
CS345 N04
과목코드 수강료
B31 70000
A15 45000
N04 70000
  • 학번 -> 과목코드
  • 과목코드 -> 수강료 분리
  • 이를 통해 갱신 이상 해결 가능

BCNF 정규형에 대해서 설명해주세요.

BCNF 정규형은 제 3 정규화를 진행한 테이블에 대해 모든 결정자가 후보키가 되도록 테이블을 분해하는 것으로
학번 과목명 담당교수
100 C123 P1
100 C234 P2
200 C123 P1
300 C234 P3
400 C234 P4
 
  • 3 정규형은 만족하지만 다음과 같은 이상 현상 가능
    • 삽입이상: 새로운 교수가 특정 과목을 담당한다는 새로운 정보를 추가할 수 없다. 적어도 한 명 이상의 수강 학생이 필요하다.
    • 갱신이상: 학번 100이 C234 과목을 취소하면, P2가 C234 과목을 담당한다는 정보도 삭제된다.
    • 삭제이상: 삭제 이상 : - 갱신 이상 : P1의 과목이 변경되면 P1인 행을 모두 찾아 변경시켜주어야 한다.
  • 원인은 결정자(Determinant)가 후보키(Alternative Key)로 취급되고 있지 않기 때문이다.
  • 후보키는 슈퍼키(super key) 중에서 최소성을 갖는 키이므로 이 릴레이션에서는 (학번, 과목명)이나 (학번, 담당교수)가 후보키가 된다.
  • 담당 교수만으로는 후보키가 될 수 없다. 하지만, 후보키가 아님에도 과목명을 결정할 수 있기 때문에 담당 교수는 결정자에 속한다. 
    학번 담당교수
    100 P1
    100 P2
    200 P1
    300 P3
    400 P4
    담당교수 과목명
    P1 C123
    P2 C234
    P3 C234
    P4 C234

반정규화에 대해서 설명해주세요.

반정규화는 정규화를 통해 릴레이션을 디자인할 시 릴레이션의 분해나, 데이터베이스의 성능이 떨어지는 결과를 초래할 수 있습니다. 따라서 이러한 경우를 예방하기 위한 기술로 종류로는 테이블 통합/분할/추가/중복 속성 추가등이 있습니다. 반정규화 과정을 거치게 되면 데이터의 일관성이나 무결성이 보장되지 않을 수 있기에 개발자가 데이터 관리를 더욱 신경써야합니다.

 반정규화

  • 시스템의 성능 향상을 위해 정규화된 데이터 모델을 통합하는 작업
  • 종류
    • 테이블 통합
    • 테이블 분할
    • 테이블 추가
    • 중복 속성 추가
  • 시스템의 성능 향상
  • 데이터의 일관성이나 무결성 보장 불가능
    • 검색기능 향상
    • 갱신, 삭제 성능 낮아짐
  • 대상
    • 수행 속도가 많이 느린 경우
    • 테이블의 JOIN연산을 지나치게 사용하여 데이터를 조회하는 것이 어려운 경우
    • 테이블에 많은 데이터가 있고, 다량의 범위 혹은 특정 범위를 자주 처리해야하는 경우.

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

[JSCode] Database 면접 스터디 회고록  (1) 2023.12.08
[JSCode] Database Week5  (2) 2023.12.05
[JSCode] Database Week3  (0) 2023.11.21
[JSCode] Database Week2  (1) 2023.11.14
[JSCode] Database Week1  (0) 2023.11.07

Week2

SQL에 대해서 설명해주세요. C언어와 같은 프로그래밍 언어와 어떤차이가 있나요?

SQL은 구조화 질의어로 관계형 데이터베이스 관리 시스템의 데이터를 관리하기 위해 설계된 언어입니다. 이는 자료 검색과 관리, 스키마 생성과 수정 등의 기능이 있고, 데이터 정의 언어인 DDL, 데이터 조작 언어인 DML, 데이터 제어 언어 DCL이 있습니다.

C나 자바와 같은 일반 프로그래밍 언어는 절차적 언어로 특징은 프로그래밍 순서대로 로직이 처리되는 것인데 반해,
SQL은 집합적 언어로 집합적 언어는 데이터를 특정 집합 단위로 분류해 이 단위별로 한 번에 처리하는 언어 입니다. 또 SQL은 어떤 DBMS를 쓰느냐에 따라서 문법이 조금씩 다릅니다.
  • SQL은 데이터베이스의 데이터와 메타 데이터를 처리하고 생성하는 문법만 가지고 있어서 데이터 부속어라고 부릅니다.
  • SQL은 비절차적(선언적) 언어이므로 찾는 데이터만 기술하고 처리과정을 기술할 필요가 없습니다. 하지만 C, JAVA와 같은 프로그래밍 언어는 절차적 언어이기 때문에 개발자가 처리절차를 일일이 기술해야 합니다.
  • SQL은 입력과 출력이 모두 테이블이지만, 일반 프로그래밍 언어의 경우에는 모든 형태의 입출력이 가능합니다.

개발자가 작성한 SQL이 어떤 과정을 통해 실행 되는지 설명해주세요.

SQL이 문법적으로 틀리지 않았는 지 확인하는 구문분석 후 필요 없는 부분들이 제거되어 표준화된 쿼리 트리가 만들어 집니다. 그 이후 통계나 조각 정보를 바탕으로 실행 계획을 만듭니다. 컴파일을 하면 이진코드가 생성되고, 액세스 루틴으로 가서 실제 처리를 하고 결과를 돌려줍니다.
  1. 구문 분석(Parsing)
    1. 해당 쿼리가 문법적으로 틀리지 않은지 확인
    2. 해당 구문을 SQL 서버가 이해할 수 있는 단위들로 분해하는 과정
    3. 구문이 부정확할 시에는 처리를 중단
      1. Batch abort: Batch중 하나라도 syntax error가 있다면 전체 batch가 실행되지 않는다.
  2. 표준화(Standardization)
    1. 실제로 필요없는 부분들이 제거되어 표준화된 쿼리 트리 생성
  3. 최적화(Optimization)
    1. 통계나 조각 정보를 바탕으로 실행 계획 생성.
    2. 쿼리 처리에서 매우 중요한 단계.
      1. 쿼리 분석: 검색 제한자(SARG)인지 조인 조건인지 판단
         WHERE PRICE BETWEEN 10 AND 20
         WHERE PRICE >= 10 AND PRICE <= 20
         // 1행을 실행하면 옵티마이저가 2행으로 변경
        
         WHERE NAME LIKE '%영웅'
         // 위 행은 검색 제한자가 아님.
💡 검색 제한자(Search argument)
옵티마이저가 인덱스를 선택할 수 있도록 해주는 WHERE 절의 검색조건 검색 제한자가 있더라도 선택도가 나쁘면 인덱스 사용하지 않음.
      1. 인덱스 선택: 분포 통계 정보를 이용하여 인덱스 검색이나 테이블 스캔 중의 하나를 선택. 여러 인덱스 중 가장 효율적인 인덱스를 선택
      2. 조인처리: JOIN, UNION, GROUP By, ORDER BY 절을 가지고 있는지 확인하여 적절한 작업 순서를 선택
      3. 이 단계의 출력은 실행 계획.
  1. 컴파일(Compilation)
    1. 컴파일을 하면 이진 코드가 생성됩니다. 일반적인 경우, 컴파일 이후 .exe, .dll 등의 이진 파일이 만들어지는데, SQL 서버에서는 메모리(프로시저 캐시)에만 올리기 때문에 컴파일 속도가 매우 빠릅니다.
💡 프로시저 캐시
SQL Server가 컴파일된 Query Plan이 저장되는 메모리 공간으로 Query Plan이 재사용 될 수 있도록 저장함으로써 complie하는 비용을 최소화하기 위해 사용됨.

실행(Execution)

  1. 액세스 루틴으로 가서 실제 처리를 하고 결과를 돌려준다.
💡 쿼리 트리
A tree data structure that corresponds to a relational algebra expression. It represents the input relations of the query as leaf nodes of the tree, and represents the relational algebra operations as internal nodes.

 

DML은 무엇인가요? 어떤 구문이 있는지도 설명해주세요.

DML은 데이터 조작 언어로써, 데이터베이스에 대한 데이터 검색, 등록, 삭제, 갱신을 위한 데이터베이스 언어입니다. DML은 즉시 실행되지만 롤백문으로 취소할 수 있습니다.

데이터의 선택하는 SELECT, 테이블에 데이터를 추가하는 INSERT, 테이블의 데이터를 삭제하는 DELETE, 값을 업데이트하는 UPDATE과 같은 구문이 있습니다.
keyword description
SELECT DB의 데이터를 조회하거나 검색.
INSERT DB의 새로운 데이터를 삽입.
DELETE 테이블에서 데이터를 삭제
UPDATE 테이블의 데이터를 수정
SELECT * FROM;
INSERT INTO 고객(ID, 이름, 주소) VALUES (1, 'John Smith', '123 Main St');
고객 UPDATE SET 주소 = '456 Park Ave' WHERE id = 1;
DELETE FROM WHERE id = 1;
  • 선언적 데이터 조작 언어
    • 사용자가 무슨 데이터를 원하는 지만 명세하는 언어
  • 절차적 데이터 조작 언어
    • 사용자가 무슨 데이터를 원하며 어떻게 접근해야하는지 명세하는 언어

DDL은 무엇인가요? 어떤 구문이 있는지도 설명해주세요.

데이터 정의 언어로써, 데이터베이스의 테이블과 인덱스 구조를 정의하는 언어입니다. DDL은 즉시 실행되고 영구적입니다. 따라서 객체가 생성, 변경 또는 삭제가 되는데에 있어 롤백을 할 수 없어 DDL을 실행 전에 DB 백업이 있는지 확인하는 것이 중요하며, 이러한 역할을 하는데 있어서 권한이 부여가 되어 있어야 합니다.

테이블을 생성하는 CREATE, 테이블의 구조 또는 컬럼을 변경하는 ALTER, RENAME, 데이터 베이스의 객체를 삭제하는 DROP, TRUNCATE과 같은 구문이 있습니다.
keyword description
CREATE 테이블, 뷰 또는 인덱스와 같은 새 데이터 베이스 개체를 생성.
ALTER 기존 DB 개체를 수정.
DROP DB 테이블을 를 삭제.
TRUNCATE 테이블의 모든 행을 삭제. DROP문과 달리 테이블 구조와 인덱스는 그대로 유지
RENAME DB 개체의 이름을 바꾸는데 사용

 

  CREATE TABLE 고객( id INT PRIMARY KEY, name VARCHAR(255), address VARCHAR(255));
  ALTER TABLE 고객 이메일 ADD VARCHAR(255);
  DROP TABLE 고객;
  RENAME TABLE TO 클라이언트로;

DDL과 DML의 차이.

  • DDL은 데이터베이스 개체를 생성, 변경 및 삭제.
  • DML은 데이터베이스의 데이터를 조작하는데 사용.

DCL은 무엇인가요? 어떤 구문이 있는지도 설명해주세요.

DCL은 데이터 제어 언어로써, 데이터베이스에서 데이터에 대한 액세스를 제어하기 위한 데이터베이스 언어입니다. 이를 통해 악의적인 사용자부터 데이터를 보안할 수 있으며 무결성 및 시스템 장애에 대한 회복을 가능하게 해줍니다. 

권한을 부여하고 박탈하는 GRANT, REVOKE과 같은 구문이 있습니다.
keyword description
GRANT 권한 부여
REVOKE 권한 박탈

 

참조 무결성에 대해서 설명해주세요.

참조 무결성은 관계 데이터베이스 관계 모델에서 2개의 관련있던 관계 변수 간의 일관성(데이터 무결성)을 말합니다. 하나의 속성이 다른 테이블의 속성을 참조하고 있다면, 참조한 해당 속성이 존재해야 합니다.

이를 통해 기본키와 외래키 간의 관계가 항상 유효하도록 관리합니다. 기본키를 참조하는 외래키가 있다면, 해당 기본키는 수정과 삭제가 불가능합니다.

무결성

  • 데이터베이스에 저장된 값들에 대하여 여러가지 제한을 통해 데이터의 신뢰를 보장하게 하여 일관성을 유지시켜주는 것.
  1. 도메인 무결성
    1. 한 칼럼에 대하여 NULL 허용 여부와 타당한 데이터 값들을 지정.
    2. data type이나 규칙과 제약, 값 범위등을 제한.
      1. 대학생의 학년 도메인이 1, 2, 3, 4 일때 9라는 값을 가질 수 없음.
  2. 참조 무결성
    1. 기본키와 참조키 간의 관계가 항상 유지됨을 보장.
    2. 참조되는 테이블의 행을 이를 참조하는 참조키가 존재하는 한 삭제될 수 없고, 기본키도 변경될 수 없음.
    3. 조건
      1. 기본 테이블에서 사용한 필드는 기본키이거나 고유 인덱스가 설정되어 있어야함.
      2. 기본 테이블과 관계 테이블 둘다 액세스 테이블이여야함.
      3. 관계를 설정하는 테이블은 형식이 같아야함.
  3. 개체 무결성
    1. 테이블에 있는 모든 행들이 유일한 식별자를 가져야함.

CASCADE 설정에 대해서 설명해주세요.

참조 무결성을 지키기 위해 데이터베이스의 값을 수정 또는 삭제할 때, 해당 값을 참조하고 있는 레코드 역시 종속적으로 수정 또는 삭제를 가능하게 하는 조건입니다.

ON UPDATE CASCADE 옵션을 활용하면 기본키의 값을 수정할 수 있습니다.
ON DELETE CASCADE 옵션을 활용하면 해당 값을 참조 중인 레코드를 삭제할 수 있습니다.

제약조건

  • NOT NULL
    • 속성은 기본적으로 NULL값을 가질 수 있음.
    • NOT NULL 조건을 통해 이 속성은 NULL 값을 명시적으로 제어할 수 있음. 
    • 💡 NULL은 알 수 없는 데이터를 표현.
CREATE TABLE PERSON ( 
	ID INT NOT NULL, 
    NAME VARCHAR (20) NOT NULL, 
    AGE INT NOT NULL, 
    ADDRESS CHAR (25), 
    PRIMARY KEY (ID) 
);
  • DEFAULT
    • INSERT INTO 문에서 구체적인 값을 입력하지 않은 열에 기본 값을 부여.
CREATE TABLE PERSON ( 
	ID INT NOT NULL, 
    NAME VARCHAR (20) NOT NULL, 
    AGE INT NOT NULL, 
    ADDRESS CHAR (25) DEFAULT("대한민국 서울특별시 종로구"), 
    PRIMARY KEY (ID) 
);

  • UNIQUE
    • 서로 다른 두 레코드의 특정한 열이 동일한 값을 가지는 것을 방지.
CREATE TABLE PERSON (
       ID       INT            NOT NULL,
       NAME     VARCHAR (20)   NOT NULL UNIQUE,
       AGE      INT            NOT NULL,
       ADDRESS  CHAR (25),
       PRIMARY KEY (ID)
);

  • PRIMARY KEY
    • 테이블에 레코드를 고유하게 식별하는 테이블의 필드 값 설정.
    • 반드시 고유해야하며(UNIQUE), NULL 값을 가질 수 없음.
    • 단일 필드 외에 여러 필드가 하나의 기본키를 가질 수 있음.

CREATE TABLE PERSON (
       ID       INT            NOT NULL,
       NAME     VARCHAR (20)   NOT NULL UNIQUE,
       AGE      INT            NOT NULL,
       ADDRESS  CHAR (25),
       PRIMARY KEY (ID)
);

  • FOREIGN KEY
    • 두 테이블의 관계설정을 위한 키
    • 다른 테이블의 기본키와 일치하는 값을 가지는 하나의 컬럼 혹은 여러 컬럼
CREATE TABLE PERSON (
       ID       INT            NOT NULL,
       NAME     VARCHAR (20)   NOT NULL UNIQUE,
       AGE      INT            NOT NULL,
       ADDRESS  CHAR (25),
       PRIMARY KEY (ID)
			 ALTER TABLE ORDERS
         ADD FOREIGN KEY(Customer_ID) REFERENCES CUSTOMERS(ID);
);

CREATE TABLE ORDERS(
       ID          INT         NOT NULL,
       DATE        DATETIME, 
       CUSTOMER_ID INT references CUSTOMERS(ID),
       AMOUNT      double,
       PRIMARY KEY(ID)
);

  • CHECK
    • 레코드에 입력되는 값에 조건을 걸 수 있음.

CREATE TABLE PERSON (
       ID       INT            NOT NULL,
       NAME     VARCHAR (20)   NOT NULL UNIQUE,
       AGE      INT            NOT NULL CHECK(AGE >= 20),
       ADDRESS  CHAR (25),
       PRIMARY KEY (ID)
);

  • INDEX
    • 데이터베이스로부터 매우 빠르게 데이터를 생성하고 검색하기 위해 사용.
    • 테이블의 단일 컬럼 또는 컬럼의 그룹을 사용함으로써 생성.
    • 데이터를 정렬하기 전에 각 행마다 ROW ID 할당.
CREATE TABLE PERSON (
       ID       INT            NOT NULL,
       NAME     VARCHAR (20)   NOT NULL UNIQUE,
       AGE      INT            NOT NULL,
       ADDRESS  CHAR (25),
       PRIMARY KEY (ID)
);
CREATE INDEX idx_age ON CUSTOMERS(AGE);

VIEW에 대해서 설명해주세요.

가상의 테이블로 정의하여 실제 테이블로 사용할 수 있도록 만든 개체입니다.
미리 정의된 뷰를 일반 테이블처럼 사용할 수 있기에 편리하고, 재사용이 가능하며 메모리를 차지하지 않습니다.
하지만 SELECT 문을 제외한 일부 물리적인 테이블의 갱신작업을 수행하는데 제약이 있습니다.
  • 기본 테이블디스크 공간에 할당. 데이터 딕셔너리 테이블에 뷰에 대한 정의만 저장. 디스크 저장공간 X
  • 무결성 제약조건 그대로 유지.
  • 기존 테이블이 삭제되면 뷰도 자동적으로 제거.
  • 한번 정의한 뷰는 변경할 수 없으며 삭제한 후 다시 생성해야한다.

뷰 필요성

  • 사용자에 따라 특정 객체만 조회할 수 있도록 할 필요가 있음. (모든 직원에 대한 정보를 모든 사원이 보면 안됨.)
  • 복잡한 질의문을 단순화
  • 데이터의 중복성을 최소화(판매부에 속한 사원들마을 사원테이블에서 찾아서 다른 테이블로 만들면 중복성이 발생.)

장단점

  • 장점
    • 논리적 독립성 제공
    • 데이터 접근 제어(보안)
    • 사용자의 데이터 관리 단순화
    • 여러 사용자의 다양한 데이터 요구 지원
  • 단점
    • 뷰의 정의 변경 불가
    • 삽입, 삭제, 갱신 연산에 제한
    create view <viewName> as <SQL Sentence>
     

SELECT 절의 처리순서에 대해서 설명해주세요.

FROM(+JOIN) -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
  1. FROM
    • 각 테이블을 확인
  2. ON
    • JOIN 조건을 확인
  3. JOIN
    • JOIN이 실행되어 데이터가 SET으로 모아지게 된다. 서브 쿼리도 함께 포함되어 임시 테이블로 만들 수 있게 도와줌.
  4. WHERE
    • 데이터 셋을 형성하게 되면 WHERE의 조건이 개별 행에 적용된다. WHERE 절의 제약조건은 FROM 절로 가져온 테이블에 적용.
  5. GROUP BY
    • WHERE의 조건 적용 후 나머지 행은 GROUP BY절에 지정된 열의 공통 값을 기준으로 그룹화.
    • 쿼리에 집계 기능이 있는 경우에만 이 기능을 사용.
  6. HAVING
    • GROUP BY 절이 쿼리에 있을 경우 HAVING 절의 제약조건이 그룹화된 행에 적용.
  7. SELECT
    • SELECT에 표현된 식이 마지막으로 적용.
  8. DISTINCT
    • 표현된 행에서 중복된 행은 삭제
  9. ORDER BY
    • 지정된 테이블을 기준으로 오름차순, 내림차순 정렬
  10. LIMIT
    • LIMIT에서 벗어난 행들은 제외되어 출력

SELECT ~ FOR UPDATE 구문에 대해서 설명해주세요.

동시성 제어를 위하여 특정 데이터에 대해 배타적 LOCK을 거는 기능으로써
가장 먼저 LOCK을 획득한 세션의 SELECT된 ROW들이 UPDATE 쿼리 후 커밋되기 이전까지 다른 세션들은 해당 ROW들을 수정하지 못하도록 하는 기능입니다.
  • SELECT ~ FOR UPDATE : 누군가가 LOCK 중이면 무한정 대기.
  • SELECT ~ FOR UPDATE NOWAIT : 누군가가 LOCK 중이면 exception 처리.
  • SELECT ~ FOR UPDATE WAIT 5(초) : 누군가가 LOCK 중이면 입력한 시간(초단위)만큼 Lock을 재시도. 이후에 exception 처리
SELECT <attribute> FROM <TableName> WHERE <where clause> FOR UPDATE;
SELECT <attribute> FROM <TableName> WHERE <where clause> FOR UPDATE NOWAIT;
SELECT <attribute> FROM <TableName> WHERE <where clause> FOR UPDATE WAIT 5;

GROUP BY절에 대해서 설명해주세요.

속성에 대해 같은 값을 가진 행끼리 그룹화하는 명령어 입니다. GROUP BY 절을 사용하면 집계함수를 사용할 수 있으며 묶은 그룹에 대해 SELECT할 수 있는 속성이 제한이 되어 있습니다.

집합 연산자는 COUNT, SUM, AVG, MAX, MIN 등이 있고 DISTINCT와 같이 중복 데이터를 제거하는 특징이 있습니다.
  SELECT <attribute> FROM <TABLE> GROUP BY <attribute>;
  SELECT <attribute> FROM <TABLE> WHERE <WHERE caluse> GROUP BY <attribute>;
  SELECT <attribute> FROM <TABLE> GROUP BY <attribute> HAVING <HAVING caluse>;

ORDER BY절에 대해서 설명해주세요.

SQL문의 실행 결과를 특정 순서대로 출력하고 싶을 때 사용하는 명령어입니다.
기본적으로 오름차순이고, 내림차순으로 정렬하려면 DESC 키워드를 사용하면 됩니다.

innodb는 모든 데이터는 pk(클러스터링 키)기반으로 비트리 형태로 저장한다.

  • secondary index는 어떤식으로 데이터를 저장하나여
    • 이게 innodb는 secondary index가 clustering 인덱스를 포함하는 요상한구조를 가지고 있음
    • 끝까지 찾아갔는데? 결국 clustering index 알려줌
    • 그래서 항상 clustering index btree를 다시 뒤져야됨
  • non-clustering index와 clustering index의 차이는 무엇인가

풀 테이블 스캔

  • 인덱스를 사용하지 않고 테이블의 데이터를 처음부터 끝까지 읽어서 요청된 작업을 처리하는 작업.

Using filesort

정렬을 처리하기위해 file sort

구분 장점 단점
인덱스 이용 I, U, D 쿼리가 실행될 때 이미 인덱스가 정렬되어 있어서 순서대로 읽기만 하면 되므로 빠르다 I, U, D 작업시 부가적인 인덱스 추가/삭제 작업이 필요하므로 느리다.
FileSort 이용 I, U, D 작업시 부가적인 인덱스 추가/삭제 작업이 필요하지 않으므로 빠르다 정렬 작업이 쿼리 실행시 처리되므로 레코드 대상건수가 많아질수록 쿼리의 응답속도가 느리다.

Sort Buffer

  • 정렬을 수행하기 위해 별도의 메모리 공간을 할당받아서 사용. 이 메모리 공간의 이름이 Sort Buffer

정렬 알고리즘

  • 레코드를 정렬할 때, 레코드 전체를 소트 버퍼에 담을지 또는 정렬 기준 칼럼만 소트 버퍼에 담을지
  • 싱글패스 알고리즘
    • 소트 버퍼에 정렬 기준 칼럼을 포함해 SELECT되는 칼럼 전부를 담아서 정렬을 수행하는 방법.
    • 정렬이 완료되면 버퍼의 내용을 그대로 클라이언트로 넘겨준다.
  • 투패스 알고리즘
    • 정렬 대상 칼럼과 PK 값만을 소트 버퍼에 담아서 정렬을 수행.
    • 정렬된 순서대로 PK로 테이블을 읽어서 SELECT 할 칼럼을 가져오는 알고리즘.
    • innodb는 PK가 없는 경우 행 ID에 의해 정렬된 행은 물리적으로 삽입된 순서대로 정렬됩니다.

 

INNER JOIN과 OUTER JOIN의 차이점에 대해서 설명해주세요.

INNER JOIN은 데이터간의 교집합으로 연관된 두 내용의 데이터를 검색하는 조인 방법입니다.두 테이블을 조인할 때, 두 테이블에 모두 지정한 열의 데이터가 있어야합니다.
반면 OUTER JOIN은 두 테이블을 조인할 때, 1개의 테이블에만 데이터가 있어도 결과가 나옵니다. 외부 조인은 반드시 OUTER가 되는 테이블을 먼저 읽어야하기 때문에 조인 순서를 옵티마이저가 선택할 수 있습니다.

INNER JOIN

  • nested-loop만 지원
💡 NESTED LOOP 2개 이상의 테이블에서 하나의 집합을 기준으로
순차적으로 상대방 row를 결합하여 원하는 결과를 조합하는 조인방식.

OUTER JOIN

  • 일치하는 레코드가 있으면 INNER 조인과 같은 결과. 없으면 NULL로 채워서 결과 가져오기.

LEFT OUTER JOIN, RIGHT OUTER JOIN에 대해서 설명해주세요.

왼쪽 테이블을 기준으로 모든 값이 출력되면서 JOIN 조건에 해당하는 오른쪽 테이블의 값을 선택하는 것이 LEFT OUTER JOIN이고
오른쪽 테이블을 기준으로 모든 값이 출력되면서 JOIN 조건에 해당하는 왼쪽 테이블의 값을 선택하는 것이 RIGHT OUTER JOIN입니다.

Driving VS Drivien

    • Join할 때 먼저 Access되어 Access Path를 주도하는 테이블을 Driving table이라고 하고 나중에 access 되는 테이블을 driven table. Driving table은 옵티마이저가 선택

CROSS JOIN에 대해서도 설명해주세요.

수학적으로 카타시안 곱이라고 하는 CROSS JOIN은 brute force하게 두 테이블의 모든 데이터를 조인하는 방법입니다.
다른 테이블에 없는 값을 가져올 시 NULL로 데이터를 채워서 가져옵니다. FULL OUTER JOIN이라고도 불립니다.

서브쿼리에 대해서 설명해주세요.

하나의 SQL문에 포함되어 있는 또 다른 SQL 문으로 단일 행 또는 복수 행 비교연산자와 함께 사용이 가능합니다.
SELECT, FROM, WHERE 절 등 다양한 곳에서 사용이 가능합니다.

단일행 서브쿼리

  • 서브 쿼리가 단일 행 비교 연산자(=, <, ≤, ≥, >)와 함께 사용할 때는 서브 쿼리 결과가 1건 이하여야 함.
  • 결과가 2건 이상이면 오류 발생
SELECT C1, C2, C3
FROM T1
WHERE C1 <= (SELECT AVG(C1) FROM T2 WHERE C2 = '3')
ORDER BY C1, C2, C3

다중행 서브쿼리

  • 서브 쿼리의 결과가 2건 이상 반환될 수 있다면 반드시 다중행 비교 연산자(IN, ALL, ANY, EXISTS)과 함께 사용.
  • IN : 서브쿼리의 결과에 존재하는 임의의 값과 동일한 조건을 의미.
  • ALL : 서브쿼리의 결과에 존재하는 모든 값을 만족하는 조건을 의미.
  • ANY(SOME) : 서브쿼리의 결과에 존재하는 어느 하나의 값이라도 만족하는 조건을 의미.
  • EXISTS : 서브쿼리의 결과에 만족하는 값이 존재하는 지 여부 확인
SELECT C1, C2, C3
FROM T1
WHERE (C1, C2) IN (SELECT C1, C2 FROM T2 WHERE C2 = '3')
ORDER BY C1, C2, C3;

위치에 따른 서브쿼리 명

  • SELECT
    • 스칼라 서브 쿼리
    • 한행, 한 컬럼만을 반환
  • FROM
    • 인라인 뷰
    • 데이터 베이스의 동적인 뷰이기에 메모리에 저장 X

DROP, TRUNCATE, DELETE에 각각에 대해 설명해주세요. 어떤차이가 있나요?

DROP은 테이블을 완전히 삭제하는 방식으로 삭제 후 되돌릴 수 없습니다. 
TRUNCATE
는 전체 데이터를 한번에 삭제하는 방식으로 테이블 용량이 줄어들고 인덱스도 삭제되지만 테이블은 삭제할 수 없고 삭제 후 되돌릴 수 없습니다.
DELTETE
는 데이터는 지우지만 테이블 용량은 줄어들지 않고 원하는 데이터만 지울 수 있고, 삭제 후 되돌릴 수 있습니다.

DROP

  • 기존 테이블의 존재를 제거합니다.
  • ROLLBACK이 불가능합니다.
  • 테이블의 행, 인덱스 및 권한도 제거됩니다.

TRUNCATE

  • 개별적으로 행을 삭제할 수 없으며, 테이블 내부의 모든 행을 삭제합니다.
  • WHERE 절을 함께 사용할 수 없습니다.(개별적으로 행 삭제가 불가능합니다.)
  • ROLLBACK이 불가능.
  • 로그 X ⇒ 작업이 빠름
  • 테이블의 데이터베이스 할당 해제를 기록

DELETE

  • 테이블의 내부의 행을 모두 삭제하며, WHERE 절을 사용하여 개별적으로 행을 삭제할 수 있습니다.
  • ROLLBACK 가능.
  • 로그되는 작업. ⇒ 각 행은 트랜잭션 로그에 기록되므로 작업이 느림.

DISTINCT에 대해서 설명해주세요. 사용해본 경험도 설명해주세요.

데이터 베이스의 중복을 제외하고 조회하는 명령어 입니다.내부적으로 GROUP BY와 동일한 코드를 사용하지만
DISTINCT는 GROUPING 작업만, GROUP BY는 GROUPING과 정렬 작업도 동시에 진행하기에 DISTINCT가 더욱 빠릅니다.
  • 정렬이 보장되지 않음.
  • 각 칼럼에 유니크한 값을 가져오지 못함. ⇒ 이를 위해선 GROUP BY 이후 DISTINCT 시도.
  • DISTINCT 키워드는 시간이 많이 드는 키워드. ⇒ exists를 통해서

SQL Injection 공격이 무엇인지 어떻게 공격을 예방할 수 있는지 설명해주세요.

악의적인 사용자가 보안상의 취약점을 이용하여 임의의 SQL문을 주입하고 실행되게 하여 데이터베이스가 비정상적으로 동작하도록 조작하는 행위를 일컫습니다.
이를 예방하기 위해서는 예외처리를 통한 검증을 하는 파라미터 바인딩저장 프로시저를 사용하는 방법이 있습니다.

공격 종류 및 방법

  • Error based SQL Injection
    • 입력 값에 대한 검증이 없음을 확인하고 임의의 SQL 구문을 주입.
  • Union based SQL Injection
    • 두개의 쿼리문에 대한 결과를 통합해서 하나의 테이블로 보여주게하는 키워드.
    • 두 테이블의 컬럼 수가 같아야하며 데이터형이 같아야함.
  • Blind SQL Injection
    • 데이터베이스로부터 특정한 값이나 데이터를 전달받지 않고, 단순히 참과 거짓의 정보만 알 수 있을 때 사용.

예방

  1. 입력값을 검증하여 사용자의 입력이 쿼리에 동적으로 영향을 주는 경우 입력된 값이 개발자가 의도한 값인지 검증 ⇒ 파라미터 바인딩.
  2. 저장 프로시저를 사용.
💡저장 프로시저
사용하고자 하는 쿼리에 미리 형식을 지정하는 것. 지정된 형식의 데이터가 아니면 Query가 실행되지 않기에 보안성이 향상

 

알고 있는 SQL 안티패턴이 있다면 설명해주세요.

SQL 쿼리나 데이터베이스 설계에서 흔히 발생하는 잘못된 패턴을 말합니다.
SQL 안티 패턴은 성능 저하, 데이터 무결성 손상, 보안 취약점 등의 문제를 야기할 수 있습니다.
따라서 SQL 안티 패턴을 인식하고 피하는 것이 중요합니다.

예시

  1. SELECT * FROM
    1. 모든 컬럼을 조회하는 쿼리.
    2. 테이블의 구조가 변경되거나 컬럼이 추가되면 예상치 못한 결과를 가져다 줌
    3. 필요하지 않은 컬럼까지 조회하므로 네트워크 부하메모리 사용량 증가
    4. 해결책
      1. 명시적으로 필요한 컬럼만 조회하도록 설계
  2. 문자열 연결로 동적 쿼리 생성
    1. SQL injection 공격에 취약.
    2. 해결책
      1. 파라미터화된 쿼리나 저장 프로시저 사용
      name = request.getParameter("name");
      sql = "SELECT * FROM users WHERE name = '" + name + "'"; -- 안티 패턴
      stmt = conn.createStatement();
      rs = stmt.executeQuery(sql);
      
      name = request.getParameter("name");
      sql = "SELECT * FROM users WHERE name = ?"; -- 권장
      pstmt = conn.prepareStatement(sql);
      pstmt.setString(1, name);
      rs = pstmt.executeQuery();
  3. 잘못된 데이터 타입 사용
    1. 데이터 타입은 데이터의 종류와 크기를 정의
    2. 데이터 타입을 잘못 사용하면 데이터의 정확성과 효율성이 떨어짐.
    3. 날짜나 시간을 문자열로 사용. ⇒ 관련 함수 사용 X
    4. 해결책
      1. 데이터의 성격과 범위에 맞는 데이터 타입을 사용.
  4. 다중 칼럼 속성
    1. 하나의 엔터티에 여러개의 칼럼으로 만들어 대응
      1. 검색: 원하는 정보가 어디에 있는 지 모두 확인해야함
      2. 수정: 어떤 칼럼을 수정해야할 지 확인하고 수정해야함.
      3. 일관성: 여러 칼럼에 중복되는 값이 저장되는 것을 예방하기 어려움
      4. 테이블 잠금: 칼럼 하나의 값을 수정하기 위해 테이블 전체가 잠금이 설정될 수 있음
    2. 해결책
      1. 종속 테이블을 생성해 사용.
-- 안티 패턴
CREATE TABLE member (
id INT PRIMARY KEY,
name VARCHAR(10),
hobby1 VARCHAR(10), // 취미1
hobby2 VARCHAR(10), // 취미2
hobby3 VARCHAR(10), // 취미3
);CREATE TABLE hobby (
hobby_id INT PRIMARY KEY,
name VARCHAR(10)
);
-- 권장
CREATE TABLE member (
member_id INT PRIMARY KEY,
name VARCHAR(10),
hobby_id INT FOREIGN KEY REFERENCES hobby(hobby_id)
);
  1. 한개의 칼럼에 컴마를 넣으면 안된다
    1. 검색하기 어렵고, 문자열 칼럼의 문자 수 제한이라는 제약.
    2. validation이 어려움
    3. 해결책
      1. 교차 테이블을 생성.
  2. 트리 형태의 계층 구조를 1개의 테이블로 표현하면 안된다.
    1. 계층 구조가 깊어지면 SQL이 복잡해지고 노드 조회 및 삭제가 어려워짐.
    2. 해결책
      1. 대체 트리 모델을 사용.
  3. 모든 테이블에 id를 만드는 것이 필요 없다.
    1. 장황해 질 가능성.
    2. id가 PK가 아니기에 헷갈릴 가능성.
    3. 해결책
      1. PK는 명확한 이름을 붙힌다.
      2. 복합키를 사용한다.
  4. FK 제약을 사용하지 않는 패턴은 좋지 않음.
    1. 참조 무결성을 재구현해야함.
    2. 데이터의 손상시 문제 발생
    3. 해결책
      1. 외부키를 사용해야한다.
  5. 테이블이나 열을 복사하거나 데이터에 의존하여 테이블을 작성하면 안됨.
    1. 열의 수가 많은 테이블을 생성하거나 적은 수의 테이블을 생성해야함.
    2. 테이블 간의 정합성을 얻기 어렵다
    3. 해결책
      1. 행으로 분리하는 수평 파티셔닝을 사용한다.
      2. 열로 분리하는 수직 파티셔닝을 사용한다.
  6. 이미지와 같은 대용량 파일을 저장할 때 링크만을 데이터베이스에 저장하는 것은 좋지 않다.
    1. 레코드가 삭제된다고 해도 실제 파일까지 삭제의 보장성이 없다.
    2. 롤백으로 되돌아오지 않는다.
    3. 외부 파일은 SQL의 액세스 권한 영향을 받지 않는다.
    4. 해결책
      1. BLOB형을 고려한다.
  7. 인덱스를 잘못 설정하는 것.
    1. 인덱스를 사용하지 않는다.
    2. 인덱스를 붙인다.
    3. ⇒ 퍼포먼스가 안좋아짐
  8. LIKE나 정규 표현을 이용한 패턴 매치는 안티패턴이다.
    1. 인덱스의 효과가 없어지고 테이블 스캔이된다.
    2. 해결책
      1. 전문 검색 엔진 혹은 elasticSearch를 활용한다
  9. 패스워드를 평문으로 저장하는 것은 안티패턴이다
    1. 보안상의 위험이 있다.
    2. 해결책
      1. 해시하여 저장한다.
      2. 패스워드는 복구가 아닌 리셋을 한다.
    등등..

페이지네이션을 구현한다고 했을때 쿼리를 어떻게 작성해야할까요?

LIMIT와 OFFSET 키워드를 사용합니다.
LIMIT는 가져올 레코드의 수를 지정하고, OFFSET은 시작점을 지정합니다.

예시

// 첫페이지
SELECT * 
FROM posts 
LIMIT 10 OFFSET 0;
// 두번째 페이지
SELECT * 
FROM posts 
LIMIT 10 OFFSET 10;

문제점

  • 성능이 안좋아짐.
  • MySQL 기준 처음부터 해당 위치까지의 모든 레코드를 읽어오기에 느려짐.
  • 또 페이지를 읽던 중 새로운 행이 추가된다면 데이터가 중복 조회됨.

WHERE 절과 인덱스를 사용하는 방법

  • id에 인덱스가 걸려있다고 가정하고
// 첫번째 페이지
SELECT * 
FROM posts 
WHERE id <= 10 
ORDER BY id DESC;
// 두번째 페이지
SELECT * 
FROM posts 
WHERE id > 10 and id <= 20 
ORDER BY id DESC;

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

[JSCode] Database 면접 스터디 회고록  (1) 2023.12.08
[JSCode] Database Week5  (2) 2023.12.05
[JSCode] Database Week4  (0) 2023.11.28
[JSCode] Database Week3  (0) 2023.11.21
[JSCode] Database Week1  (0) 2023.11.07

+ Recent posts