madebydinhire me →
← back

Jun 2026

วิธีใช้ Redis ใน Service go แบบเข้าใจง่าย

Redis คืออะไร

Redis คือฐานข้อมูลที่เก็บข้อมูลไว้ใน RAM (หน่วยความจำ) ไม่ใช่ในดิสก์เหมือน PostgreSQL หรือ MySQL ทำให้มันเร็วมาก ๆ อ่าน-เขียนระดับไมโครวินาที

ลองนึกภาพง่าย ๆ ว่า ถ้า PostgreSQL คือตู้เก็บเอกสารที่ต้องเดินไปเปิดลิ้นชักหา Redis ก็คือกระดาษโน้ตที่แปะไว้หน้าจอ หยิบมาดูได้ทันที

ข้อแลกเปลี่ยนคือ RAM มีจำกัดและแพงกว่าดิสก์ เราเลยไม่เก็บทุกอย่างไว้ใน Redis แต่เก็บเฉพาะข้อมูลที่ต้องเข้าถึงบ่อยและต้องการความเร็ว


ใช้ Redis ทำอะไรได้บ้าง

1. Cache (แคช) — งานยอดฮิตที่สุด

แทนที่จะ query ฐานข้อมูลทุกครั้ง เราเก็บผลลัพธ์ไว้ใน Redis ก่อน รอบหน้าถ้ามีข้อมูลอยู่แล้วก็หยิบจาก Redis ได้เลย ไม่ต้องไปกวน DB

func (s *Service) GetUser(ctx context.Context, id string) (*User, error) {
    key := "user:" + id

// ลองหยิบจาก cache ก่อน cached, err := s.redis.Get(ctx, key).Result() if err == nil { var u User if err := json.Unmarshal([]byte(cached), &u); err == nil { return &u, nil // เจอใน cache เลยคืนค่าทันที } }

// ไม่เจอใน cache ค่อยไปถาม DB u, err := s.repo.FindUser(ctx, id) if err != nil { return nil, err }

// เก็บลง cache ไว้ใช้รอบหน้า ตั้งหมดอายุ 5 นาที data, _ := json.Marshal(u) s.redis.Set(ctx, key, data, 5*time.Minute)

return u, nil }

แพทเทิร์นนี้เรียกว่า Cache-Aside คือ "หาใน cache ก่อน ไม่เจอค่อยไป DB แล้วเก็บกลับเข้า cache"

2. Session / Token เก็บข้อมูลที่หมดอายุได้

Redis ตั้งเวลาหมดอายุ (TTL) ให้ key ได้ พอครบเวลาข้อมูลหายไปเอง เหมาะกับเก็บ session, OTP, หรือ token ชั่วคราว

// เก็บ OTP 5 นาที พอครบเวลาหายเอง ไม่ต้องเขียน job มาลบ
s.redis.Set(ctx, "otp:"+phone, code, 5*time.Minute)

3. Rate Limiting — จำกัดจำนวนครั้งที่เรียก API

เช่น ให้ยิง API ได้ไม่เกิน 100 ครั้งต่อนาที

func (s *Service) AllowRequest(ctx context.Context, userID string) bool {
    key := "ratelimit:" + userID
    count, _ := s.redis.Incr(ctx, key).Result()

if count == 1 { s.redis.Expire(ctx, key, time.Minute) // ตั้งหมดอายุครั้งแรก } return count <= 100 }

4. Distributed Lock — กันงานทำซ้ำตอนมีหลาย instance

ถ้า service รันหลาย pod พร้อมกัน บางงานต้องให้ทำได้แค่ตัวเดียว เช่น ตัดเงินรอบเดียว เราใช้ SetNX (set ถ้ายังไม่มี key) เป็นกุญแจล็อก

ok, _ := s.redis.SetNX(ctx, "lock:job", "1", 30*time.Second).Result()
if !ok {
    return // มีตัวอื่นทำอยู่แล้ว ข้ามไป
}
defer s.redis.Del(ctx, "lock:job")
// ทำงานที่ต้องการล็อกตรงนี้

ข้อควรระวัง

ตั้ง TTL เสมอ — ถ้าไม่ตั้งหมดอายุ ข้อมูลจะค้างใน RAM เรื่อย ๆ จน memory เต็ม (เคสนี้เจอบ่อยจนโดน OOM)

Cache อาจไม่ตรงกับ DB — เวลาอัปเดตข้อมูลใน DB อย่าลืมลบหรืออัปเดต cache ด้วย ไม่งั้นผู้ใช้จะเห็นข้อมูลเก่า

func (s *Service) UpdateUser(ctx context.Context, u *User) error {
    if err := s.repo.UpdateUser(ctx, u); err != nil {
        return err
    }
    s.redis.Del(ctx, "user:"+u.ID) // ลบ cache เก่าทิ้ง
    return nil
}

Redis ล่มได้ — อย่าให้ service พังตาม ถ้าต่อ Redis ไม่ได้ ให้ fallback ไปอ่าน DB แทน (cache เป็นตัวเสริม ไม่ใช่ของจำเป็น)

Cache Stampede — ถ้า cache หมดอายุพร้อมกันแล้วมีคนเรียกเยอะ ๆ ทุก request จะวิ่งไป DB พร้อมกัน แก้ได้ด้วย lock ให้ตัวเดียววิ่งไปเติม cache ที่เหลือรอ


สรุป

Redis คือเครื่องมือเก็บข้อมูลใน RAM ที่เร็วมาก เหมาะกับ:

  • Cache — ลดภาระ DB
  • Session / OTP / Token — ข้อมูลชั่วคราวที่หมดอายุเองได้
  • Rate Limiting — จำกัดจำนวนการเรียก
  • Distributed Lock — กันงานทำซ้ำตอนมีหลาย instance

หลักคิดง่าย ๆ คือ ใช้ Redis เก็บของที่อ่านบ่อยและต้องการความเร็ว ตั้ง TTL เสมอ และอย่าลืมว่ามันเป็นตัวเสริม ถ้ามันล่ม service ต้องยังทำงานต่อได้

วิธีใช้ Redis ใน Service go แบบเข้าใจง่าย 1