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 ต้องยังทำงานต่อได้
