AI Engineering Academy · บทเรียน

วิวัฒนาการของโครงสร้างข้อมูลและความเข้ากันได้ย้อนหลัง

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

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

วิวัฒนาการของโครงสร้างข้อมูลและความเข้ากันได้ย้อนหลัง เป็นบทเรียน AI Engineering Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Engineering Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใดสคีมาจึงเปลี่ยนแปลงไปตามเวลา

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

กำหนดเวอร์ชันให้สคีมา

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

from pydantic import BaseModel
from typing import Literal

class InvoiceV1(BaseModel):
    schema_version: Literal['1.0'] = '1.0'
    vendor: str
    total_amount: float

class InvoiceV2(BaseModel):
    schema_version: Literal['2.0'] = '2.0'
    vendor: str
    vendor_tax_id: str | None = None  # new field
    total_amount: float
    currency: str = 'USD'  # new field with default

การเปลี่ยนแปลงแบบเพิ่มข้อมูลกับการเปลี่ยนแปลงที่ทำให้ใช้ไม่ได้

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

# Safe: additive change - add optional field
class ProductV2(BaseModel):
    name: str
    price: float
    sku: str | None = None   # NEW optional field - backward safe
    category: str = 'general'  # NEW with default - backward safe

# Risky: breaking change - rename or retype
# class ProductV2(BaseModel):
#     product_name: str  # RENAMED from name - breaks consumers
#     price_cents: int   # RETYPED from float - breaks data

จัดเก็บเวอร์ชันสคีมาในฐานข้อมูล

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

-- PostgreSQL table design
CREATE TABLE extractions (
    doc_id TEXT PRIMARY KEY,
    schema_version TEXT NOT NULL,
    extracted_data JSONB NOT NULL,
    extracted_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_schema_version ON extractions(schema_version);

-- Query old records needing migration
SELECT doc_id, extracted_data
FROM extractions
WHERE schema_version = '1.0'
LIMIT 1000;

เขียนสคริปต์ย้ายข้อมูล

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

import asyncpg
import json

async def migrate_v1_to_v2(pool, batch_size=100):
    async with pool.acquire() as conn:
        rows = await conn.fetch(
            'SELECT doc_id, extracted_data FROM extractions WHERE schema_version=$1 LIMIT $2',
            '1.0', batch_size
        )
        for row in rows:
            old = row['extracted_data']
            new_data = {
                'schema_version': '2.0',
                'vendor': old['vendor'],
                'vendor_tax_id': None,  # unknown for old records
                'total_amount': old['total_amount'],
                'currency': 'USD'  # assume USD for old records
            }
            await conn.execute(
                'UPDATE extractions SET extracted_data=$1, schema_version=$2 WHERE doc_id=$3',
                json.dumps(new_data), '2.0', row['doc_id']
            )

ตรวจสอบแบบขนานระหว่างการเปลี่ยนผ่าน

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

async def parallel_validate(text: str) -> dict:
    v1_result, v2_result = await asyncio.gather(
        extract_with_schema(text, InvoiceV1),
        extract_with_schema(text, InvoiceV2)
    )
    discrepancy = (
        v1_result.vendor != v2_result.vendor or
        abs(v1_result.total_amount - v2_result.total_amount) > 0.01
    )
    if discrepancy:
        log_discrepancy(text, v1_result, v2_result)
    return {'v1': v1_result, 'v2': v2_result, 'discrepancy': discrepancy}

ใช้แฟล็กฟีเจอร์เพื่อทยอยเปิดใช้สคีมา

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

import os

def get_active_schema():
    version = os.environ.get('EXTRACTION_SCHEMA_VERSION', '1.0')
    schemas = {
        '1.0': InvoiceV1,
        '2.0': InvoiceV2,
    }
    return schemas.get(version, InvoiceV1)

async def extract_document(text: str):
    SchemaClass = get_active_schema()
    return await extract_with_schema(text, SchemaClass)

ความเข้ากันได้ของผู้ใช้ข้อมูลด้วยชนิดยูเนียน

ผู้ใช้ข้อมูลปลายทางที่อ่านข้อมูลที่ดึงมาต้องรองรับสคีมาหลายเวอร์ชันได้อย่างเหมาะสม ใช้ ยูเนียนที่มีตัวแยกประเภทในโค้ดของผู้ใช้ข้อมูล เพื่อเลือกตรรกะการแยกวิเคราะห์ที่ถูกต้องตามช่อง schema_version วิธีนี้ทนทานกว่าการเขียนเงื่อนไข if-else ต่อกัน และขยายได้ง่ายกว่าเมื่อมีเวอร์ชัน 3

from pydantic import BaseModel
from typing import Union, Annotated
from typing import Literal

def parse_extraction(raw: dict) -> Union[InvoiceV1, InvoiceV2]:
    version = raw.get('schema_version', '1.0')
    if version == '1.0':
        return InvoiceV1(**raw)
    elif version == '2.0':
        return InvoiceV2(**raw)
    else:
        raise ValueError(f'Unknown schema version: {version}')

ทดสอบการเปลี่ยนแปลงสคีมาก่อนนำขึ้นระบบ

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

def eval_schema_on_test_set(test_cases: list, SchemaClass) -> dict:
    field_f1 = {}
    for case in test_cases:
        result = extract_with_schema(case['text'], SchemaClass)
        for field in case['expected']:
            expected = case['expected'][field]
            actual = getattr(result, field, None)
            # Update precision/recall counters
            update_metrics(field_f1, field, expected, actual)
    return {k: compute_f1(v) for k, v in field_f1.items()}

จัดการการเลิกใช้สคีมา

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

DEPRECATED_VERSIONS = {'1.0'}
SUNSET_DATE = '2026-09-01'

def warn_if_deprecated(version: str):
    if version in DEPRECATED_VERSIONS:
        import warnings
        warnings.warn(
            f'Schema version {version} is deprecated. '
            f'It will be removed after {SUNSET_DATE}. '
            'Migrate consumers to version 2.0.',
            DeprecationWarning,
            stacklevel=2
        )

บันทึกการเปลี่ยนแปลงและการสื่อสาร

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

# CHANGELOG.md entry format:
# ## Schema v2.0 (2026-07-01)
# ### Changes
# - ADDED: vendor_tax_id (Optional[str]) - VAT/EIN extracted from header
# - ADDED: currency (str, default='USD') - detected from symbol/code
# ### Migration
# Run: python scripts/migrate_v1_to_v2.py --batch-size=500
# ### Consumers
# - billing-service: update parse_extraction() to handle v2
# - audit-service: query now supports currency filter

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

ทดสอบความเข้าใจของคุณเกี่ยวกับวิวัฒนาการสคีมาและความเข้ากันได้แบบย้อนหลังในสายงานการดึงข้อมูล

ทบทวนบทเรียน

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

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

เรียนรู้ Python ด้วย AI tutor — ฟรี

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

คอร์ส
30
บทเรียน
120

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

บทเรียน “วิวัฒนาการของโครงสร้างข้อมูลและความเข้ากันได้ย้อนหลัง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “วิวัฒนาการของโครงสร้างข้อมูลและความเข้ากันได้ย้อนหลัง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Engineering Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “วิวัฒนาการของโครงสร้างข้อมูลและความเข้ากันได้ย้อนหลัง”

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

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Engineering Academy หรือไม่

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

บทเรียน “วิวัฒนาการของโครงสร้างข้อมูลและความเข้ากันได้ย้อนหลัง” ใช้เวลานานแค่ไหน

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

ฉันเขียนและรันโค้ดในบทเรียน AI Engineering Academy นี้ได้ไหม

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

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

  1. Instructor: การดึงข้อมูลแบบมีชนิดด้วย Pydantic
  2. การจัดการข้อมูลบางส่วนและข้อมูลที่ขาดหาย
  3. การประมวลผลเป็นชุดด้วยอะซิงโครนัสและคิว
  4. วิวัฒนาการของโครงสร้างข้อมูลและความเข้ากันได้ย้อนหลัง
← กลับไปที่ AI Engineering Academy