Advanced C++

[C++] Redis 사용하기

로파이 2022. 8. 18. 23:58

C++ Redis 라이브러리를 사용해본다.

 

Redis

데이터 베이스 서버에서 쿼리로 조회하는 것보다 캐시 서버(Redis)를 둬 I/O를 동반한 데이터 베이스 서버보다 빠르게 데이터를 가져오기 위해 사용한다.

 

기본적으로 key-value 쌍의 데이터를 저장한다.

 

Redis 는 C언어로 작성된 hiredis 라이브러리를 의존하는 C++ 라이브러리를 사용할 예정이다.

 

C hiredis
https://github.com/redis/hiredis

 

GitHub - redis/hiredis: Minimalistic C client for Redis >= 1.2

Minimalistic C client for Redis >= 1.2. Contribute to redis/hiredis development by creating an account on GitHub.

github.com

static 라이브러리를 빌드 한다.

 

C++ sewnew/redis-plus-plus
https://github.com/sewenew/redis-plus-plus#install-hiredis

 

GitHub - sewenew/redis-plus-plus: Redis client written in C++

Redis client written in C++. Contribute to sewenew/redis-plus-plus development by creating an account on GitHub.

github.com

 

redis-plus-plus를 빌드할 때 주의사항

포함 디렉터리

1. #include<hiredis/hiredis.h> // hiredis 헤더를 참조하기 위한  포함할 수 있도록 포함 디렉터리를 추가한다.

2. tls 지원 여부에 따라 no_tls/tls 디렉터리를 포함하도록 한다.

3. cxx11/cxx17 스탠다드에 따라 string_view 같은 지원을 선택할 수 있다.

 

메인 프로젝트

hiredis 폴더가 존재하는 폴더 경로를 포함 디렉터리에 추가

redis-plus-plus-master/src 폴더 경로를 포함 디렉터리에 추가 

  • hiredis 정적 라이브러리 링크
  • redis++ 정적 라이브러리 링크
  • ws_32.lib 윈도우 소켓 라이브러리 링크

헤더 포함

#include <sw/redis++/redis++.h>

using namespace std;
using namespace sw::redis;

 

기본 key-value에 대한 사용

  • get : key에 대응하는 value 가져오기
  • set : key에 대응하는 값 설정
  • strlen : value 길이 가져오기
  • getrange : 부분 길이의 문자열 value 가져오기
  • append : 기존 value에 문자열 값 추가
static void example1()
{
	redis->set("key", "hello world");

	auto val = redis->get("key");
	if (val)
	{
		cout << "val : " << *val << endl;
	}

	auto len = redis->strlen("key");
	if (len)
	{
		cout << "len : " << len << endl;
	}

	auto sub_val = redis->getrange("key", 6, -1);
	if (!sub_val.empty())
	{
		cout << "sub val : " << sub_val << endl;
	}

	redis->append("key", " redis!");

	auto val2 = redis->get("key");
	if (val2)
	{
		cout << "val2 : " << *val2 << endl;
	}
}

 

키에 대한 expire 시간 설정 및 정수, 실수 값만큼 증감

static void example2()
{
	auto val = redis->get("key");
	if (val)
	{
		cout << "val : " << *val << endl;
	}
	
	redis->expire("key", 5s);

	this_thread::sleep_for(6s);

	val = redis->get("key");
	if (!val)
	{
		cout << "value expired" << endl;
	}

	redis->set("int", "0");
	redis->incr("int");

	auto int_val = redis->get("int");
	if (int_val)
	{
		cout << "int val after incr 1 : " << *int_val << endl;
	}

	redis->incrby("int", 10);

	int_val = redis->get("int");
	if (int_val)
	{
		cout << "int val after incr 1 : " << *int_val << endl;
	}
}

 

value가 list 타입으로 사용

deque과 같이 양방향으로 삽입이 가능

  • lpush : 왼쪽부터 삽입
  • rpush: 오른쪽부터 삽입
  • lpop: 왼쪽에서 하나 빼기
  • rpop : 오른쪽에서 하나 빼기
  • lrange : 인덱스 범위로 list 가져오기
static void example3()
{
	vector<string> elements = { "a", "b", "c", "d", "e" };
	redis->lpush("my_list", elements.begin(), elements.end());

	vector<string> out_elements;
	redis->lrange("my_list", 0, -1, back_inserter(out_elements));

	cout << "my_list : ";
	for (const auto& s : out_elements)
		cout << s << ", ";
	cout << endl;

	redis->rpush("my_list", "apple");
	redis->lpop("my_list");
	redis->lpop("my_list");

	out_elements.clear();
	redis->lrange("my_list", 0, -1, back_inserter(out_elements));
	cout << "my_list : ";
	for (const auto& s : out_elements)
		cout << s << ", ";
	cout << endl;
}

 

정렬되지 않은 set

  • sadd : 원소 추가
  • scard : 원소 개수
  • smembers : 모든 원소 가져오기
  • sinter : 두 셋의 교차 원소 가져오기
  • sismember : 셋에 있는 원소 인 지 판별
// unordered_set
static void example4()
{
	redis->sadd("my_unord_set", "ppp");
	redis->sadd("my_unord_set", "ccc");
	redis->sadd("my_unord_set", "qqq");

	int card = redis->scard("my_unord_set"); // 3
	cout << "set cardinality : " << card << endl;

	vector<string> unord_set;
	redis->smembers("my_unord_set", back_inserter(unord_set));
	cout << "my_unord_set";
	for (auto& p : unord_set)
		cout << p << ", ";
	cout << endl;

	redis->sadd("my_unord_set2", "aaa");
	redis->sadd("my_unord_set2", "bbb");
	redis->sadd("my_unord_set2", "ccc");

	vector<string> unord_set2;
	redis->smembers("my_unord_set2", back_inserter(unord_set2));
	cout << "my_unord_set2";
	for (auto& p : unord_set2)
		cout << p << ", ";
	cout << endl;

	vector<string> inter;
	vector<string> li = { "my_unord_set", "my_unord_set2" };
	redis->sinter(li.begin(), li.end(), back_inserter(inter));
	cout << "intersection : ";
	for (auto& p : inter)
		cout << p << ", ";
	cout << endl;
	
	bool bismem = redis->sismember("my_unord_set2", "ddd");
	cout << "ddd is member of my_unord_set2 : " << bismem;
}

 

정렬된 set

  • score 값에 따라 멤버(원소)가 정렬된 형태
  • zadd : 멤버와 score 값을 저장
  • zrange : 모든 멤버와 score 혹은 멤버만 가져오기
  • zscore : 멤버에 해당하는 score 가져오기
static void example5()
{
	redis->zadd("my_set", { make_pair("m1", 1.4), make_pair("m2", 2.1), make_pair("m3", -0.3), make_pair("m4", -1.1)});

	vector<pair<string, double>> with_scores;
	redis->zrange("my_set", 0, -1, back_inserter(with_scores));

	cout << endl;
	cout << "my_set"<<endl;
	for (const auto& p : with_scores)
	{
		cout << p.first << ": " << p.second << endl;
	}

	auto d = redis->zscore("my_set", "m2");
	if (d)
	{
		cout << "d score : " << *d << endl;
	}
}

 

value가 맵 인 경우 : hash

  • hmset : 모든  해시 맵 멤버 삽입
  • hset : 특정 hash key-value 쌍만 멤버로 삽입
  • hget : hash key에 해당하는 value 가져옴
  • hdel : hash key로 멤버 삭제
  • hexists : hash key가 존재하는 지 확인
static void example6()
{
	map<string, string> mm = { {"key1", "value1"}, {"key2" ,"value2"}, {"key3", "value3"} };
	redis->hmset("my_map", mm.begin(), mm.end());
	
	auto value3 = redis->hget("my_map", "key3");
	if (value3)
	{
		cout << "my_map key3: " << *value3 << endl;
	}

	redis->hset("my_map", make_pair("key4", "value4"));

	bool exists = redis->hexists("my_map", "key4");

	cout << "my_map key4 exists : " << exists << endl;

	redis->hdel("my_map", "key4");

	exists = redis->hexists("my_map", "key4");

	cout << "my_map key4 exists after delete : " << exists << endl;
}