Cloud & IT Cert Prep · บทเรียน

กลยุทธ์การแคช: การโหลดแบบขี้เกียจและการเขียนผ่าน

ใช้การโหลดแบบขี้เกียจเพื่อเติมแคชเมื่อไม่พบข้อมูล และใช้การเขียนผ่านเพื่อให้แคชสอดคล้องกับทุกการเขียนลงฐานข้อมูล

บทเรียน 3 จาก 413 ขั้นตอน

กลยุทธ์การแคช: การโหลดแบบขี้เกียจและการเขียนผ่าน เป็นบทเรียน Cloud & IT Cert Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Cloud & IT Cert Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Cloud & IT Cert Prep มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใดกลยุทธ์การแคชจึงสำคัญ

การแทรกแคชไว้ระหว่างแอปพลิเคชันกับฐานข้อมูลจำเป็นต้องมีกลยุทธ์การแคช ซึ่งเป็นชุดกฎที่กำหนดว่าจะนำข้อมูลเข้าแคชเมื่อใด จะอ่านข้อมูลจากแคชเมื่อใด และจะนำข้อมูลออกจากแคชเมื่อใด การเลือกกลยุทธ์ที่ไม่เหมาะสมอาจทำให้เกิดข้อมูลล้าสมัย (แคชส่งคืนค่าที่ไม่เป็นปัจจุบัน) แคชพลาดขณะว่างเปล่า (แคชไม่มีข้อมูลและทุกคำขอต้องเข้าถึงฐานข้อมูล) หรือการถล่มของแคช (คำขอพร้อมกันจำนวนมากสำหรับคีย์เดียวกันที่ไม่มีข้อมูล ต่างก็เข้าถึงฐานข้อมูลพร้อมกัน) กลยุทธ์พื้นฐานมีสองแบบ ได้แก่ การโหลดแบบขี้เกียจและการเขียนผ่าน

การโหลดแบบขี้เกียจ (แคชแบบแยกส่วน): วิธีการทำงาน

การโหลดแบบขี้เกียจ (เรียกอีกอย่างว่าแคชแบบแยกส่วน) เป็นรูปแบบการแคชที่ใช้กันมากที่สุด แอปพลิเคชันจะตรวจสอบแคชก่อน หากพบข้อมูลในแคช ข้อมูลจะถูกส่งคืนโดยตรงจากแคช ซึ่งเป็นเส้นทางที่รวดเร็ว หากไม่พบข้อมูลในแคช แอปพลิเคชันจะดึงข้อมูลจากฐานข้อมูล เขียนผลลัพธ์ลงในแคชพร้อมกำหนด TTL แล้วส่งคืนข้อมูลให้ผู้เรียก แคชจะเก็บเฉพาะข้อมูลที่มีการร้องขอจริงเท่านั้น จึงเรียกว่า “ขี้เกียจ” คำขอถัดไปสำหรับคีย์เดียวกันจะพบข้อมูลนั้นในแคช

# Lazy loading pattern (Python with redis-py)
def get_user(user_id, redis_client, db):
    cache_key = f'user:{user_id}'

    # 1. Check cache
    cached = redis_client.get(cache_key)
    if cached:
        return json.loads(cached)   # Cache HIT

    # 2. Cache MISS: fetch from DB
    user = db.query('SELECT * FROM users WHERE id = %s', user_id)

    # 3. Populate cache with TTL of 300 seconds
    redis_client.setex(cache_key, 300, json.dumps(user))

    return user

การโหลดแบบขี้เกียจ: ข้อดี

การโหลดแบบขี้เกียจมีข้อดีสำคัญสามประการ ได้แก่ แคชเฉพาะข้อมูลที่มีการร้องขอ — แคชจะไม่เต็มไปด้วยข้อมูลที่ไม่มีใครอ่าน จึงใช้หน่วยความจำได้อย่างมีประสิทธิภาพ แคชว่างเปล่าไม่ทำให้แอปพลิเคชันหยุดทำงาน — เมื่อไม่พบข้อมูลในแคช แอปพลิเคชันจะถอยกลับไปใช้ฐานข้อมูล ดังนั้นแม้ ElastiCache จะเริ่มต้นใหม่หรือโหนดล้มเหลว แอปพลิเคชันก็ยังทำงานต่อได้ แต่มีความหน่วงสูงขึ้น แคชจะมีความสอดคล้องในท้ายที่สุดกับฐานข้อมูลเสมอ เพราะข้อมูลเก่าจะหมดอายุผ่าน TTL แม้จะพลาดการอัปเดตก็ตาม

การโหลดแบบขี้เกียจ: ข้อเสีย

การโหลดแบบขี้เกียจมีข้อเสียสำคัญสามประการ ได้แก่ การไม่พบข้อมูลในแคชมีต้นทุนสูง — ต้องดำเนินการสามอย่าง (ตรวจสอบแคช อ่านฐานข้อมูล และเขียนแคช) เทียบกับการพบข้อมูลที่ใช้เพียงอย่างเดียว ทำให้คำขอขณะแคชว่างมีความหน่วงสูงขึ้น ข้อมูลล้าสมัย — หลังจากฐานข้อมูลได้รับการอัปเดต แคชยังคงส่งคืนค่าเดิมจนกว่า TTL จะหมดอายุหรือคีย์จะถูกทำให้ใช้ไม่ได้อย่างชัดเจน (ช่วงเวลาที่แคชไม่สอดคล้องกัน) การถล่มของแคช — หากคีย์ยอดนิยมหมดอายุ คำขอพร้อมกันจำนวนมากจะไม่พบข้อมูลในแคชและเข้าถึงฐานข้อมูลพร้อมกัน ซึ่งอาจทำให้ฐานข้อมูลรับภาระมากเกินไป

# Mitigating cache stampede with a probabilistic early expiration
# (Refresh the key before it expires to avoid simultaneous misses)
def get_with_stampede_protection(key, redis_client, db_fetch_fn, ttl=300):
    value = redis_client.get(key)
    ttl_remaining = redis_client.ttl(key)

    # Probabilistically refresh before expiry
    if value is None or (ttl_remaining < 30 and random.random() < 0.1):
        value = db_fetch_fn()
        redis_client.setex(key, ttl, json.dumps(value))
    return json.loads(value)

การเขียนผ่าน: วิธีการทำงาน

ในกลยุทธ์การเขียนผ่าน ทุกครั้งที่เขียนข้อมูลลงฐานข้อมูล จะเขียนข้อมูลลงแคชพร้อมกันด้วย แอปพลิเคชันจะเขียนทั้งแคชและฐานข้อมูลเป็นส่วนหนึ่งของการดำเนินการเดียวกัน (หรือให้ฐานข้อมูลเรียกใช้การอัปเดตแคช) ดังนั้นแคชจึงสอดคล้องกับฐานข้อมูลอยู่เสมอ และไม่มีช่วงเวลาที่ข้อมูลล้าสมัย การเขียนผ่านรับประกันว่าข้อมูลในแคชเป็นข้อมูลล่าสุดเสมอ ทำให้การอ่านครั้งถัดไปของข้อมูลที่เพิ่งเขียนเป็นการพบข้อมูลในแคชเสมอ

# Write-through pattern (Python)
def update_user(user_id, user_data, redis_client, db):
    # 1. Write to database FIRST
    db.execute('UPDATE users SET name=%s WHERE id=%s',
                (user_data['name'], user_id))

    # 2. Update cache immediately (write-through)
    cache_key = f'user:{user_id}'
    redis_client.setex(cache_key, 3600, json.dumps(user_data))

    return user_data

# Every read is now a cache hit for recently updated data

การเขียนผ่าน: ข้อดี

ข้อดีของการเขียนผ่าน ได้แก่ ข้อมูลในแคชเป็นข้อมูลล่าสุดเสมอ — ไม่มีข้อมูลล้าสมัย เพราะแคชได้รับการอัปเดตทุกครั้งที่เขียน การอ่านทำได้รวดเร็วเสมอ — ข้อมูลยอดนิยมที่มีการเขียนบ่อยจะอยู่ในแคชเสมอ ไม่มีการถล่มของแคชขณะอ่าน — เนื่องจากข้อมูลถูกเติมไว้ล่วงหน้าก่อนอ่าน จึงไม่มีการไม่พบข้อมูลสำหรับข้อมูลที่เพิ่งเขียน กลยุทธ์นี้เหมาะอย่างยิ่งกับงานที่อ่านข้อมูลเป็นหลักและมีการอัปเดตบ่อย ซึ่งความใหม่ของข้อมูลมีความสำคัญ เช่น แค็ตตาล็อกผลิตภัณฑ์ ระบบราคา หรือแคชโปรไฟล์ผู้ใช้

การเขียนผ่าน: ข้อเสีย

ข้อเสียของการเขียนผ่าน ได้แก่ ต้นทุนของการเขียน — การเขียนทุกครั้งต้องดำเนินการสองอย่าง (ฐานข้อมูล + แคช) ทำให้การเขียนมีความหน่วงเพิ่มขึ้น ข้อมูลที่ไม่จำเป็นในแคช — ข้อมูลจะถูกเก็บในแคชแม้จะไม่มีการอ่านอีกเลย (เขียนครั้งเดียวและไม่เคยร้องขอ) ทำให้สิ้นเปลืองหน่วยความจำของแคช การเริ่มต้นแคชใหม่ทำให้แคชว่างเปล่า — หากคลัสเตอร์แคชเริ่มต้นใหม่ ข้อมูลที่เขียนไว้ล่วงหน้าทั้งหมดจะสูญหาย และต้องเติมข้อมูลลงแคชใหม่ผ่านการเขียนหรือกระบวนการอุ่นแคช ควรใช้การเขียนผ่านร่วมกับ TTL เพื่อป้องกันไม่ให้ข้อมูลที่ถูกอ่านน้อยเติบโตอย่างไม่จำกัด

การผสานการโหลดแบบขี้เกียจกับการเขียนผ่าน

ในทางปฏิบัติ ระบบที่ใช้งานจริงจำนวนมากผสานทั้งสองกลยุทธ์เข้าด้วยกัน ได้แก่ ใช้การเขียนผ่านกับข้อมูลที่อัปเดตบ่อยและอ่านบ่อย (เช่น เซสชันผู้ใช้หรือราคาปัจจุบัน) และใช้การโหลดแบบขี้เกียจกับข้อมูลที่อัปเดตน้อยแต่ถูกอ่านบ่อย (เช่น คำอธิบายผลิตภัณฑ์หรือเนื้อหาบทความ) กำหนด TTL ที่เหมาะสมให้กับทั้งสองแบบ โดยคีย์แบบเขียนผ่านจะมี TTL ยาว เนื่องจากข้อมูลเป็นข้อมูลล่าสุดเสมอ ส่วนคีย์ที่โหลดแบบขี้เกียจจะมี TTL สั้นกว่าเพื่อจำกัดช่วงเวลาที่ข้อมูลอาจล้าสมัย แนวทางแบบผสมนี้ช่วยเพิ่มอัตราการพบข้อมูลในแคชให้สูงสุด พร้อมลดความล้าสมัยของข้อมูล

# Hybrid: write-through for sessions, lazy loading for product data

# Write-through for session data (always fresh, critical)
def save_session(session_id, data, redis_client, db):
    db.upsert('sessions', session_id, data)
    redis_client.setex(f'session:{session_id}', 3600, json.dumps(data))

# Lazy loading for product catalog (infrequent updates OK)
def get_product(product_id, redis_client, db):
    cached = redis_client.get(f'product:{product_id}')
    if cached:
        return json.loads(cached)
    product = db.query('SELECT * FROM products WHERE id=%s', product_id)
    redis_client.setex(f'product:{product_id}', 86400, json.dumps(product))
    return product

หลักการออกแบบ TTL

อายุการใช้งาน (TTL) ของข้อมูลที่แคชไว้จะควบคุมช่วงเวลาสูงสุดที่ข้อมูลอาจล้าสมัยและขนาดพื้นที่หน่วยความจำของแคช ควรออกแบบ TTL โดยพิจารณาจากความถี่ในการอัปเดตข้อมูล (ข้อมูลเซสชันเปลี่ยนแปลงบ่อย → TTL สั้น เนื้อหาแบบคงที่เปลี่ยนแปลงน้อย → TTL ยาว) ระดับการยอมรับข้อมูลล้าสมัย (ราคาทางการเงิน → สั้นมาก ข้อความในบล็อก → หลายชั่วโมงหรือหลายวัน) และความจุหน่วยความจำของแคช (หน่วยความจำเหลือน้อย → TTL สั้นลงเพื่อนำข้อมูลเก่าออกได้เร็วขึ้น) ควรกำหนด TTL เสมอ — อย่าเก็บข้อมูลในแคชโดยไม่มีวันหมดอายุ มิฉะนั้นแคชจะเต็มไปด้วยข้อมูลเก่าในที่สุด

# TTL examples by data type

# API rate limit counter: 60 seconds
redis_client.setex(f'ratelimit:{ip}', 60, count)

# User session: 30 minutes
redis_client.setex(f'session:{id}', 1800, json.dumps(session))

# Product catalog: 24 hours
redis_client.setex(f'product:{id}', 86400, json.dumps(product))

# Stock price: 10 seconds
redis_client.setex(f'price:{symbol}', 10, price)

# Static site content: 7 days
redis_client.setex(f'page:{slug}', 604800, html_content)

การทำให้แคชใช้ไม่ได้เมื่อมีการอัปเดต

แทนที่จะพึ่งพาการหมดอายุของ TTL เพียงอย่างเดียว คุณสามารถทำให้คีย์ในแคชใช้ไม่ได้อย่างชัดเจน (ลบคีย์) เมื่อข้อมูลต้นทางมีการเปลี่ยนแปลง วิธีนี้จะกำจัดช่วงเวลาที่ข้อมูลล้าสมัยได้ทั้งหมด รูปแบบที่พบบ่อย ได้แก่ ลบเมื่อเขียน (ลบคีย์แคชหลังการอัปเดตฐานข้อมูลทุกครั้ง — การอ่านครั้งถัดไปจะเติมข้อมูลกลับผ่านการโหลดแบบขี้เกียจ) และการทำให้ใช้ไม่ได้ตามเหตุการณ์ (DynamoDB Streams หรือการเก็บการเปลี่ยนแปลงของ RDS เรียกใช้ Lambda เพื่อลบคีย์ที่ได้รับผลกระทบ) การทำให้แคชใช้ไม่ได้เป็นหนึ่งในปัญหาที่ยากที่สุดของระบบแบบกระจาย ยิ่งตรรกะการทำให้ใช้ไม่ได้เรียบง่ายเท่าใด แคชก็จะยิ่งน่าเชื่อถือมากขึ้นเท่านั้น

# Delete-on-write invalidation pattern
def update_product(product_id, new_data, redis_client, db):
    # Update the database
    db.execute('UPDATE products SET ... WHERE id=%s', (product_id,))

    # Invalidate the cache key
    redis_client.delete(f'product:{product_id}')

    # Also invalidate any list/search caches that may include this product
    redis_client.delete('products:list:page:1')
    redis_client.delete(f'products:category:{new_data["category_id"]}')

    # Next read will trigger lazy loading with fresh data

การแคชแบบเขียนภายหลัง (เขียนย้อนกลับ)

รูปแบบที่พบได้น้อยกว่าแต่มีประสิทธิภาพสูงคือการเขียนภายหลัง (เขียนย้อนกลับ) แอปพลิเคชันจะเขียนข้อมูลลงในแคชเท่านั้น จากนั้นแคชจะส่งข้อมูลไปยังฐานข้อมูลแบบไม่พร้อมกันในเบื้องหลัง วิธีนี้ให้การเขียนที่รวดเร็วมาก (เก็บไว้ในหน่วยความจำเท่านั้น) แต่ต้องแลกกับความเสี่ยงที่ข้อมูลจะสูญหาย หากแคชล้มเหลวก่อนส่งข้อมูลไปยังฐานข้อมูล การเขียนภายหลังเหมาะกับการเขียนที่มีความถี่และปริมาณสูง ซึ่งข้อมูลสามารถสร้างขึ้นใหม่ได้หรือยอมรับการสูญเสียเล็กน้อยได้ เช่น ตัวนับการพบข้อมูล เหตุการณ์การวิเคราะห์ หรือการอัปเดตคะแนนเกม ElastiCache ไม่รองรับการเขียนภายหลังโดยตรง จึงต้องนำไปใช้งานในชั้นแอปพลิเคชัน

# Write-behind pattern: write to cache, flush to DB asynchronously
# Application writes:
# redis_client.incr('post:42:views')      # fast, in-memory only

# Background job (runs every 60 seconds):
def flush_view_counts(redis_client, db):
    for key in redis_client.scan_iter('post:*:views'):
        count = redis_client.getdel(key)  # atomic get-and-delete
        post_id = key.split(':')[1]
        db.execute('UPDATE posts SET views = views + %s WHERE id = %s',
                   (int(count), post_id))

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบความเข้าใจแนวคิดของ AWS Solutions Architect (SAA-C03) จากบทเรียนนี้

สรุปบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้ว่า การโหลดแบบขี้เกียจ จะเติมข้อมูลลงแคชเมื่ออ่านแล้วไม่พบข้อมูล และใช้หน่วยความจำอย่างมีประสิทธิภาพ แต่สามารถส่งคืนข้อมูลล้าสมัยจนกว่า TTL จะหมดอายุ การเขียนผ่าน จะอัปเดตแคชทุกครั้งที่เขียน ทำให้ไม่มีข้อมูลล้าสมัย แต่สิ้นเปลืองหน่วยความจำกับข้อมูลที่ไม่มีการอ่าน และการทำให้ใช้ไม่ได้อย่างชัดเจน จะลบคีย์แคชเมื่อฐานข้อมูลได้รับการอัปเดต เพื่อกำจัดช่วงเวลาที่ข้อมูลล้าสมัย บทถัดไปเราจะสำรวจรูปแบบการจัดเก็บเซสชันและกระดานจัดอันดับด้วย ElastiCache

เริ่มต้นได้ฟรี

เรียนรู้ Cloud & IT Cert Prep ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
150
บทเรียน
600

คำถามที่พบบ่อย

บทเรียน “กลยุทธ์การแคช: การโหลดแบบขี้เกียจและการเขียนผ่าน” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “กลยุทธ์การแคช: การโหลดแบบขี้เกียจและการเขียนผ่าน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Cloud & IT Cert Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Cloud & IT Cert Prep มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “กลยุทธ์การแคช: การโหลดแบบขี้เกียจและการเขียนผ่าน”

ใช้การโหลดแบบขี้เกียจเพื่อเติมแคชเมื่อไม่พบข้อมูล และใช้การเขียนผ่านเพื่อให้แคชสอดคล้องกับทุกการเขียนลงฐานข้อมูล คุณปฏิบัติ Cloud & IT Cert Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Cloud & IT Cert Prep หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Cloud & IT Cert Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “กลยุทธ์การแคช: การโหลดแบบขี้เกียจและการเขียนผ่าน” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Cloud & IT Cert Prep นี้ได้ไหม

ได้ บทเรียน Cloud & IT Cert Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. Redis เทียบกับ Memcached: การเลือกกลไกที่เหมาะสม
  2. กลุ่มการจำลองข้อมูล Redis ของ ElastiCache และโหมดคลัสเตอร์
  3. กลยุทธ์การแคช: การโหลดแบบขี้เกียจและการเขียนผ่าน
  4. การจัดเก็บเซสชันและรูปแบบกระดานผู้นำ
← กลับไปที่ Cloud & IT Cert Prep