Development note/C#

[C#] Redis 데이터베이스를 접속해서 사용하는 방법 (StackExchange.Redis 라이브러리 사용법)

v명월v 2022. 2. 15. 18:43

안녕하세요. 명월입니다.


이 글은 C#에서 Redis 데이터베이스를 접속해서 사용하는 방법에 대한 글입니다.


Redis 데이터베이스는 RDB 종류가 아닌 NoSQL 종류의 Key-Value타입의 데이터베이스입니다. 간단하게 공유 메모리 데이터베이스입니다.

이전 글에서 Linux 환경에서 설치 및 사용하는 방법에 대해 설명한 적 있습니다.

링크 - [CentOS] Redis 데이터베이스 설치와 명령어 사용법


그 Redis 데이터베이스를 C#에서 사용해 보겠습니다.

먼저 C#에서 사용하기 위해서는 Nuget을 이용해서 StackExchange.Redis 라이브러리를 설치합니다.

그럼 라이브러리가 설치되었으면 Redis 데이터베이스에 값을 저장하고 취득하겠습니다.

using System;
using StackExchange.Redis;

namespace ConsoleApp1
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // redis 접속한다.
      ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(new ConfigurationOptions
      {
        EndPoints = { "192.168.1.200:6379" }
      });
      // 기본 데이터베이스 취득
      var db = redis.GetDatabase();
      // ping pong 확인
      var pong = db.Ping();
      // 콘솔 출력
      Console.WriteLine(pong);

      // test 키로 hello world 값을 넣는다. 만료시간은 10분입니다.
      db.StringSet("test", "hello world", TimeSpan.FromMinutes(10));
      // test 키로 데이터를 취득한다.
      string data = db.StringGet("test");
      // 콘솔 출력
      Console.WriteLine(data);

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

위 소스에서 test의 키로 hello world를 넣었습니다. 그리고 10분 뒤에는 자동으로 key-value가 사라집니다.

그리고 test키로 취득하니 입력한 값이 콘솔에 출력이 되네요.


관련 함수에 관해서는 API 도큐멘트를 참조하세요.

링크 - https://stackexchange.github.io/StackExchange.Redis/


위에는 단순히 key 값으로 데이터를 넣어서 취득하는 기본적인 처리입니다.

그럼 실제 프로그램 작성할 때 자주 사용할 만한 처리식을 만들어 보겠습니다.

using System;
using System.IO;
using StackExchange.Redis;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace ConsoleApp1
{
  class Program
  {
    // 직렬화 타입
    [Serializable]
    // 예제 클래스
    class Node
    {
      // 맴버 변수
      private string data;
      // 생성자
      public Node(string data)
      {
        // 맴버 변수 설정
        this.data = data;
      }
      // 출력 함수
      public void Print()
      {
        // 콘솔 출력
        Console.WriteLine(this.data);
      }
    }
    // Node 클래스를 직렬화하여 binary로 바꾼뒤 String 타입으로 변환
    static String ConvertToStringFromClass(Node node)
    {
      // 직렬화 클래스
      var formatter = new BinaryFormatter();
      // 클래스를 직렬화하여 보관할 데이터
      byte[] data;
      // 메모리 스트림
      using (MemoryStream stream = new MemoryStream())
      {
        // 클래스를 직렬화하여 stream으로 변환
        formatter.Serialize(stream, node);
        // 스트림을 byte[] 데이터로 변환한다.
        data = stream.GetBuffer();
      }
      // 값의 변형이 없게 ASCII 코드로 String 타입으로 변환
      return Encoding.ASCII.GetString(data);
    }
    // 실행 함수
    static void Main(string[] args)
    {
      // redis 접속한다.
      ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(new ConfigurationOptions
      {
        EndPoints = { "192.168.1.200:6379" }
      });
      // 기본 데이터베이스 취득
      var db = redis.GetDatabase();

      // 인스턴스 생성
      var node = new Node("Test");
      // Node 인스턴스를 string 타입으로 변환
      var data = ConvertToStringFromClass(node);
      // Redis 데이터베이스에 test 키로 저장 (만료시간 10분)
      db.StringSet("test", data, TimeSpan.FromMinutes(10));

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

위 소스에서 Node 클래스의 인스턴스를 직렬화로 이용하여 byte로 바꾸고 다시 String 타입으로 변환하여 Redis 데이터베이스에 입력했습니다.

그럼 이걸 다른 형식의 프로그램에서 취득할 수 있을까?

using System;
using System.IO;
using StackExchange.Redis;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace ConsoleApp1
{
  class Program
  {
    // 직렬화 타입
    [Serializable]
    // 예제 클래스
    class Node
    {
      // 맴버 변수
      private string data;
      // 생성자
      public Node(string data)
      {
        // 맴버 변수 설정
        this.data = data;
      }
      // 출력 함수
      public void Print()
      {
        // 콘솔 출력
        Console.WriteLine(this.data);
      }
    }
    // String 타입으로 byte로 변환하여 직렬화를 통해 인스턴스로 변환
    static Node ConvertToClassFromString(String val)
    {
      // String을 통해서 byte로 변환
      var binary = Encoding.ASCII.GetBytes(val);
      // 직렬화 클래스
      var formatter = new BinaryFormatter();
      // 메모리 스트림
      using (MemoryStream stream = new MemoryStream(binary))
      {
        // stream을 직렬화하여 인스턴스로 변환
        return (Node)formatter.Deserialize(stream);
      }
    }
    // 실행 함수
    static void Main(string[] args)
    {
      // redis 접속한다.
      ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(new ConfigurationOptions
      {
        EndPoints = { "192.168.1.200:6379" }
      });
      // 기본 데이터베이스 취득
      var db = redis.GetDatabase();

      // test 키로 데이터를 취득한다.
      var data = db.StringGet("test");
      // string 타입을 Node 인스턴스로 변환
      var node = ConvertToClassFromString(data);
      // Node 클래스의 Print 함수 호출
      node.Print();

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

다른 프로그램에서 생성된 Node 인스턴스를 redis 데이터베이스에 넣고, 다시 다른 프로그램에서 취득하여 인스턴스로 변환하여 사용할 수 있는 것을 확인했습니다.

이 뜻은 직렬화를 통해서 클래스 형식으로 여러 프로그램에서 값을 공유할 수 있다는 뜻입니다. C#의 직렬화이니 다른 언어 프로그램에서는 사용할 수 없고 같은 언어인 C#에서만 사용이 가능하겠네요..


다른 언어간에 사용하려면 Json 타입으로 변환해서 사용하게 되면 언어가 다른 경우에도 값을 공유할 수 있겠네요.

using System;
using StackExchange.Redis;
using Newtonsoft.Json;

namespace ConsoleApp1
{
  class Program
  {
    // 예제 클래스
    class Node
    {
      // 맴버 변수
      public string Data { get; set; }
      // 생성자
      public Node(string data)
      {
        // 맴버 변수 설정
        this.Data = data;
      }
      // 출력 함수
      public void Print()
      {
        // 콘솔 출력
        Console.WriteLine(this.Data);
      }
    }
    // 실행 함수
    static void Main(string[] args)
    {
      // redis 접속한다.
      ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(new ConfigurationOptions
      {
        EndPoints = { "192.168.1.200:6379" }
      });
      // 기본 데이터베이스 취득
      var db = redis.GetDatabase();

      // 인스턴스 생성
      var node = new Node("Test");
      // 인스턴스를 Json 타입으로 변환
      var data = JsonConvert.SerializeObject(node);
      // Redis 데이터베이스에 test1 키로 저장 (만료시간 10분)
      db.StringSet("test1", data, TimeSpan.FromMinutes(10));

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

위 직렬화를 통해서 String으로 변환하여 Redis 데이터베이스에 저장한 것과 비슷합니다만 이번에는 직렬화가 아닌 Json 구조 타입으로 변환해서 입력했습니다.

using System;
using StackExchange.Redis;
using Newtonsoft.Json;

namespace ConsoleApp1
{
  class Program
  {
    // 예제 클래스
    class Node
    {
      // 맴버 변수
      public string Data { get; set; }
      // 생성자
      public Node(string data)
      {
        // 맴버 변수 설정
        this.Data = data;
      }
      // 출력 함수
      public void Print()
      {
        // 콘솔 출력
        Console.WriteLine(this.Data);
      }
    }
    // 실행 함수
    static void Main(string[] args)
    {
      // redis 접속한다.
      ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(new ConfigurationOptions
      {
        EndPoints = { "192.168.1.200:6379" }
      });
      // 기본 데이터베이스 취득
      var db = redis.GetDatabase();

      // test1 키로 데이터를 취득한다.
      var data = db.StringGet("test1");
      // Json 타입의 String타입을 인스턴스로 변환
      var node = JsonConvert.DeserializeObject<Node>(data);
      // Node 클래스의 Print 함수 호출
      node.Print();

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

Redis 데이터베이스에서 test1의 key값으로 Json 타입의 String값을 취득한 후 Node 인스턴스로 변환했습니다.

역시 Print 함수를 호출하니 위에서 입력한 Test 값이 콘솔에 출력이 되었습니다.


다음은 Redis에서 사용될 List와 Map, SortedSet 형식의 자료형입니다.

using System;
using StackExchange.Redis;

namespace ConsoleApp1
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // redis 접속한다.
      ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(new ConfigurationOptions
      {
        EndPoints = { "192.168.1.200:6379" }
      });
      // 기본 데이터베이스 취득
      var db = redis.GetDatabase();

      // List 형식 오른쪽 Push
      db.ListRightPush("list", "1");
      // List 형식 왼쪽 Push
      db.ListLeftPush("list", "2");
      // 2,1
      db.ListSetByIndex("list", 1, "3");
      // 2,3
      // List 왼쪽부터 출력
      Console.WriteLine(db.ListLeftPop("list"));
      Console.WriteLine(db.ListLeftPop("list"));

      // 개행
      Console.WriteLine();
      // Hash 형식의 key-value 값 입력
      db.HashSet("map", "a", "1");
      db.HashSet("map", "b", "2");
      db.HashSet("map", "c", "3");
      // {{"a","1"},{"b","2"},{"c","3"}}

      // map의 b 키로 출력
      Console.WriteLine(db.HashGet("map", "b"));

      // 개행
      Console.WriteLine();
      // SortedSet 형식의 값 입력
      db.SortedSetAdd("SortedSet", "aaa", 1);
      db.SortedSetAdd("SortedSet", "bbb", 0);

      // 입력 순서대로 sort
      foreach(var sort in db.SortedSetScan("SortedSet"))
      {
        // 콘솔 출력
        Console.WriteLine(sort);
      }
      // 개행
      Console.WriteLine();
      // Score 순위로 sort
      foreach (var sort in db.SortedSetRangeByRank("SortedSet"))
      {
        // 콘솔 출력
        Console.WriteLine(sort);
      }
      // 개행
      Console.WriteLine();
      // aaa의 Socre
      Console.WriteLine(db.SortedSetScore("SortedSet", "aaa"));

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

위는 Redis에서 list와 map, SortedSet 형식으로 사용되는 타입입니다.


사실 저는 잘 사용하지 않는 타입이기도 합니다.

Redis 프로그램의 알고리즘이 나쁘다고는 생각하지는 않지만 Redis 데이터베이스의 성능에 대한 병목 현상이나 알고리즘의 성능이 C#의 기본 List나 Dictionary보다 더 좋다고 생각되지는 않아서 가능하면 데이터를 그대로 넣고 취득해서 프로그램 내부에서 처리합니다.

제가 아직 Redis의 경험이 많지 않아서 정확하게는 잘 모르겠네요.


여기까지 C#에서 Redis 데이터베이스를 접속해서 사용하는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.