แคชข้อมูลด้วย Redis ใน Flask: เร็วขึ้นเยอะจริงดิ!

เบื่อไหมกับแอปพลิเคชันที่เราเขียนอยู่ แล้วมันก็ช้าเอาช้าเอา โดยเฉพาะตอนที่ต้องดึงข้อมูลจากฐานข้อมูลมาแสดงซ้ำๆ หน้าเดิมๆ เนี่ย? เออ...ใช่! มันเป็นงี้แหละ ปัญหาคลาสสิกเลยนะ

วันนี้เลยอยากมาลองแนะนำ Redis ให้รู้จักกัน มันคือ In-memory Data Structure Store ที่เร็วโคตรๆ เพราะเก็บข้อมูลใน RAM ไง เหมาะมากกับการเอามาทำเป็นแคช (Cache) ให้แอปเราเร็วปรื๋อขึ้นมาทันตาเห็นเลย แค่ไม่กี่บรรทัดโค้ดเองนะเชื่อดิ!

เตรียม Redis ให้พร้อม

วิธีที่ง่ายที่สุดในการรัน Redis ก็คือ Docker แหละ ถ้าใครยังไม่มี Docker ก็ไปหาลงซะนะ มันชีวิตดีขึ้นเยอะ

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

แค่นี้ Redis Server ของเราก็พร้อมใช้งานที่ localhost:6379 แล้วโคตรชิลเลย!

ติดตั้ง Library ใน Python

แน่นอนว่าเราต้องใช้ redis-py ในโปรเจกต์ Flask ของเรา

pip install Flask redis

สร้าง Flask App แบบ Basic

มาลองดูโค้ด Flask ง่ายๆ ที่ดึงข้อมูล (สมมติว่าเป็นข้อมูลจาก DB ที่ช้าโคตรๆ) ทุกครั้งที่มีการเรียกใช้ Endpoint กันก่อนนะ:

# app_no_cache.py
from flask import Flask, jsonify
import time

app = Flask(__name__)

def fetch_data_from_db():
    # สมมติว่านี่คือการดึงข้อมูลจาก DB ที่ใช้เวลานาน
    time.sleep(2)  # Simulate a 2-second DB query
    print("\n*** Fetching data from DB... ***\n")
    return {"id": 1, "name": "สินค้าสุดว้าว", "price": 999.00, "description": "สินค้านี้โคตรดีจริง!"}

@app.route('/product/<int:product_id>')
def get_product_no_cache(product_id):
    print(f"Request for product {product_id} (no cache)")
    data = fetch_data_from_db()
    return jsonify(data)

if __name__ == '__main__':
    app.run(debug=True)

ลองรันแล้วเรียก http://127.0.0.1:5000/product/1 สักสองสามครั้ง จะเห็นว่ามันใช้เวลา 2 วินาทีทุกครั้งเลยใช่ปะล่ะ? น่าหงุดหงิดเนอะ.

เพิ่ม Redis Cache เข้าไป!

ทีนี้มาดูเวอร์ชั่นที่มี Redis Cache กันบ้าง เราจะใช้ redis-py เพื่อ SET และ GET ข้อมูลโดยตรงเลยนะ ไม่ต้องพึ่ง Flask-Caching ก็ได้ ถ้าอยากควบคุมเองเต็มที่:

# app_with_cache.py
from flask import Flask, jsonify
import redis
import time
import json # ต้องใช้ json เพื่อแปลง dict เป็น string ก่อนเก็บใน Redis

app = Flask(__name__)

# ตั้งค่า Redis Connection
# ตรงนี้ถ้า Redis ไม่ได้รันอยู่ หรือคอนฟิกผิด 
# มันจะฟ้อง ConnectionError ทันทีที่รันแอปเลยนะ 
# เจอประจำ! ต้องเช็คดีๆ
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 0
CACHE_EXPIRATION_SECONDS = 60 # ให้แคชอยู่ได้ 60 วินาที

try:
    cache = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, decode_responses=True)
    cache.ping() # ลอง ping ดูว่าเชื่อมต่อได้จริงไหม
    print("\n*** Redis connection successful! ***\n")
except redis.exceptions.ConnectionError as e:
    print(f"\n!!! ERROR: Could not connect to Redis: {e} !!!\n")
    print("!!! Make sure Redis is running and accessible at {REDIS_HOST}:{REDIS_PORT} !!!\n")
    cache = None # ถ้าต่อไม่ได้ก็ตั้งเป็น None ไปเลยจะได้ไม่เออเร่อตรงส่วนที่ใช้ cache

def fetch_data_from_db():
    # สมมติว่านี่คือการดึงข้อมูลจาก DB ที่ใช้เวลานาน
    time.sleep(2) 
    print("\n*** Fetching data from DB (actual DB call)... ***\n")
    return {"id": 1, "name": "สินค้าสุดว้าว", "price": 999.00, "description": "สินค้านี้โคตรดีจริง!"}

@app.route('/product/<int:product_id>')
def get_product_with_cache(product_id):
    key = f'product:{product_id}'

    if cache:
        cached_data = cache.get(key)
        if cached_data:
            print(f"\n--- Data for {key} found in cache! ---\n")
            # อย่าลืมแปลงจาก string กลับเป็น dict นะ
            return jsonify(json.loads(cached_data))

    # ถ้าไม่มีในแคช หรือต่อ Redis ไม่ได้ ก็ไปดึงจาก DB
    data = fetch_data_from_db()

    if cache:
        # เก็บข้อมูลลงแคช พร้อมกำหนดเวลาหมดอายุ (expire)
        # ตรงนี้สำคัญนะ ถ้าลืมกำหนด expire แคชมันจะค้างอยู่ตลอดไปเลย!
        # แล้วถ้าข้อมูลใน DB อัปเดต แคชเราจะเก่าอยู่ตลอด ตรงนี้แหละที่ต้องระวัง
        cache.setex(key, CACHE_EXPIRATION_SECONDS, json.dumps(data))
        print(f"\n--- Data for {key} stored in cache for {CACHE_EXPIRATION_SECONDS} seconds.---\n")

    return jsonify(data)

if __name__ == '__main__':
    app.run(debug=True)

ลองรัน app_with_cache.py แล้วเรียก http://127.0.0.1:5000/product/1 อีกครั้งดูนะ

  • ครั้งแรก: จะเห็นว่าใช้เวลา 2 วินาที เพราะต้องดึงจาก DB
  • ครั้งที่สอง: แทบจะทันทีเลย! เพราะดึงจากแคช (ถ้าเรียกภายใน 60 วินาที)

เห็นความแตกต่างปะ? มันเร็วขึ้นเยอะจริงดิ!

ข้อควรระวังและสิ่งที่เจอประจำ

  1. redis.exceptions.ConnectionError: อันนี้เจอบ่อยสุดๆ เลย เวลาลืมรัน Docker Redis หรือรันแล้วแต่พอร์ตชนกัน ต้องเช็ค docker ps หรือ netstat ให้ดีๆ นะ
  2. Serialization/Deserialization: Redis มันเก็บข้อมูลเป็น String หรือ Binary นะ ถ้าเราจะเก็บ Python dict, list หรือ object ซับซ้อนๆ ลงไป เราต้องแปลงมันเป็น JSON String ก่อน (ใช้ json.dumps()) ตอนดึงออกมาก็ต้องแปลงกลับ (ใช้ json.loads()) ไม่งั้น TypeError มาแน่
  3. ลืม setex หรือ ttl: ถ้าใช้ cache.set(key, value) เฉยๆ เนี่ย แคชมันจะอยู่ตลอดไปเลยนะ! ข้อมูลเก่าในแคชมันก็จะไม่ถูกอัปเดต ถ้า DB เปลี่ยนแล้วล่ะก็ แคชเราก็ยังแสดงข้อมูลเก่าอยู่ดี ทำให้ข้อมูลไม่ตรงกัน อันนี้แหละโคตรอันตราย ต้องกำหนดเวลาหมดอายุ setex(key, seconds, value) หรือ expire(key, seconds) เสมอนะจ๊ะ
  4. Cache Invalidation: ถ้าข้อมูลใน DB เปลี่ยนก่อนที่แคชจะหมดอายุ เราต้องหาวิธีลบแคชตัวนั้นทิ้งด้วยนะ ไม่งั้นผู้ใช้ก็จะเห็นข้อมูลเก่าอยู่ดี อาจจะยิงคำสั่ง cache.delete(key) ตอนที่เราอัปเดตข้อมูลใน DB ก็ได้

สรุปส่งท้าย

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

ลองเอาไปประยุกต์ใช้กับโปรเจกต์ของตัวเองดูนะ แล้วจะติดใจ! ไม่ต้องกลัว มันไม่ได้ยากอย่างที่คิดหรอก.

Read more

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

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

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

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

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

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

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

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

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

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

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

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

By ทีมงาน devdog