future & promise 사용법


  • promise 객체를 통해 자신과 연결된 future 객체를 생성할 수 있습니다. promise 객체에 값이 셋팅되었을 때 future를 통해서 해당 값을 가져올 수 있습니다.




future & promise 사용법 예시


#include <iostream>
#include <future>

using namespace std;

void WorkerThread(promise<string>* p, const string *name)
{
    try
    {
        // name이 nullptr일 경우 throw
        if (name == nullptr)
        {
            throw runtime_error("name is nullptr");
        }

        // promise에 값을 셋팅
        p->set_value(*name + ", Hello\n");
    }
    catch (const std::exception& exp)
    {
        // promise에 exception_ptr을 셋팅
        p->set_exception(make_exception_ptr(exp));
    }

    return;
}

int main()
{
    promise<string> p;

    // 자신과 연결된 promise 객체를 생성
    future<string> f = p.get_future();

    string str = "devhun";

    thread t(WorkerThread, &p, nullptr);

    f.wait();

    try
    {
        // promise에 셋팅된 값을 가져옴
        std::cout << f.get();
    }
    catch (const std::exception& exp)
    {
        // promise에 셋팅된 exception 값을 가져옴
        std::cout << exp.what();
    }

    t.join();

    return 0;
}

  • future와 promise를 통해서 다른 쓰레드에서의 결과값을 가져올 수 있습니다. 그리고 future의 get은 단 한번만 사용할 수 있습니다.

  • future를 통해서 exception 값을 셋팅하고 promise를 통해서 exception 값을 가져올 수 있습니다. 이때 exception을 promise에 셋팅할 때 반드시 exception_ptr로 셋팅해야 합니다. 그리고 promise에 exception이 셋팅되면 future의 get에서 이를 catch할 수 있습니다.

  • future의 get만을 사용해서도 promise에 값이 셋팅될 때까지 기다리지만, future의 get보다 wait이 가볍기 때문에 wait을 이용해서 대기하고 get을 통해 값을 가져오는 것이 좋습니다.




shared_future


#include <iostream>
#include <future>

using namespace std;

void WorkerThread(promise<string>* p, const string *name)
{
    try
    {
        if (name == nullptr)
        {
            throw runtime_error("name is nullptr");
        }

        p->set_value(*name + ", Hello\n");
    }
    catch (const std::exception& exp)
    {
        p->set_exception(make_exception_ptr(exp));
    }

    return;
}

int main()
{
    promise<string> p;

    shared_future<string> f = p.get_future();

    string str = "devhun";

    thread t(WorkerThread, &p, &str);

    f.wait();

    try
    {
        for (int i = 0; i < 10; ++i)
        {
            std::cout << f.get();
        }
    }
    catch (const std::exception& exp)
    {
        std::cout << exp.what() << "\n";
    }

    t.join();

    return 0;
}

  • future는 get 함수를 한 번만 호출할 수 있으며, 이후 호출부터는 no state 예외가 발생됩니다. 하지만, shared_future를 사용할 경우 get을 중복하여 호출할 수 있습니다.




packaged_task


#include <iostream>
#include <future>

using namespace std;

string WorkerThread(const string *name, const string* hello)
{
    this_thread::sleep_for(chrono::seconds(2));

    return *name + *hello;
}

int main()
{
    // return 타입과 매개변수 타입을 지정
    packaged_task<string(const string*, const string*)> task(WorkerThread);

    future<string> f = task.get_future();

    string str1 = "devhun";
    string str2 = ", Hello";

    // 복사 생성이 불가능하기 때문에 move를 통해 인자로 전달해야 합니다.
    thread t(move(task), &str1, &str2);

    cout << f.get() << endl;

    t.join();

    return 0;
}

  • packaged_task를 생성할 때 비동기로 실행할 함수를 매개 변수로 전달하고 함수의 리턴 타입과 매개 변수 타입에 대한 템플릿 타입을 지정합니다. 그리고 생성된 packaged_task를 이용해서 future 객체를 생성하여 이를 통해 비동기 실행에 대한 결과를 받아볼 수 있습니다.

  • packaged_task는 promise의 set_value를 사용하지 않고 함수의 return 타입을 대신하여 처리할 수 있습니다.

  • packaged_task는 promise의 set_exception을 사용하지 않고도 task에 대한 예외가 설정됩니다.




async


#include <iostream>
#include <future>

using namespace std;

string WorkerThread(const string *name, const string* hello)
{
    this_thread::sleep_for(chrono::seconds(2));

    return *name + *hello;
}

int main()
{
    string str1 = "devhun";
    string str2 = ", Hello";

    future<string> f = async(launch::async, WorkerThread, &str1, &str2);

    cout << f.get() << endl;

    return 0;
}

  • promise 또는 packaged_task는 비동기적으로 실행하기 위해서 thread 객체를 직접 생성하여 처리해야 했습니다. 하지만, asyn를 사용할 경우 내부적으로 쓰레드를 생성하여 매개변수로 전달한 함수를 비동기적으로 처리합니다.




lanch::async


async(launch::async, WorkerThread, &str1, &str2);

  • launch::async를 전달할 경우 내부적으로 새로운 쓰레드를 생성하여 함수를 비동기적으로 처리합니다.




lanch::deferred


async(launch::deferred, WorkerThread, &str1, &str2);

  • launch::deferred를 사용할 경우 별도의 쓰레드를 생성하지 않고 future 객체를 통해 get을 호출하였을 때 해당 쓰레드에서 함수를 호출하는 방식입니다.




'Programming Language > C, C++' 카테고리의 다른 글

C++ condition_variable 사용 방법  (0) 2023.04.15
C++ unique_lock 사용방법  (0) 2023.04.15
C++ mutex와 lock_guard  (0) 2023.04.15
C++ 다중 상속(Multiple inheritance)이란?  (0) 2023.03.26
C++ 람다(lambda)란?  (0) 2023.03.25

condition_variable

 

  • condition_variable은 C++11에 추가된 클래스로써, condition_variable을 통해 생성한 객체를 이용하면 조건에 맞지 않으면 대기하고, 다른 쓰레드에서 조건이 맞게 수정하였다면 이를 알려 대기중인 쓰레드를 깨울 수 있습니다.


  • condition_variable은 Event 객체와 같이 Windows에 종속적인 커널 오브젝트를 직접적으로 사용하지 않고 여러 OS에서 공통적으로 사용할 수 있는 클래스입니다.

 

  • condition_variable은 unique_lock을 같이 병행하여 사용하여 조건에 해당하는 값을 읽고 수정할 수 있습니다.




condition_variable 사용방법

 

#include <iostream>
#include <mutex>
#include <thread>
#include <queue>

std::mutex m;
std::condition_variable cv;
std::queue<int> q;

void EnqueueThread()
{
    for(;;)
    {
        {
            std::unique_lock<std::mutex> lk(m);
            q.push(rand() % 10);
        }

        // 동일한 condition_variable 객체를 대상으로 대기중인 쓰레드를 깨워 조건을 확인시킴
        cv.notify_one();
    }

    return;
}

void DequeueThread()
{
    for (;;)
    {
        std::unique_lock<std::mutex> lk(m);

        // lock을 건 상태에서 조건을 확인
        // 조건에 맞지 않는다면 lock을 해제 후 쓰레드 block
        // 조건에 맞을 경우 lock을 유지한 상태에서 쓰레드 block을 하지 않고 다음 로직 수행
        cv.wait(lk, []()->bool {return !q.empty(); });

        std::cout << q.front() << "\n";
        q.pop();
    }

    return;
}

int main()
{
    std::thread th1(EnqueueThread);
    std::thread th2(DequeueThread);

    th1.join();
    th2.join();

    return 0;
}

 

  • wait에 unique_lock과 람다를 전달해서 조건에 조건에 맞을 경우 unique_lock에 대한 소유권을 유지한체 다음 로직을 수행하고, 조건에 맞지 않을 경우 unique_lock에 대한 소유권을 해제하고 쓰레드 블락됩니다.

 

  • notify_one은 동일한 condition_variable 객체를 대상으로 대기중인 쓰레드를 하나를 깨워 condition을 다시 한 번 확인하도록 요청합니다. condition이 맞을 경우 이후 다음 로직을 수행하고, condition이 맞지 않을 경우 다음 로직을 수행하지 않고 다시 block됩니다.




'Programming Language > C, C++' 카테고리의 다른 글

future & promise, packaged_task, async 사용법  (0) 2023.04.18
C++ unique_lock 사용방법  (0) 2023.04.15
C++ mutex와 lock_guard  (0) 2023.04.15
C++ 다중 상속(Multiple inheritance)이란?  (0) 2023.03.26
C++ 람다(lambda)란?  (0) 2023.03.25

unique_lock 이란?

 

  • unique_lock 객체는 lock_guard와 같이 RAII 기법을 통해 생성자에서 락에 대한 소유권을 획득하고 소멸자에서 락에 대한 소유권을 반환하는 객체입니다.

 

  • unique_lock을 생성할 때 derfer_lock, try_to_lock, adopt_lock 중 하나를 추가로 전달하여 lock_guard보다 다양한 기능을 사용할 수 있습니다.

 

  • lock_guard가 상대적으로 unique_lock 보다 더 가볍기 때문에 lock_guard를 우선적으로 사용하고 필요시에 unique_lock을 사용하는 것이 좋습니다.




defer_lcok

 

#include <iostream>
#include <mutex>
#include <thread>

int num;
std::mutex m;

void WorkerThread() {
    for (int i = 0; i < 100000; ++i)
    {
        std::unique_lock<std::mutex> lk(m, std::defer_lock);
        lk.lock();

        ++num;
    }

    return;
}


int main()
{
    std::thread th1(WorkerThread);
    std::thread th2(WorkerThread);

    th1.join();
    th2.join();

    std::cout << num;

    return 0;
}

 

  • defer_lock을 전달할 경우 소유권 획득 시점을 생성자가 아닌 lock 멤버 함수를 호출하는 시점으로 연기할 수 있습니다. 그리고 lock이 호출되었다면, 소멸자에서 이를 반환합니다.




try_to_lock

#include <iostream>
#include <mutex>
#include <thread>

int num;
std::mutex m;

void WorkerThread() {
    for (int i = 0; i < 100000; ++i)
    {
        std::unique_lock<std::mutex> lk(m, std::try_to_lock);
        if (!lk.owns_lock()) {
            --i;
            continue;
        }

        ++num;
    }

    return;
}


int main()
{
    std::thread th1(WorkerThread);
    std::thread th2(WorkerThread);

    th1.join();
    th2.join();

    std::cout << num;

    return 0;
}

 

  • try_to_lock은 생성자에서 lock에 대한 소유권을 획득할 수 있는지 여부를 시도를 합니다. 만약, 생성자에서 lock에 대한 소유권을 획득하였다면, owns_lock() 멤버 함수의 return 값이 true이고 획득하지 못했다면 false를 return 합니다.




adopt_lock

 

#include <iostream>
#include <mutex>
#include <thread>

int num;
std::mutex m;

void WorkerThread() {
    for (int i = 0; i < 100000; ++i)
    {
        m.lock();
        std::unique_lock<std::mutex> lk(m, std::adopt_lock);
        ++num;
    }

    return;
}


int main()
{
    std::thread th1(WorkerThread);
    std::thread th2(WorkerThread);

    th1.join();
    th2.join();

    std::cout << num;

    return 0;
}

 

  • adopt_lock는 이미 매개변수로 전달한 동기화 객체에 대한 소유권이 이미 획득되어 있는 상태이고, 이를 소멸자에서 반환하기 위해서 사용합니다.




mutex

 

  • mutex는 C++11에 추가된 class로써 운영체제에 종속되지 않고 공통적으로 사용할 수 있는 유저 모드 동기화 객체입니다.

 

  • 배타적인 접근만 가능하며, 중복 락이 불가능합니다.




lock과 unlock

 

#include <iostream>
#include <thread>
#include <mutex>

std::mutex m;

void WorkerThread()
{
    for (int i = 0; i < 100000; ++i)
    {
        m.lock();
        ++num;
        m.unlock();
    }

    return;
}

int main(){
    std::thread th1(WorkerThread);
    std::thread th2(WorkerThread);

    th1.join();
    th2.join();

    std::cout << num;

    return 0;
}

 

  • lock()을 통해서 배타적인 접근이 가능하고, unlock()을 통해서 배타적인 접근에 대한 소유권 해제가 가능합니다.

 

  • try_lock()은 현재 배타적인 접근이 가능한지를 확인하고 소유권을 획득하는 멤버 함수이며, return 타입은 bool로써 소유권 획득 성공 여부를 뜻합니다.




lock_guard

 

#include <iostream>
#include <thread>
#include <mutex>

int num;
std::mutex m;

void WorkerThread()
{
    for (int i = 0; i < 100000; ++i)
    {
        std::lock_guard<std::mutex> lock(m);
        ++num;
    }

    return;
}


int main()
{
    std::thread th1(WorkerThread);
    std::thread th2(WorkerThread);

    th1.join();
    th2.join();

    std::cout << num;

    return 0;
}

 

  • lock_guard 객체는 RAII 디자인 패턴을 이용해서 생성자에서 락을 획득하고 소멸자에서 락을 반환하는 객체입니다.


  • lock_guard는 복사가 불가능하기 때문에 Call-by-value 방식으로 매개 변수 또는 리턴으로 값을 전달할 수가 없습니다.




MongoDB 특징

 

  • MongoDB는 Reliability(신뢰성), Scalability(확장성), Flexibility(유연성), Index Support(인덱스 지원) 4가지 특징을 가진 NoSQL Database 입니다.




Reliability(신뢰성)

 

 

  • Primary와 Secondry로 구성된 Replica Set 구조로 고가용성을 지원합니다. 이를 통해 서버의 장애가 발생되어도 자체적으로 이를 회복하여 서비스가 정상적으로 운영되도록 할 수 있습니다.




Replica Set 동작

 

  • MongoDB의 Replica Set은 기본적으로 1개의 Primary와 2개의 Secondary로 이루어져 있습니다.

 

  • 쓰기 작업은 Primary가 담당하고, 읽기 작업은 Secondary가 담당합니다. 이를 통해 읽기/쓰기에 대한 부하를 분산할 수 있습니다.

 

  • Primary는 쓰기 작업을 처리하여 데이터에 대한 변경 사항을 Secondary에 전달하여 동기화합니다.

 

  • Primary 서버에 문제가 발생되어 사용할 수 없게 되었다면, 자동적으로 Secondary 서버를 Primary 서버로 설정하고 새로운 Secondary 서버를 셋팅하여 서버 장애에 대응합니다.




Scalability(확장성)

 

 

  • 데이터와 트래픽이 증가함에 따라 데이터 샤딩을 통한 수평확장(scale-out)을 하여 부하 분산을 할 수 있습니다.


  • Replica Set구조로 이루어진 각 Shard에 Shard Key를 기준으로 데이터를 분산하여 저장합니다.

 

  • MongoDB는 RDBMS와 같이 정규화를 통한 테이블 분산을 거의 하지 않고, Document의 field에 배열 또는 Sub Document를 담음으로써 하나의 Document에 필요한 데이터를 모두 저장할 수 있습니다. 이를 통해 수평 확장에 대한 이점을 더욱 얻을 수 있습니다.




MongoDB의 밸런싱(Balancing)

 

  • 몽고DB에 데이터가 증가하여 더 이상 하나의 Replica Set에 담을 수 없게 되거나 특정 샤드에 데이터가 몰리면 다른 샤드로 데이터를 이동 시켜, 전반적으로 모든 샤드가 균등하게 데이터가 저장될 수 있도록 합니다. 이러한 작업은 서비스 중단 없이 온라인 과정에서 진행됩니다. 그리고 이러한 동작을 밸런싱(Balancing)이라고 합니다.




Flexibility(유연성)

 

 

  • 스키마 구조 변경 없이 여러 가지 형태의 데이터를 저장할 수 있기 때문에 서비스 요구 사항에 맞춰 다양한 형태의 데이터를 저장할 수 있습니다.

 

  • JSON 형태로 데이터를 저장하고 읽기 때문에 개발자들 입장에서도 Object와 1:1로 매칭하여 사용할 수 있어 더욱 편리합니다.

 

  • 하나의 Document에 배열 또는 Sub Document를 추가함으로써 RDBMS와 같이 join을 이용해 여러 테이블을 조회하는 상황을 줄일 수 있습니다.




Index Support(인덱스 지원)

 




MongoDB의 Cluster 구조

 

 

  • MongoDB의 Cluster 구조는 mongos, shard cluster, config server로 3개의 Component 구성됩니다.

 

  • shard cluster에는 실제 데이터가 저장됩니다.

 

  • config server에는 접근하고자 하는 데이터가 어떤 shard cluster에 저장되어 있는지 여부를 확인 수 있는 메타 데이터가 저장됩니다.

 

  • app server는 mongos에만 연결하여 데이터를 요청합니다. mongos는 config server를 통해서 어떤 shard cluster에 데이터가 저장되었는지 확인하여 shard cluster에서 데이터를 가져와 app server에 응답합니다.

 

  • 위와 같은 구조 덕분에 app server는 MongoDB의 Cluster 구조를 알 필요 없이 mongos에만 접근하여 원하는 데이터를 접근하고 조작할 수 있습니다. 덕분에 사용성 측면과 유지보수 측면에서의 이점이 있습니다.




BSON과 JSON

 

  • MongoDB는 JSON 형태로 데이터를 저장하는 것처럼 보이지만, 실제로는 BSON(Binary JSON) 형태로 데이터를 저장합니다.

 

  • JSON은 텍스트 기반으로 저장되기 때문에 구문 분석이 느리고, 공간 효율성이 떨어지고 데이터 타입 표현의 한계가 있습니다. 그렇기 때문에 사용자에게 보여질 때는 JSON으로 보여주고, 네트워크로 전송하고 저장할 때는 BSON으로 인코딩하여 처리합니다.

 

  • BSON은 JSON보다 공간 효율성, 처리 속도, 다양한 데이터 타입 지원 부분에서 이점이 있습니다.




자료 출처 : https://tv.kakao.com/v/414072595

 

'데이터 베이스 > NoSQL' 카테고리의 다른 글

MongoDB index 종류  (0) 2023.03.22
NoSQL이란?  (0) 2023.01.14
mongoDB 기본 개념 및 용어 설명  (0) 2023.01.13
MongoDB transaction 사용법  (0) 2023.01.08

C++ 다중 상속(Multiple inheritance)이란?

 

  • C++은 다중 상속을 지원하여 두 가지 이상의 클래스를 상속 받을 수 있습니다. 하지만, 다중 상속은 득보다 실이 더 많은 문법이기 때문에 사용하지 않는 것이 훨씬 더 좋은 방법입니다.




다중 상속의 모호성(Ambiguous)

 

#include <iostream>

using namespace std;

class CBaseOne {
public:
    void SayHello() {
        cout << "Hello World 1\n";
    }
};

class CBaseTwo {
public:
    void SayHello() {
        cout << "Hello World 2\n";
    }
};

class MultipleDerived : public CBaseOne, CBaseTwo{
public:

    void Say() {
    // 스코프 연산자를 통해 모호한 상황을 해결
        CBaseOne::SayHello();
        CBaseTwo::SayHello();
    }
};

int main() {

    MultipleDerived obj;

    obj.Say();

    return 0;
}

 

  • 위와같이 다중 상속을 받았는데, 부모 클래스들이 동일한 멤버 함수를 가지고 있을 경우 컴파일러는 어떤 부모 클래스의 멤버 함수를 호출할지 알 수 없습니다. 그렇기 때문에 스코프 연산자(::)를 통해서 이를 확실히 정해줘야 합니다.




가상 상속(Virtual Inheritance)

 

'윤성우의 열혈 C++' 다중 상속 이미지

 

#include <iostream>

using namespace std;

class CBase {
public:

    CBase() {
        cout << "CBase Constructor\n";
    }

  void SayHello(){
    cout << "Hello World\n";
  }

};

// 가상 상속
class CDerivedOne : virtual public CBase {
public:
    CDerivedOne()
    :CBase()
    {
        cout << "CDerivedOne Constructor\n";
    }
};

// 가상 상속
class CDerivedTwo : virtual public CBase {
public:
    CDerivedTwo()
        :CBase()
    {
        cout << "CDerivedTwo Constructor\n";
    }
};

class CLastDerived : public CDerivedOne, public CDerivedTwo {
public:
    CLastDerived()
        :CDerivedOne(),
        CDerivedTwo()
    {
        cout << "CLastDerived Constructor\n";
    SayHello();
    }
};


int main() {

    CLastDerived obj;

    return 0;
}

 

  • 위 코드에서 보는것과 같이 CDerivedOne, CDerivedTwo는 가상 상속을 받고 CLastDerived는 CDerivedOne, CDerivedTwo에 대해서 다중 상속을 받고있습니다.

    만약, CDerivedOne, CDerivedTwo가 가상 상속을 받지 않았다면 모호성으로 인해서 CLastDerived 생성자 내부에서 SayHello() 멤버 함수를 호출할 수 없게 되며 CBase에 대한 생성자도 두 번 호출되게 됩니다.

    이를 방지하려면 다중 상속을 하는 부모 객체는 virtual 키워드를 통해 가상 상속을 받아야 합니다.




C++ 람다(lambda)란?

 

  • C++ 11에 추가된 함수 객체 표현식으로서, 주로 다른 함수의 인자로 전달되거나 함수 내부에서 익명 함수로 사용됩니다.


  • 람다는 C++의 클로저(Closure)로서 캡쳐를 통해 람다 함수 내부에서 외부 변수에 대한 접근이 가능합니다.




람다 함수 객체를 생성하는 방법

 

 

  • 람다 왼쪽부터 오른쪽으로 순서대로 개시자(introducer), 인자(parameters), 반환 타임(return type), 함수의 몸통(statement)으로 구성되어 있습니다.

 

  • 람다 내부에서 외부 변수에 접근할 때 접근할 변수를 인자로 전달할 수도 있지만, Captrue 기능을 이용해서 접근할 수 있습니다. 이는 람다를 핸들러로서 사용하기 위해 정해진 인터페이스 형태로 정의해야만 할 때 매우 유용하게 사용할 수 있습니다.




[&] Capture

 

  • [&] Captrue는 외부의 모든 변수를 Call-by-reference 형태로 가져올 때 사용합니다.




[=] Capture

 

  • [=] Capture는 외부의 모든 변수를 Call-by-value 형태로 가져올 때 사용합니다. 그리고 Call-by-value 형태로 Capture할 경우 자동적으로 const 속성이 붙습니다.

 

  • 값 형태로 가져온 Capture 값을 수정하기 위해서는 [=]() mutable -> {} 형태로 사용해야 합니다.




[&, x, y]

 

  • [&, x, y] Capture는 외부의 x, y는 값 형태로 가져오고 나머지 모든 변수를 레퍼런스 형태로 가져오는 방식입니다. 이와같이 값과 래퍼런스를 혼합해서 Capture 할 수 있습니다.




[=] or [&] 그리고 this

 

  • 멤버 함수 내부에서 람다를 생성하고 [=] or [&] 형태로 캡쳐할 경우 자동으로 this가 캡쳐 됩니다.




람다와 외부 객체 참조

 

#include <iostream>

using namespace std;

class CTest {
public:
    CTest() {
        cout << "CTest\n";
    }

    ~CTest() {
        cout << "~CTest\n";
    }

    CTest(const CTest& obj) {
        cout << "Copy Creator\n";
    }

    void SayHello() const {
        cout << "Hello World\n";
    }
};

auto GetLambda() {
    CTest test;

  // [=]으로 캡쳐해야 함
    return [&]() -> void {
        test.SayHello();
    };
}

int main() {

    auto g = GetLambda();

    g();

    return 0;
}

 

  • GetLambda 함수를 호출하면 내부에서 생성된 람다 객체가 return 되면서 test 객체의 소멸자가 호출됩니다. 그렇기 때문에 람다 객체가 외부 변수에 접근할 때 해당 변수가 스택에서 이미 제거 될 수 있다는 점을 생각하고 사용해야 합니다. 즉, GetLambda 안에서 생성된 람다 객체는 GetLambda 함수 스택보다 오랫동안 유지되기 때문에 test를 Call-by-value 형태로 캡쳐해야 합니다.




클래스를 활용한 상속


class Animal {
  constructor(age, weight) {
    this.age = age;
    this.weight = weight;
  }
  eat() {
    return "eat";
  }
  move() {
    return "move";
  }
}

class Bird extends Animal {
  constructor(age, weight) {
    super(age, weight);
  }

  fly() {
    return "fly";
  }
}




프로토타입을 기반으로 한 상속


const Animal = (function () {
  function Animal(age, weight) {
    this.age = age;
    this.weight = weight;
  }

  return Animal;
})();

const Bird = (function () {
  function Bird(age, weight) {
    Animal.apply(this, [age, weight]);
  }

  Bird.prototype = Object.create(Animal.prototype);
  Bird.prototype.constructor = Bird;

  return Bird;
})();




클래스와 생성자 함수의 차이점


  • 클래스는 new 없이 호출할 경우 error가 발생합니다.

  • 클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode를 해제할 수 없습니다.

  • 클래스 내의 메서드 및 정적 메서드는 모두 [[Enumable]]이 false 입니다.

  • 클래스를 통해 상속을 구현한다면, 부모 클래스와 자식 클래스의 인스턴스 프로토타입 체인뿐만 아니라 클래스간의 프로토타입 체인도 생성합니다.




MongoDB index 종류

 

  • MongoDB는 다양한 인덱스를 지원하기 때문에 서비스 요구 사항에 적합한 인덱스를 선택하여 보다 빠른 검색 속도를 지원합니다.


  • MongoDB에서의 index는 모두 Non-Clustered index입니다.


  • index는 필요한 만큼 생성 가능합니다.

 




단일 필드 인덱스(Single Field Index)

 

  • 단일 필드만으로 구성된 인덱스입니다. B-Tree로 구현되어 오름/내림 차순 정렬이 가능합니다.




복합 인덱스(Compound Index)

 

  • 복합 인덱스는 여러 필드를 조합하여 구성된 인덱스입니다. 복합 인덱스는 복합 인덱스를 이루는 여러개의 필드로 검색할 때 큰 이점을 얻을 수 있으며, 지정된 필드 순서대로 정렬되기 때문에 인덱스를 구성하는 필드의 순서가 중요합니다. 예를들어서 복합 인덱스가 (field1, field2)로 구성되어 있다면 field1을 정렬한 후에 field1에 대한 field2를 정렬합니다. 그렇기 때문에 field1으로만 검색했을 때에는 복합 인덱스로 인한 이점을 얻을 수 있지만, field2로만 검색할 때는 복합 인덱스에 대한 이점을 얻을 수 없습니다.




텍스트 인덱스(Text Index)

 

  • 텍스트 인덱스(Text Index)는 MongoDB에서 문자열을 검색할 때 사용하는 인덱스입니다. 일반적인 인덱스와는 다르게 MongoDB의 텍스트 검색 기능을 사용하면 여러 단어나 문장에 대한 검색이 가능하며, 검색 결과를 스코어링하여 결과값을 정렬할 수 있습니다.(스코어링이란 검색어와 문서의 일치도를 계산하여 검색 결과를 정렬하는 방법입니다.)

    텍스트 인덱스는 단일 필드 인덱스와 복합 인덱스로 구성될 수 있습니다. 텍스트 인덱스를 지정한 필드에서는 전문 검색(full-text search)이 가능합니다. 이를 통해 전체 텍스트를 검색할 수 있고, 검색 결과에 대한 스코어링을 통해 결과값을 정렬할 수 있습니다.

    텍스트 인덱스를 사용하면 텍스트 검색 속도가 매우 빨라지며, 텍스트 검색 기능의 활용도를 높일 수 있습니다. 다만, 텍스트 인덱스를 사용하면 색인 크기가 커지기 때문에 저장 공간이 필요 이상으로 많아질 수 있다는 단점이 있습니다.




지리 공간 인덱스(Geospatial Index)

 

  • 지리 공간 인덱스(Geospatial Index)는 지리 정보(Geographic Information)가 저장된 필드를 더 빠르게 검색할 수 있도록 하는 인덱스입니다. MongoDB에서는 2차원의 평면 지리 정보를 다루는데, Geospatial Index는 해당 평면 상에서 좌표 값(latitude, longitude)으로 이루어진 위치 정보를 저장할 수 있습니다.

    Geospatial Index는 R-Tree 알고리즘을 사용하여 인덱싱을 수행합니다. R-Tree는 2차원 공간 상에서 사각형의 형태로 인덱싱을 수행하여 공간 상의 논리적 관계를 유지합니다. 이를 통해 지정한 범위 내에서 빠른 검색을 수행할 수 있습니다.

    Geospatial Index를 사용하면 지도 어플리케이션 등에서 위치 기반 검색을 더욱 빠르게 수행할 수 있습니다. 또한, 지리 정보가 포함된 다양한 데이터를 분석하는 데에도 유용합니다.


  • 카카오 택시, 카카오 대리, 카카오 모빌리티 등과 같은 지리적 특성을 이용한 서비스에서 사용됩니다.




TTL 인덱스(Time-To-Live Index)

 

  • TTL(Time-To-Live) 인덱스는 MongoDB에서 지원하는 인덱스 중 하나로, 일정 기간이 지나면 데이터를 삭제하는 인덱스입니다. 즉, 데이터의 유효기간을 설정하여 그 기간이 지난 데이터를 자동으로 삭제해주는 역할을 합니다.

    TTL 인덱스는 적용할 필드에 일정 기간을 설정하면 해당 기간이 지나면 데이터가 자동으로 삭제됩니다. 예를 들어, create_at 필드에 1일을 설정했다면, 1일이 지나면 해당 데이터는 자동으로 삭제됩니다.

    TTL 인덱스는 일반적으로 로그 데이터, 세션 데이터 등 유효기간이 정해져 있는 데이터에 많이 사용됩니다. 이를 통해 필요하지 않은 데이터가 지속적으로 쌓이는 것을 방지하고, 데이터의 용량을 줄일 수 있습니다.




해시 인덱스(Hashed Index)

 

  • 해시 인덱스(Hashed Index)는 해시 함수(Hash Function)를 사용하여 인덱스를 생성하는 인덱스 종류입니다. 해시 기반의 인덱스이기 때문에 O(1)의 시간복잡도를 가지며, 검색 성능이 매우 빠릅니다. 해시의 특성상 정렬이 불가하고, 인덱스의 크기가 고정되어 있기 때문에, 데이터가 많아져 해시가 충돌할 수록 인덱스의 성능이 저하될 수 있습니다. 해시 인덱스는 주로 전체 데이터에서 일부 데이터를 빠르게 검색할 때 사용됩니다.




'데이터 베이스 > NoSQL' 카테고리의 다른 글

MongoDB의 특징  (0) 2023.03.29
NoSQL이란?  (0) 2023.01.14
mongoDB 기본 개념 및 용어 설명  (0) 2023.01.13
MongoDB transaction 사용법  (0) 2023.01.08

ERD(Entity Relationship Diagram)란?

 

  • ERD(Entity Relationship Diagram)는 단어에서 의미하는 그대로 'Entity 개체'와 'Relationship 관계'를 중점적으로 표시하여 데이터 베이스 구조를 한 눈에 알아보기 위해 그려놓는 다이어그램입니다. 개체 관계도라고도 불리며 요구 분석 사항에서 얻은 개체와 속성들의 관계를 그림으로 표현할 수 있습니다.




ERD에서 Entity 표기법

 

Entity

 

 

  • Entity는 정의 가능한 사물 또는 개념을 의미합니다.

 

  • 학생과 학생의 취미를 표현할 때, 학생은 유형 Entity, 학생의 취미는 무형 Entity로 구별할 수 있습니다.

 

  • 데이터 베이스 테이블을 Entity로 포현할 수 있습니다.




Entity Attribute

 

 

  • Entity에는 개체가 갖고있는 속성(Attribute)를 포함합니다.

 

  • 데이터 베이스 테이블의 각 컬럼을 Attribute로 표현할 수 있습니다.




Entity Domain

 

 

  • Domain은 속성의 값, 타입, 제약 사항 등의 대한 값의 범위를 표현할 수 있습니다.

 

  • 사용자 기호에 따라 속성 타입만 그릴수도 있고, 가독성을 위해서 생략할 수도 있습니다.

 

  • 데이터 타입을 명시할 때, 사용할 데이터 베이스가 지원하는 타입에 맞게 표시합니다.




ERD 키와 제약 조건 표기법

 

기본 키(Primary key)

 

 

  • Primary Key는 속성 앞에 ◆를 기재하여 표현합니다.(ERD 프로그램에 따라 다름)

 

  • Primary Key는 다른 속성들과 구분하기 위해서 구분선을 두기도 합니다.




외래 키(Foreign Key)

 

 

  • 데이터 베이스 테이블의 Foreign Key를 표현할 때 해당 Foreign Key를 Primary Key로 들고 있는 개체와의 선을 이어주어 관계를 표시합니다.

 

  • 외래 식별자 역시 Key의 일종이라 ERD Entity에서도




NOT NULL

 

 

  • 해당 속성에 들어갈 값에 NULL을 허용하지 않는다면 N 또는 NN을 적습니다.

 

  • NULL을 허용한다면 아무것도 적지 않습니다.




ERD의 Entity 관계 표기법

 

  • Entity간의 관계를 표현할 때 선을 이어주어 표현합니다. 이 때 강한 연결관계 또는 약한 연결관계에 따라서 실선 또는 점선으로 표현합니다.




강한 연결관계(식별자 관계)

 

 

  • FK를 자신의 PK에 사용한다면 강한 연결관계로서 실선으로 관계를 표현합니다.




약한 연결관계(비식별자 관계)

 

 

  • FK를 자신의 속성에 사용한다면 약한 연결관계로서 점선으로 관계를 표현합니다.




ERD Entity 관계의 Cardinality

 

  • 관계가 존재하는 두 Entity 사이에 관계 수를 표현하는 방법은 아래와 같습니다.




ERD 관계 표현 방법

 

 

  • '|' 표시가 있는 곳은 반드시 있어야 하는 개체. (필수)

 

  • 'O' 표시가 있다면 없어도 되는 개체. (선택)

 

  • 이와 같은 표현 방법으로 1:1, 1:N, M:N 등의 표현을 할 수 있습니다.




관계의 선택 기호

 

 

  • 취미를 가지지 않는 학생이 있을 수는 있지만, 취미는 있는데 학생이 없는 경우는 없습니다. 즉, 이러한 경우 취미쪽을 선택 기호를 이용해서 표현합니다.




관계의 필수 기호

 

 

  • 취미가 있다면, 해당 취미를 가진 학생은 반드시 존재해야 합니다. 즉, 어느 한쪽에 존재한다면 다른 쪽은 반드시 존재해야 하는 관계를 필수 관계 기호를 사용해 표현합니다.




'데이터 베이스 > Common' 카테고리의 다른 글

데이터 모델링(Date Modeling)이란?  (0) 2023.03.17
MySQL과 mongoDB의 차이  (0) 2023.01.14

+ Recent posts