Redis: แคชด่วน! เอามาใช้กับ Python ให้เว็บเร็วขึ้น

โอเค คือหลายทีที่เราทำเว็บ ทำ API แล้วมันช้าใช่ป่ะ? แบบว่าทุก request ต้องไป query database ตลอดเลยงี้ บางทีข้อมูลมันไม่ได้เปลี่ยนบ่อยอะ ทำไมต้องไปดึงมาใหม่ทุกรอบ? นี่แหละ ที่มาของ "แคช" หรือ Cache นั่นเอง

ตัวเลือกที่คนชอบใช้กันเยอะๆ ก็มี Redis นี่แหละ. มันเป็น in-memory data store คือเก็บข้อมูลใน RAM ทำให้มันโคตรเร็ว! เหมาะกับการเอามาทำแคชสุดๆ นอกจากแคชก็ใช้ทำอย่างอื่นได้เยอะนะ เช่น message queue, pub/sub ไรงี้ แต่บทความนี้จะเน้นเรื่องแคชละกัน

เริ่มต้น Redis ยังไงดี? (แบบง่ายๆนะ)

ง่ายสุดก็ Docker ครับพี่น้อง! ถ้ามี Docker อยู่แล้วก็พิมพ์ไปเลย

docker run --name my-redis -p 6379:6379 -d redis

แค่นี้ก็ได้ Redis โผล่มาแล้ว ที่พอร์ต 6379 (ซึ่งเป็น default ของมัน)

Python คุยกับ Redis

ใน Python เราใช้ redis-py ครับ install ก่อนเลย

pip install redis

แล้วลองโค้ดนี้ดู:

import redis

# เชื่อมต่อ Redis
# host='localhost' เพราะเรา run Docker บนเครื่องเดียวกัน
# port=6379 ก็ default ของมัน
r = redis.Redis(host='localhost', port=6379, db=0)

# ลองเก็บข้อมูล
r.set('my_key', 'Hello Redis from Python!')
print("ข้อมูลถูกเก็บแล้ว!")

# ลองดึงข้อมูล
value = r.get('my_key')
print(f"ดึงข้อมูลได้: {value.decode('utf-8')}") # ต้อง decode เป็น utf-8 นะ

# ตั้งค่าให้มันหมดอายุ (expire)
r.setex('temp_key', 10, 'อันนี้จะหายไปใน 10 วิ')
print("ตั้งค่า temp_key หมดอายุ 10 วิ")

import time
time.sleep(5)
print(f"5 วิผ่านไป temp_key ยังอยู่ไหม?: {r.get('temp_key')}")
time.sleep(6) # รอเพิ่มอีก 6 วิ รวมเป็น 11 วิ
print(f"11 วิผ่านไป temp_key ยังอยู่ไหม?: {r.get('temp_key')}") # ควรเป็น None แล้ว

ลองรันดู จะเห็นว่า temp_key มันหายไปเองหลัง 10 วิ เจ๋งปะล่ะ!

เอามาทำแคชให้ฟังก์ชัน

สมมติว่าเรามีฟังก์ชันที่มันใช้เวลานาน เช่นไปดึงข้อมูลจาก database ที่กว่าจะ process เสร็จเป็นวินาทีงี้ เราก็เอา Redis มาช่วยแคชผลลัพธ์ได้ง่ายๆ เลย

import redis
import json
import time

r = redis.Redis(host='localhost', port=6379, db=0)

def get_data_from_db_slowly(user_id):
    """
    ฟังก์ชันจำลองการดึงข้อมูลจาก DB แบบโคตรช้า
    """
    print(f"กำลังดึงข้อมูล user_id: {user_id} จาก DB จริงๆ...")
    time.sleep(3) # แกล้งว่าช้า 3 วิ
    return {"id": user_id, "name": f"User {user_id}", "email": f"user{user_id}@example.com"}

def cached_get_user_data(user_id):
    cache_key = f"user_data:{user_id}"

    # ลองเช็คใน Redis ก่อน
    cached_data = r.get(cache_key)
    if cached_data:
        print(f"🎯 ได้ข้อมูลจาก Redis สำหรับ user_id: {user_id}")
        return json.loads(cached_data.decode('utf-8'))

    # ถ้าไม่มีใน Redis ก็ไปดึงจาก DB จริงๆ
    data = get_data_from_db_slowly(user_id)

    # เอาข้อมูลไปเก็บใน Redis พร้อมตั้งเวลาหมดอายุ (สมมติ 60 วิ)
    r.setex(cache_key, 60, json.dumps(data))
    print(f"💾 เก็บข้อมูล user_id: {user_id} ลง Redis แล้ว")
    return data

# ลองเรียกครั้งแรก (จะช้า)
print("\n--- เรียกครั้งแรก ---")
user_1_data = cached_get_user_data(1)
print(user_1_data)

# ลองเรียกครั้งที่สอง (จะเร็ว)
print("\n--- เรียกครั้งที่สอง ---")
user_1_data_cached = cached_get_user_data(1)
print(user_1_data_cached)

# ลองเรียก user คนอื่น (จะช้าอีก)
print("\n--- เรียก user คนใหม่ ---")
user_2_data = cached_get_user_data(2)
print(user_2_data)

จะเห็นเลยว่าเรียกซ้ำๆ สำหรับ user_id=1 มันเร็วขึ้นมาก เพราะไปดึงจากแคชแทน

ปัญหาที่อาจจะเจอ (เคยเจอมากับตัว)

บางทีเราแคชข้อมูลไว้แล้ว ข้อมูลใน DB จริงๆ มันเปลี่ยนไป เช่น อัพเดทชื่อ user งี้ แต่ในแคชยังเป็นข้อมูลเก่าอยู่เลย... อันนี้คือ Cache Invalidation ครับ.

วิธีแก้เบื้องต้น: 1. ตั้งเวลาหมดอายุ (TTL): แบบที่ทำ setex ไปเมื่อกี้แหละ ให้มันหายไปเองหลังเวลาที่กำหนด. ง่ายสุดแต่ก็อาจจะเจอปัญหาข้อมูลไม่ตรงกันได้พักนึง. 2. Manual Invalidation: เวลาที่เราอัพเดทข้อมูลใน DB แล้ว ก็ไปสั่ง Redis ให้ลบ key นั้นทิ้งซะเลย (ใช้ r.delete(cache_key)). อันนี้แน่นอนสุด แต่ต้องเขียนโค้ดเพิ่มตอนอัพเดทข้อมูล.

อีกเรื่องคือถ้า Redis มันล่ม หรือคอนเนคไม่ได้ จะทำไง? แอปเราพังเลยปะ? เคยเจอแบบว่าลืมสตาร์ท Docker Redis ตอน Dev แล้วโค้ดมันพยายามไป r.get แล้วก็ ConnectionRefusedError โผล่มาเต็มหน้าจอ! ฮา...

redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.

วิธีแก้ก็ต้อง try-except ดีๆ ครับ หรือใช้ไลบรารีที่มันมี resilience ในตัว หรือ setup failover ให้ Redis แต่นั่นมันเรื่อง Advance ละ เอาแค่ try-except ก็น่าจะพอสำหรับเริ่มๆ

try:
    r.set('test_key', 'ok')
    print("Redis connected and working.")
except redis.exceptions.ConnectionError as e:
    print(f"เชื่อมต่อ Redis ไม่ได้โว้ย!: {e}")
    # อาจจะ fallback ไปดึงจาก DB ตรงๆ หรือ log error

สรุปง่ายๆ

Redis เป็นเครื่องมือที่ดีมากสำหรับการทำแคช เพราะมันเร็วโคตรๆ แค่ต้องเข้าใจเรื่องพวก cache invalidation กับการ handle error ตอนคอนเนคไม่ได้นิดหน่อย.

ลองเอาไปใช้ดูนะครับ รับรองว่าเว็บหรือ API ที่คุณทำมันจะวิ่งได้เร็วขึ้นเยอะเลยล่ะ! ไม่ต้องกลัวเรื่องคอขวดจาก DB ละ :D

Read more

ไอลีน กู: ตำนานนักสกีฟรีสไตล์ผู้พลิกโฉมวงการและความหมายของชัยชนะ

ไอลีน กู: ตำนานนักสกีฟรีสไตล์ผู้พลิกโฉมวงการและความหมายของชัยชนะ

เจาะลึกเรื่องราวของ Eileen Gu นักสกีฟรีสไตล์ผู้สร้างประวัติศาสตร์ในโอลิมปิก 2026 สถิติที่ไม่เคยมีมาก่อน ประเด็นถกเถียง และความแข็งแกร่งส่วนตัวที่ทำให้เธอก้าวสู่ระดับโลก

By ทีมงาน devdog
วันพระ: คู่มือฉบับสมบูรณ์สำหรับพุทธศาสนิกชนและผู้สนใจยุคใหม่

วันพระ: คู่มือฉบับสมบูรณ์สำหรับพุทธศาสนิกชนและผู้สนใจยุคใหม่

เจาะลึกวันพระและความสำคัญของวันมาฆบูชา 2569 ทั้งวันหยุดราชการ ธนาคาร กิจกรรมเวียนเทียนต้นไม้ และผลกระทบต่อบริการขนส่ง เตรียมตัววางแผนทำบุญและพักผ่อน

By ทีมงาน devdog
ถอดรหัสรักแท้: "บังมัดคลองตันต้นข้าว" เรื่องราวที่สะท้อนการให้อภัยและการเริ่มต้นใหม่

ถอดรหัสรักแท้: "บังมัดคลองตันต้นข้าว" เรื่องราวที่สะท้อนการให้อภัยและการเริ่มต้นใหม่

เจาะลึกงานวิวาห์ "บังมัดคลองตัน" กับ "ต้นข้าว มิสแกรนด์" พร้อมเหตุผลจากใจเจ้าสาวที่เลือกความรักเหนือกาลเวลาและคำวิจารณ์ สู่การเริ่มต้นชีวิตคู่ที่สะท้อนการให้อภัย

By ทีมงาน devdog
ไฮไลท์บอลไทยลีก 2: มหาสารคาม เอสบีที เอฟซี กับฟอร์มร้อนแรงสู่เส้นทางเพลย์ออฟ

ไฮไลท์บอลไทยลีก 2: มหาสารคาม เอสบีที เอฟซี กับฟอร์มร้อนแรงสู่เส้นทางเพลย์ออฟ

เจาะลึกไฮไลท์บอลไทยลีก 2 ของมหาสารคาม เอสบีที เอฟซี กับฟอร์มร้อนแรง ชัยชนะสำคัญจาก ชิตชนก และบทบาทโค้ชดุสิต สู่เส้นทางเพลย์ออฟที่น่าจับตา!

By ทีมงาน devdog