Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

Redis เป็น Primary Data Store ใน .NET 8 Web API

บทนำ

ในบทความนี้ ผมจะพูดถึง Redis และกรณีการใช้งานในฐานะ Primary Database เพื่อเก็บข้อมูลของเรา

  • บทนำ Redis
  • ชนิดข้อมูล Redis (Redis Datatypes)
  • ข้อดีและข้อเสียของการใช้ Redis เป็น Database
  • การตั้งค่า Redis ใน Container
  • การใช้ Redis เป็น Primary Database กับ .NET 8 Web API

ข้อกำหนดเบื้องต้น

  • Visual Studio Code
  • .NET 8 SDK
  • Redis Desktop Manager (ดาวน์โหลดได้จาก https://redis.io/resources/tools/)
  • Docker Desktop
  • Packages:
    • Microsoft.Extensions.Caching.StackExchangeRedis
    • StackExchange.Redis

บทนำ Redis

Redis เป็น datastore แบบ in-memory และ key-value datastore ใช้ส่วนใหญ่สำหรับ caching layer เนื่องจาก Redis เก็บข้อมูลในหน่วยความจำ (memory) จึงทำให้การอ่านและเขียนข้อมูลเร็วมาก เหมาะสำหรับการเข้าถึงข้อมูลที่ต้องการ low-latency คุณอาจจะถามว่า Redis เก็บข้อมูลใน memory แต่เมื่อระบบรีบูต เราจะสูญเสียข้อมูลหรือไม่

เพื่อแก้ปัญหาการ persistence ของข้อมูล Redis มีกลไกต่างๆ เช่น snapshots (RDB) และ append-only files (AOF) กลไกเหล่านี้ช่วยให้คุณเก็บข้อมูลบนดิสก์อย่างถาวร ทำให้สามารถกู้คืนข้อมูลได้แม้หลังจากระบบรีบูต คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับ Redis Persistence ได้จากเอกสารอย่างเป็นทางการ

Redis ใช้โมเดล Document database โดยเก็บข้อมูลในรูปแบบเอกสารส่วนใหญ่เป็น JSON format คุณสามารถอ่านเพิ่มเติมได้จากเอกสารอย่างเป็นทางการ

ชนิดข้อมูล Redis (Redis Datatypes)

Redis มีชนิดข้อมูลหลายแบบที่ช่วยให้เราแก้ปัญหาต่างๆ ได้ ตั้งแต่ caching, queuing, และ event processing

  • Strings: แทนลำดับของไบต์ (bytes)
  • Lists: รายการของ strings
  • Sets: ชุดเก็บข้อมูล string แบบไม่มีลำดับและไม่ซ้ำกัน
  • Hashes: ประเภท record ที่สร้างแบบ field-value pairs

คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับชนิดข้อมูลได้จากเอกสารอย่างเป็นทางการ

ข้อดีของการใช้ Redis เป็น Database

  • High Performance (ประสิทธิภาพสูง)
  • Versatile DataStructure (โครงสร้างข้อมูลหลากหลาย)
  • Low Latency (ความหน่วงต่ำ)
  • Scalability (การขยายตัวได้)

ข้อเสียของการใช้ Redis เป็น Database

  • Data Durability (ความทนทานของข้อมูล - ต้องตั้งค่า persistence ให้ถูกต้อง)
  • Limited Query Capabilities (ความสามารถในการ query จำกัด - ไม่มี SQL หรือ complex queries)
  • Memory Constraints (ข้อจำกัดหน่วยความจำ - ขึ้นกับ RAM ที่มีอยู่)

การตั้งค่า Redis ใน Container

ตรวจสอบว่า Docker Desktop กำลังทำงานอยู่ และเปิด Visual Studio 2022

เลือก "Create a New Project" → คลิก "ASP.NET Core Web API" แล้วคลิก "Next"

Redis เป็น Primary Data Store ใน .NET 8 Web API

ในหน้า Configure ใส่ชื่อโปรเจกต์ แล้วกด "Next"

Redis เป็น Primary Data Store ใน .NET 8 Web API

ในหน้า Additional information เลือกข้อมูลตามภาพ และกด "Create"

สร้างไฟล์ "docker-compose.yaml" เพื่อเก็บการกำหนดค่าสำหรับ Redis และวางโค้ดด้านล่าง

version: '3.8'
services:
  redis:
    image: redis:alpine
    container_name: redisStudentAPI
    ports:
      - 6379:6379

หลังจากนั้น เปิด Developer Powershell prompt จาก Tools → Command line → Developer Powershell

นำทางไปในโฟลเดอร์โปรเจกต์ และรันคำสั่งเพื่อรันไฟล์ YAML: docker compose up -d

Redis เป็น Primary Data Store ใน .NET 8 Web API

เปิด Docker Desktop ไปที่ containers คุณจะเห็น container ที่สร้างจาก YAML file สามารถรัน docker ps เพื่อดู containers ที่กำลังทำงานได้

เพื่อโต้ตอบกับ Redis Server ให้รันคำสั่งด้านล่างพร้อม container ID

docker exec -it <container_id> /bin/sh

การตั้งค่า Docker พื้นฐานเสร็จสิ้น เราสามารถโต้ตอบกับ Redis ผ่าน command line ได้แล้ว

ตอนนี้ เราจะดูเพิ่มเติมเกี่ยวกับชนิดข้อมูล Strings เนื่องจากเราจะเก็บ key-value pairs เป็น strings

Strings

เป็นชนิดค่าที่ง่ายที่สุดที่เชื่อมโยงกับ key และเป็นการแมป one-to-one ระหว่าง key และ value

เราสามารถตั้งค่าได้โดยใช้ SET <key> <value> และดึงค่าได้โดย GET <key>

สามารถลบ key ได้โดยใช้ DEL <key>

ตอนนี้ ติดตั้งแพ็กเกจที่จำเป็นจาก NuGet Package Manager

Redis เป็น Primary Data Store ใน .NET 8 Web API

เพิ่มการกำหนดค่าในไฟล์ Program.cs

Program.cs

builder.Services.AddSingleton<IConnectionMultiplexer>(options => 
    ConnectionMultiplexer.Connect(("127.0.0.1:6379")));
builder.Services.AddScoped<IStudentRepository, StudentRepository>();

สร้างโฟลเดอร์ Models และสร้างไฟล์ Model Student.cs แล้ววางโค้ด

Student.cs

namespace StudentAPIWithRedisDB.Models {
    public class Student {
        public string Id { get; set; } = $"student:{Guid.NewGuid().ToString()}";
        public required string StudentName { get; set; } = string.Empty;
    }
}

คัดลอกและวางโค้ดด้านล่างในไฟล์ที่เกี่ยวข้อง

IStudentRepository.cs

using StudentAPIWithRedisDB.Models;
namespace StudentAPIWithRedisDB.Data {
    public interface IStudentRepository {
        IEnumerable<Student> GetAllStudents();
        Student? GetStudentById(string id);
        void AddStudent(Student student);
        Student? UpdateStudent(Student student);
        Student? DeleteStudent(string id);
    }
}

StudentRepository.cs - การนำ interface ไปใช้งาน

using StackExchange.Redis;
using StudentAPIWithRedisDB.Models;
using System.Text.Json;
namespace StudentAPIWithRedisDB.Data {
    public class StudentRepository : IStudentRepository {
        private readonly IConnectionMultiplexer _redis;
        public StudentRepository(IConnectionMultiplexer redis) {
            _redis = redis;
        }
        public void AddStudent(Student student) {
            if(student == null) {
                throw new ArgumentOutOfRangeException(nameof(student));
            }
            var db = _redis.GetDatabase();
            var serializedStudent = JsonSerializer.Serialize(student);
            db.StringSet(student.Id, serializedStudent);
        }
        public Student? DeleteStudent(string id) {
            var db = _redis.GetDatabase();
            var student = db.StringGet(id);
            if (student.IsNullOrEmpty) {
                return null;
            }
            db.KeyDelete(id);
            return JsonSerializer.Deserialize<Student>(student);
        }
        public IEnumerable<Student> GetAllStudents() {
            var db = _redis.GetDatabase();
            var studentKeys = db.Multiplexer.GetServer(_redis.GetEndPoints().First()).Keys(pattern: "student:*");
            var students = new List<Student>();
            foreach (var key in studentKeys) {
                var studentJson = db.StringGet(key);
                if (!studentJson.IsNullOrEmpty) {
                    var student = JsonSerializer.Deserialize<Student>(studentJson);
                    students.Add(student);
                }
            }
            return students;
        }
        public Student? GetStudentById(string id) {
            var db = _redis.GetDatabase();
            var student = db.StringGet(id);
            if(student.IsNullOrEmpty) {
                return null;
            }
            return JsonSerializer.Deserialize<Student>(student);
        }
        public Student UpdateStudent(Student student) {
            var db = _redis.GetDatabase();
            var id = student.Id;
            if (db.KeyExists(id)) {
                var updatedStudentJson = JsonSerializer.Serialize(student);
                db.StringSet(id, updatedStudentJson);
                return student;
            } else {
                return null;
            }
        }
    }
}

StudentsController.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using StudentAPIWithRedisDB.Data;
using StudentAPIWithRedisDB.Models;
namespace StudentAPIWithRedisDB.Controllers {
    [Route("api/[controller]")]
    [ApiController]
    public class StudentsController : ControllerBase {
        private readonly IStudentRepository _studentRepository;
        public StudentsController(IStudentRepository studentRepository) {
            _studentRepository = studentRepository;
        }
        [HttpGet("{Id}", Name = "GetStudentById")]
        public ActionResult<Student> GetStudentById(string Id) {
            var student = _studentRepository.GetStudentById(Id);
            if(student == null) {
                return NotFound();
            }
            return Ok(student);
        }
        [HttpPost]
        public ActionResult<Student> AddStudent(Student student) {
            _studentRepository.AddStudent(student);
            return CreatedAtRoute(nameof(GetStudentById), new { Id = student.Id }, student);
        }
        [HttpGet(Name = "GetAllStudents")]
        public ActionResult<Student> GetAllStudents() {
            var students = _studentRepository.GetAllStudents();
            return Ok(students);
        }
        [HttpDelete("{id}")]
        public ActionResult<Student> DeleteStudent(string id) {
            var student = _studentRepository.DeleteStudent(id);
            if(student == null) {
                return NotFound();
            }
            return Ok(student);
        }
        [HttpPatch]
        public ActionResult<Student> UpdateStudent(Student student) {
            var studentToUpdate = _studentRepository.GetStudentById(student.Id);
            if(studentToUpdate == null) {
                return NotFound();
            }
            _studentRepository.UpdateStudent(student);
            return NoContent();
        }
    }
}

ตอนนี้ รันแอปพลิเคชัน คุณจะเห็น endpoints บนเบราว์เซอร์ผ่าน Swagger

Redis เป็น Primary Data Store ใน .NET 8 Web API

เปิด Redis Desktop Manager เพื่อดูข้อมูล เริ่มต้นจะมี 16 databases โดยไม่มีข้อมูล

Redis เป็น Primary Data Store ใน .NET 8 Web API

เมื่อเรียก endpoint create จะสร้าง record ใน DB0 อัตโนมัติ

CREATE

เพื่อการทดสอบง่ายๆ ผมใช้ Postman ทดสอบ endpoints

ส่งแค่ชื่อ (name) ระบบจะสร้าง ID อัตโนมัติพร้อมชื่อนักเรียนและ suffix เป็น GUID ใหม่

Redis เป็น Primary Data Store ใน .NET 8 Web API

เอกสารถูกสร้างใน DB0

Redis เป็น Primary Data Store ใน .NET 8 Web API

GetStudentById

สำหรับนี้ ต้องคัดลอก ID จากเอกสาร แล้วส่งใน request URL

Redis เป็น Primary Data Store ใน .NET 8 Web API

จะคืนค่าตาม ID

Redis เป็น Primary Data Store ใน .NET 8 Web API

GetAllStudents

คืนค่าเอกสารทั้งหมดที่มีใน Database

Redis เป็น Primary Data Store ใน .NET 8 Web API

Update

ต้องส่ง ID และค่าที่อัปเดต เพื่ออัปเดตตาม ID

Redis เป็น Primary Data Store ใน .NET 8 Web API

รีเฟรช manager แล้วจะเห็นค่าที่อัปเดตแล้ว

Redis เป็น Primary Data Store ใน .NET 8 Web API

Delete

สำหรับการลบผู้ใช้ ส่ง ID แล้วจะลบเอกสารออกจาก Student DB

Redis เป็น Primary Data Store ใน .NET 8 Web API

เอกสารถูกลบจากไฟล์

Redis เป็น Primary Data Store ใน .NET 8 Web API

ก่อนหน้านี้ มี 2 เอกสารในตาราง Students ตอนนี้ตัวแรกถูกลบแล้ว

นี่คือวิธีง่ายๆ ในการแสดงการใช้ Redis เป็น Database ในแอปพลิเคชันของเรา ที่นี่ เราลด Cache ลงและเก็บข้อมูลโดยตรงใน Redis และดึงข้อมูลแก้ไขได้เช่นกัน คุณสามารถนำไปประยุกต์ใช้กับแอปพลิเคชันของคุณได้เช่นกัน