ข้าม​ไป​ยัง​เนื้อหา

สร้าง query engine เอง — ทำไม query ช้า และ planner ตัดสิน​ใจ​ด้วย​อะไร

query planner ไม่​ได้ “รู้” ว่า​แผน​ไหน​เร็ว​กว่า มัน​เดา​จำนวน​แถว แล้ว​เอา​ไป​คูณ​กับ​ราคา​ที่​มัน​สมมติ​ขึ้น​เอง — ทั้ง​การ​เดา​และ​ราคา​นั้น​ผิด​ได้ และ​เมื่อ​คุณ​สร้าง​ทั้ง​สอง​อย่าง​ด้วย​มือ คุณ​จะ​อธิบาย​ได้​ว่า​แผนที่​คุณ​เห็น​เกิด​จาก​ความ​ผิดพลาด​ข้อ​ไหน คอร์ส​นี้​จึง​ไม่​ได้​สอน​ให้​จูน query แต่​พา​ประกอบ​ชิ้น​ส่วน​ที่​ผลิต​แผน​นั้น​ขึ้น​มา​เอง​ด้วย Python stdlib ล้วน: หน้า 4096 byte กับ heap file → B+tree ที่​ทำให้​จำนวน​หน้าที่​แตะ​แทบ​ไม่​โต​ตาม​ข้อมูล → สถิติ​ที่​ใช้​เดา​จำนวน​แถว​โดย​ไม่​รัน query → cost model ที่​ไม่มี​หน่วย​เวลา → ทาง​เข้า​และ covering index → อัลกอริทึม join → ปิด​ท้าย​ด้วย​การ​เอา planner ของ​คุณ​ไป​เทียบ​กับ sqlite3 แล้ว​อธิบาย​ทุก​จุด​ที่​ไม่​ตรง คำถาม​ที่​คอร์ส​นี้​ตอบ​และ​ไม่มี​คอนเทนต์​อื่น​บน​ไซต์​ตอบ​คือ ทำไม​การ​เพิ่ม index บาง​ครั้ง​ถึง​ไม่​ช่วย และ​เอนจินตัดสิน​ใจ​เรื่อง​นี้​ด้วย​ตัวเลข​อะไร 8 บท​ลงมือ​จริง
ความคืบหน้า0 / 8 บทเรียน

สิ่ง​ที่​ต้อง​มี​คือ Python ระดับ​อ่าน​ออก​กับ SQL ระดับ​ใช้งาน ส่วน​ความ​รู้เรื่อง​ภายใน​ฐาน​ข้อมูล​ไม่​ต้อง​มี​มา​ก่อน

  • Python ระดับ​อ่าน code ออก​และ​รัน​เอง​ได้ ทุก​บท​เขียน​ด้วย Python 3 และ stdlib ล้วน ไม่​ต้อง​ติดตั้ง อะไร​เพิ่ม​เลย​สัก​ตัว โมดูล​ที่​ใช้​มี​แค่​ของ​ที่​ติด​มา​กับ​ภาษา​อยู่​แล้ว
  • SQL ระดับ​เขียน SELECT … WHERE … ORDER BY เป็น พอ ไม่​ต้อง​รู้เรื่อง transaction หรือ tuning มา​ก่อน คอร์ส​นี้​ไม่ใช่​คอร์ส​สอน SQL และ​ไม่ใช่​คอร์ส Postgres
  • ไม่​ต้อง​รู้เรื่อง​ภายใน​ของ​ฐาน​ข้อมูล​มา​ก่อน ทุก​โครงสร้าง​ใน​คอร์ส​นี้​ถูก​สร้าง​ขึ้น​ใหม่​ตั้งแต่ byte แรก บน​หน้า​ขนาด​คงที่ ไม่มี​บท​ไหน​ที่​ขอ​ให้​เชื่อ​โดย​ไม่​ให้​ดู
  • ถ้า​มา​ด้วย​อาการ “เพิ่ม index แล้ว​ยัง​ช้า​เหมือน​เดิม” บทความ ทำไม index ที่​คุณ​เพิ่ง​เพิ่ม​ถึง​ไม่​ถูก​ใช้ คือ​การ​วินิจฉัย​อาการ จาก​แผนที่​ได้​มา ส่วน​คอร์ส​นี้​สร้าง​สิ่ง​ที่​ผลิต​แผน​นั้น​ขึ้น​มา อ่าน​คู่​กัน​ได้ แต่​ไม่​บังคับ​ว่า​ต้อง​อ่าน​ก่อน

เส้น​ขอบเขต​ของ​คอร์ส — หน่วย​คือ​ครั้ง​ที่​แตะ​ที่​เก็บ​ข้อมูล ไม่ใช่​วินาที

หัวข้อ​ที่​มีชื่อ​ว่า “เส้น​ขอบเขต​ของ​คอร์ส — หน่วย​คือ​ครั้ง​ที่​แตะ​ที่​เก็บ​ข้อมูล ไม่ใช่​วินาที”

หัวข้อ​นี้​ประกาศ​หน่วย​ที่​ใช้​วัด​ทั้ง​คอร์ส และ​บอกว่า​อะไร​ที่​คอร์ส​นี้​จงใจ​ไม่​ทำ

query เดิม เครื่อง​เดิม บางที​ตอบ​ใน​สามสิบ​มิลลิ​วินาที บางที​สาม​วินาที เพราะ​เวลา​ไม่​ได้​วัด​แผน มัน​วัด​เครื่อง cache ของ OS และ​เพื่อนบ้าน​บน​เครื่อง​เดียวกัน​ไป​ด้วย คอร์ส​นี้​จึง​เลิก​ใช้​วินาที​ตั้งแต่​บท​แรก แล้ว​ใช้​หน่วย​ที่นับได้ตรงๆ สาม​หน่วย​แทน

  1. จำนวน​หน้าที่​แตะ — อ่าน​หนึ่ง​หน้า​คือ​หนึ่ง​ครั้ง ทุก​บท​ที่​รายงาน​ตัวเลข​นี้​นับ​จาก code ที่​รัน​จริง
  2. จำนวน​ครั้ง​ที่​เทียบ​คีย์ — เพิ่ม​เข้า​มา​ตอน​บท join เพราะ​สอง​แผนที่​อ่าน​หน้า​เท่า​กัน​เป๊ะ ยัง​ทำงาน​ต่าง​กัน​ได้​มาก
  3. จำนวน​แถว​ที่​ต้อง​ถือ​ไว้​พร้อม​กัน — ราคา​ที่​ไม่​ได้​จ่าย​เป็น I/O แต่​จ่าย​เป็น​หน่วย​ความ​จำ

รูปธรรม​ของ​ธีสิส​คือ​คู่​ตัวเลข​ที่​วัด​ได้​ตั้งแต่​บท​ที่​สอง​และ​วน​กลับ​มา​อีก​หลาย​ครั้ง​ตลอด​คอร์ส: หยิบ​แถว​แบบ​สุ่ม 0.50 % ของ​ตาราง จ่าย​ไป 31.72 % ของ​ราคา​การ​อ่าน​ทั้ง​ตาราง

รายการ​นี้​เป็น​เส้น​ขอบเขต ไม่ใช่​ข้อ​แก้ตัว — ทุก​ข้อ​คือ​สิ่ง​ที่​จงใจ​ตัด​ออก​เพื่อ​ให้​ส่วน​ที่​เหลือ​รัน​ได้​จริง

  • ไม่​ได้​สร้าง​ฐาน​ข้อมูล​ที่​ใช้งาน​ได้ ไม่มี SQL parser ไม่มี transaction ไม่มี WAL ไม่มี MVCC ไม่มี concurrency และ​ไม่มี​การ​ลบ​หรือ​แก้​แถว heap ของ​คอร์ส​นี้​เขียน​ต่อ​ท้าย​อย่าง​เดียว
  • ไม่​ได้​แข่ง​กับ SQLite และ​ไม่​ได้​ทำซ้ำ SQLite ประโยค​ที่​คอร์ส​นี้​ยืน​อยู่​มี​ประโยค​เดียว: เรา​สร้าง​สิ่ง​ที่​อธิบาย​ได้​ว่า​ทำไม​แผน​ของ SQLite ถึง​หน้าตา​แบบ​นั้น ไม่ใช่​สิ่ง​ที่​ดี​กว่า SQLite
  • ไม่​รายงาน​เวลา ไม่มี​วินาที ไม่มี​มิลลิ​วินาที และ​ไม่มี​ประโยค​ว่า “เร็ว​ขึ้น​กี่​เท่า” ตลอด​ทั้ง​คอร์ส
  • ไม่ใช่​คอร์ส​สอน SQL และ​ไม่ใช่​คอร์ส Postgres เรื่อง​อย่าง MVCC, WAL หรือ partitioning ปรากฏ​ได้​แค่​ใน​ฐานะ​ไดอะแกรม​กับ​การ​อ้างอิง ไม่มี code และ​ไม่มี​ตัวเลข​ประมาณ​การ

ตัวเลข​ทุก​ตัว​มา​จาก​สคริปต์​ที่​คุณ​รัน​เอง​ได้

หัวข้อ​ที่​มีชื่อ​ว่า “ตัวเลข​ทุก​ตัว​มา​จาก​สคริปต์​ที่​คุณ​รัน​เอง​ได้”

หัวข้อ​นี้​อธิบาย​ว่า​ทำไม​ตัวเลข​ใน​คอร์ส​นี้​ผู้​อ่าน​ตรวจสอบ​เอง​ได้​ทั้งหมด

ข้อมูล​ตั้งต้น​ของ​ทั้ง​คอร์ส​คือ​ตารางออเดอร์ 200,000 แถว​ที่​สร้าง​จาก seed คงที่ จึง​ได้​ค่า md5 เดิม ทุก​ครั้ง​บน​ทุก​เครื่อง ตัวเลข​ทุก​ตัว​ที่​ปรากฏ​ใน​บทเรียน​ถูก​พิมพ์​ออก​มา​จาก​โมดูล Python ที่ commit ไว้​ใน​รี​โป พร้อม stdout ที่​มี​เทสต์เทียบ​ที​ละ byte — ถ้า​คุณ​รัน​แล้ว​ได้​ไม่​เท่า​กัน แปล​ว่า​มี​อะไร​ผิด ไม่ใช่ “แล้ว​แต่​เครื่อง”

หน้า​ขนาด 4096 byte ที่​คอร์ส​นี้​ใช้​ไม่​ได้​เลือก​มาลอยๆ มัน​ตรง​กับ page_size ของ file SQLite ที่​บท capstone เอา​มา​เทียบ​พอดี หน่วย “หน้า” ของ​สอง​ฝั่ง​จึง​วาง​ทาบ​กันได้ตรงๆ

แผนผัง​นี้​จะ​เติม​ขึ้น​เอง​เมื่อ​บทเรียน​ถูก​เพิ่ม​เข้า​มา