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

เรียง​ตาม restaurant แล้ว zone map ของ placed_at เหลือ​ข้าม 5 กลุ่ม ไม่ใช่ 0

บท​ที่ 6 วัด​ไว้​ว่า zone map ตัด​กลุ่ม​ทิ้ง​ได้​ก็​ต่อ​เมื่อ​ค่า​ใน column นั้น​เดิน​ตาม​ลำดับ​ที่​เขียน​แถว​ลง​คลัง คำถาม​ถัด​ไป​จึง​เขียน​ตัว​มัน​เอง​ว่า แล้ว​ถ้า​เปลี่ยน​ลำดับ​ที่​เขียน​ล่ะ

บท​นี้​เรียง​ตาราง​ใบ​เดิม​ใหม่​ตาม restaurant แล้ว​วัด​สอง​ฝั่ง​พร้อม​กัน คือ​ฝั่ง​ที่​ได้​มา และ​ฝั่ง​ที่​จ่าย​ไป

column ที่​ใช้​จัด​ลำดับ​แถว​ก่อน​เขียน​ลง​คลัง​เรียก​ว่า sort keysort keycolumn ที่​ใช้​จัด​ลำดับ​แถว​ก่อน​เขียน​ลง​คลัง และ​ตาราง​มี​ลำดับ​ได้​ลำดับ​เดียว · เรียง​ตาม placed_at แล้ว​คำถาม​ช่วง​เวลา​ข้าม​ได้ 19 กลุ่ม คำถาม​ร้าน​ข้าม​ได้ 0 กลุ่ม · เรียง​ตาม restaurant แล้ว​คู่​นั้น​เป็น 5 กับ 18 · ราคา​จ่าย​ที่ column ที่​ไม่​ได้​ใช้​เรียง คือ placed_at แบบ delta(frame of reference) ขึ้น​จาก 491,610 เป็น 585,360 ไบต์ ขณะ​ที่ restaurant ซึ่ง​เป็น column ที่​ใช้​เรียง แบบ bit packing ลง​จาก 108,680 เหลือ 26,657 ไบต์ และ ตาราง​หนึ่ง​ใบ​มี​ลำดับ​ได้​ลำดับ​เดียว การ​เลือก sort key จึง​ไม่ใช่​การ​เพิ่ม​ของ มัน​คือ​การ​ย้าย​ส่วน​ได้​เปรียบ​จาก column หนึ่ง​ไป​ให้​อีก column

คำ​ทำนาย​ของ​ผู้​เขียน probe ก่อน​วัด​คือ เรียง​ตาม restaurant แล้ว zone map ของ placed_at จะ​ข้าม​ได้ 0 กลุ่ม ค่าที่​วัด​ได้​คือ 5 กลุ่ม และ​เหตุผล​อยู่​ใน​สอง​บรรทัด​ของ Python

📦 ตัวเลข​ทุก​ตัว​ใน​บท​นี้​รัน​เอง​ได้ ไม่​ต้อง​มี Docker และ​ไม่​ต้อง​ต่อ​เน็ต

สั่ง python3 scripts/column-store/probe.py ได้​เลย มัน​ใช้ Python 3 กับ standard library ล้วน ไม่​ต้อง​ติด​ตั้ง​อะไร​ก่อน และ​ไม่มี container ให้​เก็บ​กวาด​ทีหลัง

ค่าที่​วัด​ได้​ทุก​ตัว​ที่​บท​นี้​พิมพ์​มี​คีย์​ของ​ตัว​เอง​อยู่​ใน scripts/column-store/expected/column-store.txt และ​ชื่อ​คีย์​นั้น​เขียน​กำกับ​ไว้​ใน​ทุก​ตาราง ค่า​ของ​ครึ่ง​แรก​อยู่​ใน​หมวด 7_เรียงแล้วได้อย่างเสียอย่าง ส่วน​ค่า​ของ​ครึ่ง​หลัง​อยู่​ใน​หมวด 8_ขนาด_row_group

zone map ตัว​หนึ่ง​ตื่น​ขึ้น อีก​ตัว​ทรุด​ลง แต่​ไม่​ตาย

หัวข้อ​ที่​มีชื่อ​ว่า “zone map ตัว​หนึ่ง​ตื่น​ขึ้น อีก​ตัว​ทรุด​ลง แต่​ไม่​ตาย”

ตัวเลข​สี่​ตัว​จาก​หมวด 7_เรียงแล้วได้อย่างเสียอย่าง และ​ตัว​ที่​หัก​ล้าง​คำ​ทำนาย​คือ​ตัว​ที่​สอง

คำถาม​ข้อ​แรก​คือ​เงื่อนไข​บน​ช่วง​เวลา​หนึ่ง​วัน ข้อ​เดียว​กับ​ที่​บท​ที่ 6 วัด ส่วน​ข้อ​ที่​สอง​ถาม​ร้าน​หนึ่ง​ร้าน ซึ่ง​เป็น​ทรง​ที่​หมวด​นี้​วัด​ขึ้น​ใหม่ สิ่ง​ที่​เปลี่ยน​ระหว่าง​สอง​ฝั่ง​คือ​ลำดับ​ที่​เขียน​แถว​ลง​คลัง​อย่าง​เดียว

ทรง​ที่​วัดคีย์​ใน​ผล​ของ probeกลุ่ม​ที่​ข้าม​ได้
เรียง​ตาม placed_at · ถาม​ช่วง​เวลาเรียงตาม placed_at · กลุ่มที่ข้ามได้เมื่อถามช่วงเวลา19
เรียง​ตาม restaurant · ถาม​ช่วง​เวลาเรียงตาม restaurant · กลุ่มที่ข้ามได้เมื่อถามช่วงเวลา5
เรียง​ตาม placed_at · ถาม​ร้านเรียงตาม placed_at · กลุ่มที่ข้ามได้เมื่อถามร้าน0
เรียง​ตาม restaurant · ถาม​ร้านเรียงตาม restaurant · กลุ่มที่ข้ามได้เมื่อถามร้าน18

ทั้ง​สี่​แถว​นับ​จาก​คลัง​ที่​มี 20 กลุ่ม​เท่า​กัน ตาม​คีย์ กลุ่มทั้งหมด ใน​หมวด​เดียวกัน

แถว​ที่​สาม​กับ​แถว​ที่​สี่​คือ​ฝั่ง​ที่​ได้​มา คำถาม​ที่​ถาม​ร้าน​เคย​ข้าม​ไม่​ได้​เลย แล้วก​ลาย​เป็น​ข้าม​ได้ 18 จาก 20 กลุ่ม นั่น​คือ​สิ่ง​ที่​การ​เลือก sort key ซื้อ​มา

แถว​ที่​สอง​คือ​ฝั่ง​ที่​จ่าย​ไป และ​มัน​คือ​แถว​ที่​หัก​ล้าง​คำ​ทำนาย 5 ไม่ใช่ 0 zone map ของ placed_at ทรุด​ลง​จริง แต่​มัน​ยัง​ทำงาน​อยู่

sort ของ Python เสถียร เวลา​จึง​ยัง​เรียง​อยู่​ข้าง​ใน​บล็อก​ของ​แต่ละ​ร้าน

หัวข้อ​ที่​มีชื่อ​ว่า “sort ของ Python เสถียร เวลา​จึง​ยัง​เรียง​อยู่​ข้าง​ใน​บล็อก​ของ​แต่ละ​ร้าน”

กลไก​ที่​ทำให้​ตัวเลข​เป็น 5 แทนที่​จะ​เป็น 0 และ probe อ่าน​มัน​ออก​มา​เป็น​บู​ลี​นข​อง​ตัว​เอง

การ​เรียง​ใหม่​ใน​บท​นี้​เป็นการ​เรียก​ฟังก์ชัน​เดียว และ​มัน​ไม่​ได้​ระบุ​ว่า​จะ​ทำ​อะไร​กับ​แถว​ที่​คีย์​เท่า​กัน

scripts/column-store/colstore.py:466 — การ​เรียง​ใหม่​ทั้ง​ตาราง
def order_by(rows, col):
ix = COL_IX[col]
return sorted(rows, key=lambda r: r[ix])

sorted ของ Python เป็น stable sortstable sortการ​เรียง​ที่​รักษา​ลำดับ​เดิม​ของ​แถว​ซึ่ง​มี​คีย์​เท่า​กัน ซึ่ง​เป็น​พฤติกรรม​ของ sort ใน Python · probe อ่าน​ค่า​นี้​ออก​มา​เป็น true บล็อก​ของ​แต่ละ​ร้าน​จึง​ยัง​เรียง​ตาม​เวลา​อยู่​ข้าง​ใน · นั่น​คือ​เหตุผล​ที่ zone map ของ placed_at เหลือ 5 กลุ่ม ไม่ใช่ 0 และ​ที่ 11 จาก 20 กลุ่ม​มี​ร้าน​เดียว​ล้วน คือ​แถว​ที่​คีย์​เท่า​กัน​ยัง​รักษา​ลำดับ​เดิม​ไว้ แถว​ของ​ร้าน​เดียวกัน​จึง​เรียง​ตาม​เวลา​ต่อ​กัน​เหมือน​เดิม เพราะ​ลำดับ​เดิม​ของ​ตาราง​เรียง​ตาม​เวลา​อยู่​แล้ว

probe ไม่​ได้​เชื่อ​เอกสาร​ของ Python มัน​วัด​ข้อ​นี้​จาก​แถว​จริง​แล้ว​เก็บ​ผล​ไว้​เป็น​คีย์​ของ​ตัว​เอง

scripts/column-store/probe.py:412 — ตรวจ​ว่า​เวลา​ยัง​เรียง​อยู่​ใน​บล็อก​ของ​แต่ละ​ร้าน
ixr, ixt = COL_IX["restaurant"], COL_IX["placed_at"]
blocks_in_time_order = all(
rows_by_rest[i][ixr] != rows_by_rest[i + 1][ixr]
or rows_by_rest[i][ixt] <= rows_by_rest[i + 1][ixt]
for i in range(len(rows_by_rest) - 1))
ทรง​ที่​วัดคีย์​ใน​ผล​ของ probeค่า
เวลา​ยัง​เรียง​อยู่​ใน​บล็อก​ของ​แต่ละ​ร้านบล็อกของแต่ละร้านยังเรียงตามเวลาอยู่ (sort เสถียร)true
กลุ่ม​ที่​มี​ร้าน​เดียว​ล้วน​หลัง​เรียง​ตาม​ร้านrow group ที่มีร้านเดียวล้วน (เรียงตามร้าน)11

11 จาก 20 กลุ่ม​ถือ​แถว​ของ​ร้าน​เดียว​ล้วน กลุ่ม​เหล่า​นั้น​จึง​เป็น​บล็อก​ที่​เรียง​ตาม​เวลา​ทั้ง​ก้อน และ​ช่วง min ถึง max ของ placed_at ใน​กลุ่ม​นั้น​เป็น​ช่วง​เวลา​ที่​ต่อ​เนื่อง ไม่ใช่​ช่วง​ที่​กิน​ทั้ง​ตาราง

คำ​ทำนาย​ที่​บท​นี้​หัก​ล้าง

คำ​ทำนาย​ที่​เขียน​ไว้​ก่อน​วัด​คือ เปลี่ยน sort key แล้ว zone map ของ column เดิม​จะ​ข้าม​ได้ 0 กลุ่ม เพราะ​ทุก​กลุ่ม​จะ​กิน​ช่วง​เวลา​เกือบ​ทั้ง​ตาราง​เหมือน​กัน​หมด

ค่าที่​วัด​ได้​คือ 5 ตาม​คีย์ เรียงตาม restaurant · กลุ่มที่ข้ามได้เมื่อถามช่วงเวลา

สิ่ง​ที่​คำ​ทำนาย​ลืม​คือ​คุณสมบัติ​ของ​ตัวการ​เรียง ไม่ใช่​คุณสมบัติ​ของ​ข้อมูล การ​เรียง​ที่​เสถียร ไม่​ได้​ลบ​ลำดับ​เดิม​ทิ้ง มัน​เก็บ​ลำดับ​เดิม​ไว้​ข้าง​ใน และ zone map อ่าน​ของ​ที่​เหลือ​อยู่​นั้น​ได้

flowchart TB
  T["ตารางออเดอร์ใบเดิม · 20 row group เท่ากันทั้งสองฝั่ง"]
  T --> A["เรียงตาม placed_at · ลำดับที่เสาข้อมูลใช้อยู่"]
  T --> B["เรียงตาม restaurant · sort เสถียร เวลาจึงยังเรียงอยู่ในบล็อกของแต่ละร้าน"]
  A --> A1["ถามช่วงเวลา · ข้าม 19 กลุ่ม"]
  A --> A2["ถามร้าน · ข้าม 0 กลุ่ม"]
  B --> B1["ถามช่วงเวลา · ข้าม 5 กลุ่ม"]
  B --> B2["ถามร้าน · ข้าม 18 กลุ่ม"]

คำ​บรรยาย​ภาพ: กิ่ง​ซ้าย​กับ​กิ่ง​ขวา​เป็น​ตาราง​ใบ​เดียวกัน​และ​จำนวน​กลุ่ม​เท่า​กัน ต่าง​กัน​ที่​ลำดับ ที่​เขียน​แถว​ลง​คลัง​อย่าง​เดียว ใบ​ล่าง​ทั้ง​สี่​ใบ​คือ​กลุ่ม​ที่ zone map ตัด​ทิ้ง​ได้ และ​คู่ 19 กับ 5 บน​คำถาม​ช่วง​เวลา เทียบ​กับ​คู่ 0 กับ 18 บน​คำถาม​ร้าน คือ​สิ่ง​ที่​บอก​ว่าการ​เรียง​เป็นการ​ย้าย ไม่ใช่​การ​เพิ่ม

ราคา​ของ​การ​เรียง​จ่าย​ที่ column ที่​ไม่​ได้​ใช้​เรียง

หัวข้อ​ที่​มีชื่อ​ว่า “ราคา​ของ​การ​เรียง​จ่าย​ที่ column ที่​ไม่​ได้​ใช้​เรียง”

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

การ​เรียง​ใหม่​ไม่​ได้​เปลี่ยน​แค่​ว่า​ข้าม​กลุ่ม​ไหน​ได้ มัน​เปลี่ยน​ขนาด​ของ​คลัง​ด้วย เพราะ​การ​เข้า​รหัส ทุก​แบบ​ใน​บท​ที่ 4 กับ​บท​ที่ 5 คิด​จาก​ค่าที่​มา​อยู่​ใน​กลุ่ม​เดียวกัน และ​จาก​ลำดับ​ของ​ค่า​เหล่า​นั้น

ทรง​ที่​วัดคีย์​ใน​ผล​ของ probeไบต์
placed_at · delta · เรียง​ตาม​เวลาplaced_at ไบต์ · delta · เรียงตามเวลา491,610
placed_at · delta · เรียง​ตาม​ร้านplaced_at ไบต์ · delta · เรียงตามร้าน585,360
id · delta · เรียง​ตาม​เวลาid ไบต์ · delta · เรียงตามเวลา350,360
id · delta · เรียง​ตาม​ร้านid ไบต์ · delta · เรียงตามร้าน434,110
placed_at · bit packing · เรียง​ตาม​เวลาplaced_at ไบต์ · bitpack · เรียงตามเวลา1,138,004
placed_at · bit packing · เรียง​ตาม​ร้านplaced_at ไบต์ · bitpack · เรียงตามร้าน1,149,184
restaurant · bit packing · เรียง​ตาม​เวลาrestaurant ไบต์ · bitpack · เรียงตามเวลา108,680
restaurant · bit packing · เรียง​ตาม​ร้านrestaurant ไบต์ · bitpack · เรียงตามร้าน26,657

สาม​คู่​บน​บวม​ขึ้น และ​ไม่มี​คู่​ไหน​ที่​ผู้​เขียน​ไป​แตะ​การ​เข้า​รหัส​ของ​มัน​เลย สิ่ง​เดียว​ที่​เปลี่ยน คือ​แถว​ที่มา​อยู่​ใน​กลุ่ม​เดียวกัน

delta ของ​หมวด​นี้​คือ​แบบ frame of reference ตาม​ชื่อ​ที่ colstore.py:42 ตั้ง​ไว้ คือ​เก็บ​ส่วน​ต่าง จาก​ค่าที่​น้อย​ที่สุด​ของ​กลุ่ม id ใน​ลำดับ​เดิม​ที่​ตก​อยู่​ใน​กลุ่ม​เดียวกัน​จึง​เป็น​ช่วง​ที่​ต่อ​เนื่อง และ​ระยะ​จาก​ค่า​น้อย​สุด​ถึง​ค่า​มาก​สุด​แคบ พอ​เรียง​ใหม่​ตาม​ร้าน id ใน​กลุ่ม​เดียวกัน​กระจาย​ทั้ง​ตาราง ระยะ​นั้น​กว้าง​ขึ้น และ​ตัว​รหัส​ก็​กว้าง​ตาม

คู่​สุดท้าย​คือ​ฝั่ง​ที่​ได้ restaurant เป็น column ที่​ถูก​ใช้​เรียง ค่าที่​เหมือน​กัน​จึง​มา​อยู่​ติด​กัน และ​กลุ่ม​ที่​มี​ร้าน​เดียว​ล้วน​ก็​ต้องการ​รหัส​แค่​ค่า​เดียว

คลัง​ใบ​เดียว​จ่าย​ทั้ง​สอง​อย่าง​พร้อม​กัน​ไม่​ได้ ถ้า​อยาก​ได้ 26,657 ไบต์​ของ​แถว​สุดท้าย ต้อง​รับ 585,360 ไบต์​ของ​แถว​ที่​สอง​ไป​ด้วย

🔁 การ​เรียง​ใน​บท​ที่ 4 ไม่ใช่​การ​เรียง​ใน​บท​นี้

บท​ที่ 4 เรียง​ตาม status แล้ว​วัดผล​ที่​เกิด​กับ status เอง ส่วน​บท​นี้​เรียง​ตาม restaurant แล้ว​วัดผล​ที่​เกิด​กับ column ที่​ไม่​ได้​ถูก​ใช้​เรียง

สอง​บท​จึง​ตอบ​คนละ​คำถาม บท​ที่ 4 ตอบ​ว่าการ​เรียง​ให้​อะไร​กับ sort key และ​บท​นี้​ตอบ​ว่า​มัน​เก็บ​เงิน ที่ไหน

ครึ่ง​หลัง — ขนาด​ของ row group ไม่มี​จุด​ต่ำ​สุด​ตรง​กลาง

หัวข้อ​ที่​มีชื่อ​ว่า “ครึ่ง​หลัง — ขนาด​ของ row group ไม่มี​จุด​ต่ำ​สุด​ตรง​กลาง”

ตะแกรง​ห้า​ค่า​ใน​หมวด 8_ขนาด_row_group และ​เส้น​ของ​ฝั่ง query ที่​ขึ้น​ตลอด

ขนาด​ของ​ก้อน​เป็น​ค่า​ตั้ง​ที่​ผู้​สร้าง​คลัง​เลือก​เอง คำ​ทำนาย​ที่​สม​เหตุ​สม​ผล​คือ​มัน​มี​ค่าที่​ดี​ที่สุด อยู่​ตรง​กลาง เพราะ​ก้อน​เล็ก​เกิน​ไป​ก็​มี metadata เยอะ ก้อน​ใหญ่​เกิน​ไป​ก็​ตัด​ทิ้ง​ไม่​ได้

probe กวาด​ห้า​ค่า​โดย​เปลี่ยน​ขนาด​ก้อน​อย่าง​เดียว ทุก​อย่าง​ที่​เหลือ​เหมือน​เดิม​หมด

scripts/column-store/probe.py:443 — ตะแกรง​ที่​เปลี่ยน​ขนาด​ก้อน​อย่าง​เดียว
sweep = {}
for rg in SWEEP:
st = build(f"sweep-{rg}", rows, rg, all_enc(BITPACK))
z = zone_query(st, "placed_at", DAY_LO, DAY_HI, extra=("total",))
pp, _ = point_read(st, one_i)

ฝั่ง​ของ query วัด​ห้า​อย่าง ทุก​อย่าง​อ่าน​จาก​คีย์​ของ​ตัว​เอง​ใน​ตะแกรง​เดียวกัน

แถว​ต่อ​ก้อนrow groupกลุ่มที่ข้ามแถวที่สแกนหน้าที่แตะ_column เงื่อนไขหน้าที่แตะ_สอง columnหน้าที่แตะเมื่อหยิบหนึ่งแถวทั้งแถว
5004003971,500357
2,000100992,0003615
10,000201910,000152942
25,0008725,000377286
50,0004350,00074142154

ไม่มี​ตัว​วัด​ของ query ตัว​ไหน​แย่​ลง​เมื่อ​ก้อน​เล็ก​ลง เส้น​ขึ้น​ตลอด​จาก​บน​ลง​ล่าง และ​คำ​ทำนาย​เรื่อง​จุด​ต่ำ​สุด​ตรง​กลาง​ไม่มี​อยู่​ใน​ตะแกรง​นี้

เหตุผล​ตรง​ไป​ตรง​มา ก้อน​เล็ก​แปล​ว่า​หน่วย​ที่​ตัด​ทิ้ง​ได้​เล็ก​ลง คำถาม​วัน​เดียว​จึง​ลาก​แถว​ที่​ไม่​เกี่ยวข้อง ติด​มา​น้อย​ลง คือ 1,500 แถว​ที่​ก้อน​ละ 500 เทียบ 50,000 แถว​ที่​ก้อน​ละ 50,000

ตัวเลข​ฝั่ง​เก็บ​สี่​ตัว​จาก​ตะแกรง​เดียวกัน และ​มัน​เดิน​สวน​ทาง​กับ​ตาราง​ข้าง​บน​ทุก​แถว

แถว​ต่อ​ก้อนไบต์ทั้งคลังหน้าทั้งคลังไบต์ที่เป็น dictionaryไบต์ metadata (footer)
5004,406,2451,0793,289,565106,448
2,0004,224,3271,0342,938,52726,648
10,0004,038,7929892,536,6325,368
25,0003,982,3369742,406,4722,176
50,0003,977,8609732,327,4281,112

ตัว​ที่​โต​คือ dictionary ทุก​ก้อน​ถือ dictionary ใบ​ของ​ตัว​เอง ก้อน​ละ 500 แถว​จึง​คัด​ลอก​รายการ ค่าที่​ไม่​ซ้ำ​ซ้ำ​ใหม่ 400 รอบ ตาม​คีย์ row group ของ​แถว​แรก

ไบต์ที่เป็น dictionary ที่ 3,289,565 ใน​แถว​แรก เทียบ​กับ 2,327,428 ใน​แถว​สุดท้าย คือ​ส่วน​ที่​ผู้​อ่าน​บท​ที่ 3 คาด​ไว้​ได้ เพราะ​บท​นั้น​วัด​ไว้​แล้ว​ว่า​ขอบเขต​ของ​กลุ่ม​เป็น​ตัว​ตัดสิน

หัวข้อ​ที่​มีชื่อ​ว่า “footer เป็น​ราคา​ที่​มอง​ไม่​เห็น จน​กระทั่ง​ก้อน​เล็ก​ลง”

ค่า​ของ​คีย์ ไบต์ metadata (footer) สอง​ปลายข​อง​ตะแกรง และ​กลไก​ที่​ทำให้​มัน​โต

metadata ท้าย​คลัง​ที่​ถือ min กับ max และ​ตำแหน่ง​ของ​ทุก column chunk เรียก​ว่า footerfootermetadata ท้าย​คลัง​ที่​ถือ min/max และ​ตำแหน่ง​ของ​ทุก column chunk · มัน​โต​ขึ้น​เมื่อ row group เล็ก​ลง วัด​ได้ 5,368 ไบต์​ที่​ก้อน​ละ 10,000 แถว และ 106,448 ไบต์​ที่​ก้อน​ละ 500 แถว มัน​คือ​ที่​ที่ zone map อยู่​จริง และ​เป็น​สิ่ง​ที่ query ต้อง​อ่าน​ก่อน​ตัดสิน​ใจ​ข้าม​กลุ่ม​ไหน

scripts/column-store/colstore.py:287 — ราคา​คงที่​ต่อ​หนึ่ง column chunk
def footer_bytes(kind, metas):
"""ไบต์ของ footer ถ้าเขียนลง file จริง · off,len,n,ndict,nruns u32 + bits,enc u8 + min,max"""
total = 0
for m in metas:
total += 4 * 5 + 1 + 1 # off,len,n,ndict,nruns + bits + enc

column ที่​เป็น​จำนวนเต็ม​สี่​ตัว จ่าย metadata เท่า​กัน​ทุก​ก้อน ไม่​ว่า​ข้าง​ใน​ก้อน​นั้น​จะ​มี​กี่​แถว ส่วน​สอง column ที่​เป็น​สตริง​จ่าย​ไม่​เท่า​กัน เพราะ colstore.py:295 บวก​ความ​ยาว​ของ ค่า min กับ max เข้าไป​ด้วย · จำนวน​ก้อน​จึง​เป็น​ตัว​คูณ​ที่​เกือบ​ตรง ไม่​ใช่​ตรง​เป๊ะ

ไบต์ metadata (footer) อ่าน​ได้ 1,112 ที่​ก้อน​ละ 50,000 แถว และ 106,448 ที่​ก้อน​ละ 500 แถว ใน​ตะแกรง​เดียวกัน

นี่​คือ​ของ​ที่​ตำรา​เรียก​ว่า​ราคา​ของ​ก้อน​เล็ก และ​มัน​มี​จริง แต่​มัน​ไม่​ได้​ทำให้​เกิด​จุด​ต่ำ​สุด​ตรง​กลาง เพราะ​ฝั่ง query ก็​ดี​ขึ้น​เรื่อยๆ ใน​ทิศ​เดียวกัน​จน​สุด​ตะแกรง

คำถาม​ที่​ถูก ไม่ใช่​ขนาด​ไหน​ดี​ที่สุด แต่​เป็น​จะ​จ่าย​ที่​ตัว​วัด​ไหน

หัวข้อ​ที่​มีชื่อ​ว่า “คำถาม​ที่​ถูก ไม่ใช่​ขนาด​ไหน​ดี​ที่สุด แต่​เป็น​จะ​จ่าย​ที่​ตัว​วัด​ไหน”

คีย์​สาม​ตัว​ปิด​ท้าย​หมวด 8_ขนาด_row_group และ​ตัว​ที่​สาม​เป็น​บู​ลี​นที่​ตอบ​คำถาม​ของ​บท​นี้​ตรงๆ

probe ไม่​ปล่อย​ให้​ผู้​อ่าน​สรุป​เอง มัน​เลือก​ผู้​ชนะ​ของ​แต่ละ​ตัว​วัด​แล้ว​เก็บ​ไว้​เป็น​คีย์

คำถามคีย์​ใน​ผล​ของ probeค่า
ขนาด​ที่ query เลือกสรร​อ่าน​น้อย​ที่สุดrow group ที่ query เลือกสรร อ่านน้อยที่สุด500
ขนาด​ที่​คลัง​ทั้ง​ใบ​เล็ก​ที่สุดrow group ที่คลังเล็กที่สุด50000
สอง​อัน​นี้​เป็น​อัน​เดียวกัน​ไหมสองอันนี้เป็นอันเดียวกันfalse

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

  • ถ้า​ปริมาณ​ที่​เก็บ​เป็น​ข้อ​จำกัด ก้อน​ใหญ่​คือ​คำ​ตอบ และ​คุณ​จ่าย​ด้วย​หน้าที่​แตะ​ทุก​คำถาม
  • ถ้า​คำถาม​เลือกสรร​และ​ต้อง​แตะ​หน้า​น้อย ก้อน​เล็ก​คือ​คำ​ตอบ และ​คุณ​จ่าย​ด้วย dictionary ที่​ถูก​คัด​ลอก​ซ้ำ กับ footer ที่​โต​ขึ้น

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

รู้​แล้ว​ว่า​จัด​วาง​อย่างไร​ให้​คำถาม​ที่​เลือกสรร​ได้​เปรียบ ยัง​ไม่รู้​ว่าการ​หยิบ​หนึ่ง​แถว​คืน​มา​แพง​แค่​ไหน

ช่อง​สุดท้าย​ของ​ตาราง​ฝั่ง query คือ หน้าที่แตะเมื่อหยิบหนึ่งแถวทั้งแถว และ​มัน​ขยับ​จาก 7 ถึง 154 ตลอด​ตะแกรง

ตัวเลข​ชุด​นั้น​เป็น​เงา​ของ​สิ่ง​ที่ บท​ที่ 8 วัด​เต็มๆ คือ​ราคา​ที่​การ​วาง​แนว​เรียก​เก็บ​ตอน​ต้อง​ประกอบ​แถว​กลับ​คืน​ทั้ง​แถว


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้
  • Python — Sorting Techniques (เข้า​ถึง 2026-09-10) — ที่มา​ของ​ข้อ​ที่​ว่า sorted รักษา​ลำดับ​เดิม​ของ​แถว​ที่​คีย์​เท่า​กัน · probe ไม่​ได้​เชื่อ​ข้อ​นี้​เฉยๆ มัน​วัด​จาก​แถว​จริง​ที่ probe.py:412 แล้ว​เก็บ​ผล​เป็น​คีย์

  • Apache Parquet — Metadata (เข้า​ถึง 2026-09-10) — ที่มา​ของ​กลไก​ที่​ว่า min กับ max ของ​ทุก column chunk อยู่​ใน metadata ท้าย​คลัง · ColumnMetaData ถือ Statistics ที่​มี min_value กับ max_value · ดึง​หน้า File Format มา​อ่าน​แล้ว​ไม่มี​คำ​ว่า statistics min_value หรือ max_value เลย เพื่อ​ให้​ข้าม​ทั้ง​ก้อน​ได้​โดย​ไม่​ต้อง​อ่าน​ข้าง​ใน · อ้าง​ใน​ฐานะ​รูป​แบบ ไม่ใช่​ใน​ฐานะ​ของ​ที่​วัด และ คลัง​ของ​คอร์ส​นี้​ไม่ใช่ Parquet

  • ตัวเลข​ทุก​ตัว​ใน​บท​นี้​มา​จาก python3 scripts/column-store/probe.py ซึ่ง​สร้าง​คลัง​ขึ้น​ใหม่ ทุกร​อบ​จาก fixture.py ของ​คอร์ส​ที่ 26 · ค่าที่​ตรึง​ไว้​อยู่​ที่ scripts/column-store/expected/column-store.txt หมวด 7_เรียงแล้วได้อย่างเสียอย่าง และ​หมวด 8_ขนาด_row_group

  • ทรง​ของ​การ​เรียง​อยู่​ที่ scripts/column-store/colstore.py บรรทัด​ที่ 466 และ​ทรง​ของ footer อยู่​ที่​บรรทัด​ที่ 287 · คอร์ส​นี้​ไม่​แก้​อะไร​ใน scripts/query-engine/qe/ แม้แต่ byte เดียว

เช็กความเข้าใจ — บทที่ 7

ข้อ 1 / 3

เรียงตารางใหม่ตาม restaurant แล้ว คำถามช่วงเวลาข้าม row group ได้กี่กลุ่ม