หยิบกลับมาหนึ่งแถวทั้งแถว heap แตะ 1 หน้า ส่วนคลัง column แตะ 42 หน้า
คำถามที่คลัง column ทำได้แย่ที่สุดไม่ใช่คำถามที่แตะทุก column มันคือคำถามที่บอกว่า เอาแถวนี้มาทั้งแถว โดยรู้ตัวชี้ของแถวนั้นอยู่แล้ว
heap ตอบคำถามนั้นด้วยการแตะหน้าเดียว เพราะค่าทุก column ของแถวนั้นเขียนติดกันอยู่ในหน้าเดียว ส่วนคลัง column ต้องไปหยิบทีละที่แล้วประกอบกลับ
การหยิบแบบนี้เรียกว่า point lookuppoint lookupการหยิบหนึ่งแถวทั้งแถวด้วยตัวชี้ที่รู้อยู่แล้ว · heap แตะ 1 หน้า ส่วนคลัง column แตะ 42 หน้าในทรง bitpack และ 133 หน้าในทรง raw · 42 หน้านั้นแยกราย column ออกมาเป็น id 11 · customer 4 · status 2 · restaurant 2 · placed_at 11 และ total 12 · ที่จ่ายเพิ่มคือ dictionary ทั้งใบ ซึ่งกินหน้าที่เป็น dictionary ล้วนรวมทุก row group ทั้ง 20 ก้อนไป 214 หน้าที่ id · 204 หน้าที่ placed_at และ 209 หน้าที่ total ค่าสามตัวนี้เป็นของทั้ง column ไม่ใช่ส่วนหนึ่งของ 42 และบทนี้วัดว่ามันแพงแค่ไหน
คำทำนายที่ผู้เขียน probe เขียนไว้ก่อนวัดคือ หนึ่งหน้าต่อหนึ่ง column ค่าที่วัดได้คือ 42 หน้า ในทรง bitpack และ 133 หน้า ในทรง raw
สั่ง python3 scripts/column-store/probe.py ได้เลย มันใช้ Python 3 กับ standard
library ล้วน และไม่มี container ให้เก็บกวาดทีหลัง
ค่าที่วัดได้ทุกตัวที่บทนี้พิมพ์มีคีย์ของตัวเองอยู่ใน
scripts/column-store/expected/column-store.txt และชื่อหมวดกับชื่อคีย์เขียนกำกับไว้ในทุกตาราง
บทนี้ไม่สร้างตัวเลขใหม่สักตัว และไม่คำนวณอัตราส่วนหรือส่วนต่างขึ้นเองแม้แต่ค่าเดียว
คำทำนายคือหนึ่งหน้าต่อหนึ่ง column และการวัดหักล้างมัน
หัวข้อที่มีชื่อว่า “คำทำนายคือหนึ่งหน้าต่อหนึ่ง column และการวัดหักล้างมัน”heap แตะหน้าเดียว ส่วนคลัง column ทั้งสองทรงแตะมากกว่าที่คำทำนายก่อนวัดเขียนไว้
ตัวชี้ของแถวถูกส่งเข้าไปให้ทั้งสองฝั่งเหมือนกัน ไม่มีฝั่งไหนต้องค้นหาก่อน สิ่งที่เหลืออยู่ จึงเป็นราคาของการไปหยิบค่าจากที่ที่มันถูกวางไว้
ทรงที่วัด · หมวด ### 2_SELECT_ดาว | คีย์ในผลของ probe | หน้าที่แตะ |
|---|---|---|
| heap · หยิบหนึ่งแถวโดยมีตัวชี้ | หนึ่งแถว · heap (มี rid) | 1 |
| คลัง column ที่ยังไม่เข้ารหัส | หนึ่งแถว · column raw | 133 |
| คลัง column · bit packing | หนึ่งแถว · column bitpack | 42 |
แถวล่างสุดคือทรงเดียวกับที่ชนะ heap ขาดตอนสแกนทั้งตาราง ทรงนั้นไม่ได้เปลี่ยนอะไรเลย เปลี่ยนแค่คำถาม แล้วมันกลายเป็นฝั่งที่แพ้
ทรงที่วัด · หมวด ### 2_SELECT_ดาว | คีย์ในผลของ probe | หน้าที่แตะ |
|---|---|---|
| heap · สแกนทั้งตาราง | สแกนทั้งตาราง · heap | 3153 |
| คลัง column ที่ยังไม่เข้ารหัส · สแกนทั้งตาราง | สแกนทั้งตาราง · column raw | 3320 |
| คลัง column · bit packing · สแกนทั้งตาราง | สแกนทั้งตาราง · column bitpack | 989 |
ค่า 3153 · 3320 และ 989 เป็นพาดหัวของ บทที่ 1 ซึ่งเป็นเจ้าของทั้งสามค่า
หมวด ### 2_SELECT_ดาว เก็บทั้งสามค่าไว้เป็นคีย์ของตัวเองด้วย ตารางข้างบนจึงอ่านจาก
หมวดเดียวกับที่บทนี้ใช้ ไม่ได้ลอกข้ามหมวดมา
dictionary ทั้งใบคือของที่ต้องอ่านก่อนถอดรหัสหนึ่งค่า
หัวข้อที่มีชื่อว่า “dictionary ทั้งใบคือของที่ต้องอ่านก่อนถอดรหัสหนึ่งค่า”สิ่งที่ทำให้เลขบวมไม่ใช่จำนวน column ที่ต้องแตะ แต่เป็นสิ่งที่ต้องอ่านก่อนแตะค่านั้น
ทรง bitpack เก็บตัวรหัสไว้ที่หนึ่ง และเก็บรายการค่าที่ไม่ซ้ำไว้อีกที่หนึ่ง ตัวรหัสตัวเดียว แปลกลับเป็นค่าจริงไม่ได้ ถ้าไม่มีรายการนั้นอยู่ในมือ
if enc in (DICT, BITPACK): vs = self.dict_of(col, g) # ต้องมี dictionary ทั้งใบก่อนเสมอ if enc == DICT: w = m["bits"] // 8 fmt = {1: "<B", 2: "<H", 4: "<I"}[w] b = self.read_range(col, m["off"] + m["codes_off"] + w * j, w) return vs[struct.unpack(fmt, b)[0]] a, ln = bit_range(j, m["bits"]) b = self.read_range(col, m["off"] + m["codes_off"] + a, ln) v = (int.from_bytes(b, "little") >> ((j * m["bits"]) % 8)) & ((1 << m["bits"]) - 1) return vs[v]บรรทัด vs = self.dict_of(col, g) คือราคาที่ซ่อนอยู่ การอ่านตัวรหัสเองแตะหน้าน้อยมาก
แต่ dictionary ที่ต้องมีก่อนนั้นเป็นของทั้งใบ
probe แยกหน้าที่เป็น dictionary ล้วนออกมารายตัวให้แล้ว ค่าข้างล่างนับหน้าที่ไม่ซ้ำ ของทุก row grouprow groupก้อนของแถวที่คลังแนว column ตัดออกมาเป็นหน่วยเดียวกัน ทุก column ในก้อนเดียวกันถือแถวชุดเดียวกัน · ขอบเขตของก้อนเป็นสิ่งที่ตัดสินว่า dictionary ถูกคัดลอกซ้ำกี่ใบ และ zone map ข้ามอะไรได้บ้าง รวมกันในทรง bitpack
| column | คีย์ หน้าที่เป็น dictionary ล้วนต่อ column (bitpack) | หน้า |
|---|---|---|
id | id | 214 |
customer | customer | 58 |
status | status | 18 |
restaurant | restaurant | 25 |
placed_at | placed_at | 204 |
total | total | 209 |
column ที่มีค่าไม่ซ้ำเยอะจ่าย dictionary ที่ใหญ่ที่สุด และ column พวกนั้นคือ id
placed_at กับ total ซึ่งเป็นสามตัวเดียวกับที่บทที่ 3 วัดว่า dictionary ทำให้ใหญ่ขึ้น
flowchart TB Q["หยิบหนึ่งแถวทั้งแถว · มีตัวชี้ของแถวอยู่แล้ว"] Q --> H["heap · ค่าทุก column ของแถวนี้อยู่ในหน้าเดียว · แตะ 1 หน้า"] Q --> C["คลัง column · ค่าของแถวนี้กระจายอยู่ใน column chunk คนละก้อน"] C --> C1["ทรง raw · สตริงความกว้างไม่คงที่ กระโดดไปค่าเดียวไม่ได้ · แตะ 133 หน้า"] C --> C2["ทรง bitpack · ต้องอ่าน dictionary ของก้อนนั้นทั้งใบก่อนถอดหนึ่งค่า · แตะ 42 หน้า"]
คำบรรยายภาพ: กิ่งซ้ายคือคลังแนวแถวที่จบในหน้าเดียว กิ่งขวาแตกเป็นสองทรงที่วางแนวเหมือนกัน และแพ้ด้วยเหตุผลคนละข้อ ทรงหนึ่งแพ้ที่ความกว้างที่ไม่คงที่ อีกทรงแพ้ที่ dictionary
ทรงที่ยังไม่เข้ารหัสแพงกว่าทรงที่เข้ารหัสแล้ว
หัวข้อที่มีชื่อว่า “ทรงที่ยังไม่เข้ารหัสแพงกว่าทรงที่เข้ารหัสแล้ว”แยกหน้าที่แตะออกรายตัว แล้วคนที่กินหน้าในสองทรงนั้นเป็นคนละกลุ่มกัน
ส่วนของ column ตัวเดียวที่อยู่ใน row group หนึ่งก้อนเรียกว่า column chunkcolumn chunkส่วนของ column หนึ่งตัวภายใน row group หนึ่งก้อน · มันเลือกการเข้ารหัสและความกว้างของตัวรหัสเป็นของตัวเอง ค่าสองอย่างนั้นจึงเป็นค่าต่อก้อน ไม่ใช่ค่าต่อทั้ง column การหยิบค่าเดียวคือการเปิด chunk ของแถวนั้นทีละ column แล้วนับว่าใครทำให้แตะกี่หน้า
| column | คีย์ หนึ่งแถว · หน้าที่แตะแยกราย column · raw | คีย์ หนึ่งแถว · หน้าที่แตะแยกราย column · bitpack |
|---|---|---|
id | 1 | 11 |
customer | 1 | 4 |
status | 25 | 2 |
restaurant | 104 | 2 |
placed_at | 1 | 11 |
total | 1 | 12 |
ช่องซ้ายบอกว่าในทรง raw ค่าที่แตะหน้ามากคือ status กับ restaurant ซึ่งเป็น column
ที่เก็บสตริงตามที่มันเป็น ส่วน column ที่เป็นเลขจ่ายน้อยที่สุดเท่าที่เป็นไปได้
เหตุผลอยู่ในบรรทัดสุดท้ายของ one_value เอง
# RAW ที่ความกว้างไม่คงที่ และ RLE — กระโดดตรงไปค่าเดียวไม่ได้ ต้องอ่านทั้ง chunk return self.chunk(col, g)[j]ค่าที่กว้างคงที่คำนวณตำแหน่งได้ตรงตัว จึงอ่านเฉพาะช่วง byte ที่ต้องการ ส่วนสตริงที่ยาวไม่เท่ากัน บอกไม่ได้ว่าค่าที่ต้องการเริ่มที่ byte ไหน จนกว่าจะอ่านทุกค่าก่อนหน้ามันแล้ว
ช่องขวาสลับกลุ่มผู้จ่าย ในทรง bitpack status กับ restaurant ถูกลงมาเหลือหน้าน้อยที่สุด
และตัวที่ขึ้นมาแทนคือ id placed_at กับ total ซึ่งเป็นเจ้าของ dictionary ใบใหญ่
นี่คือข้อที่กลับด้านกับสัญชาตญาณ ทรงที่ยังไม่เข้ารหัสควรจะเบากว่าในการหยิบค่าเดียว เพราะไม่มี dictionary ให้อ่าน แต่ค่าที่วัดได้คือ 133 เทียบ 42
ตะแกรงของ k คลัง column ทรงตัว ส่วน heap ไต่ขึ้นเรื่อยๆ
หัวข้อที่มีชื่อว่า “ตะแกรงของ k คลัง column ทรงตัว ส่วน heap ไต่ขึ้นเรื่อยๆ”หยิบทีละแถวมันแพง แต่ราคาของมันไม่ได้โตตามจำนวนแถวที่หยิบ
probe สุ่มแถวมา k แถวแล้วดึงทั้งแถวทุกแถว ฝั่ง heap นับหน้าที่ไม่ซ้ำที่แถวเหล่านั้นตกอยู่ ส่วนฝั่งคลัง column นับหน้าที่แตะทั้งหมดจากตัวนับเดียวกัน
| k | คีย์ heap (หน้าที่ไม่ซ้ำ) | คีย์ column bitpack |
|---|---|---|
| 1 | 1 | 42 |
| 10 | 10 | 297 |
| 100 | 99 | 910 |
| 1000 | 868 | 987 |
| 2000 | 1491 | 987 |
| 3000 | 1934 | 989 |
| 5000 | 2519 | 989 |
| 10000 | 3051 | 989 |
ทั้งหมดอยู่ใต้คีย์ สุ่ม k แถวแล้วดึงทั้งแถว · หน้าที่แตะ ในหมวด ### 2_SELECT_ดาว
ช่องขวาชนเพดานแล้วหยุด เพราะเมื่อสุ่มมามากพอ ทุกก้อนก็ถูกเปิดครบอยู่ดี ค่าที่มันทรงตัว คือค่าเดียวกับการสแกนทั้งตารางในทรงเดียวกัน ส่วนช่องซ้ายไต่ขึ้นตามจำนวนแถวที่หยิบ
ที่วัด · หมวด ### 2_SELECT_ดาว | คีย์ในผลของ probe | ค่า |
|---|---|---|
| ค่าแรกในตะแกรงที่คลัง column ไม่แพ้แล้ว | k ที่คลัง column เริ่มไม่แพ้ heap | 2000 |
| ค่านั้นคิดเป็นสัดส่วนของตาราง | คิดเป็นสัดส่วนของตาราง (%) | 1.0 |
probe เดินตะแกรงของ k ตามค่าที่ตั้งไว้ มันไม่ได้ค้นหาจุดที่สองเส้นตัดกัน
ที่ k = 1000 คลัง column ยังแพ้ คือ 987 เทียบ 868 และที่ k = 2000 มันไม่แพ้แล้ว คือ 987 เทียบ 1491
จุดที่สองเส้นตัดกันจริงอยู่ที่ไหนสักแห่งระหว่างจุดสองจุดนั้น และ probe ไม่ได้วัดมัน ค่า 2000 ที่รายงานออกมาจึงเป็นค่าแรกในตะแกรงที่เงื่อนไขเป็นจริง ไม่ใช่จุดตัด
ในคำถามของร้าน ครัวคุณแม่ late materialization ลดค่าที่ถอดรหัส แต่ไม่ลดหน้าที่แตะสักหน้า
หัวข้อที่มีชื่อว่า “ในคำถามของร้าน ครัวคุณแม่ late materialization ลดค่าที่ถอดรหัส แต่ไม่ลดหน้าที่แตะสักหน้า”ยาแก้ที่ตำราเสนอสำหรับราคาข้อนี้ ให้ผลไม่เท่ากันในสองหน่วย และไม่เท่ากันในสี่ทรงที่ probe วัด
ยาแก้ที่ตำราเสนอคือ late materializationlate materializationการถอดรหัส column เงื่อนไขก่อน แล้วค่อยดึงค่าของแถวที่ตรงจริง · ผลของมันต่างกันตามทรงและตามคำถาม · ในทรง bitpack ของร้าน ครัวคุณแม่ ค่าที่ถอดรหัสลงจาก 400,000 เหลือ 211,934 ค่า แต่หน้าที่แตะค้างที่ 277 หน้าทั้งสองทาง และเมื่อนับ dictionary ที่ต้องอ่านด้วย ค่าที่ถอดรหัสขึ้นเป็น 406,972 ค่า ซึ่งมากกว่าทางที่มันมาแทน · ส่วนในทรง raw ของลูกค้า 995 หน้าที่แตะลงจาก 196 เหลือ 78 หน้า คือถอดรหัส column ที่เป็นเงื่อนไขก่อน แล้วค่อยไปดึงค่าของ column ที่ต้องส่งออก เฉพาะแถวที่ตรงเงื่อนไขจริง
def late_mat(store, pred_col, pred_val, payload_col): """late materialization: ถอดรหัส column เงื่อนไขก่อน แล้วค่อยไปหยิบเฉพาะแถวที่ตรง""" q = cs.Q(store) matched = [] for g in range(store.ngroups): vals = q.chunk(pred_col, g) for j, v in enumerate(vals): if v == pred_val: matched.append((g, j)) pred_pages = q.reads_of(pred_col) for g, j in matched: q.one_value(payload_col, g, j) late_pages = q.reads_of(payload_col) q.close()ทางที่มันมาแทนคือการถอดรหัสทั้งสอง column ทั้งก้อน แล้วค่อยทิ้งแถวที่ไม่ตรง
ค่าของทางนั้นคือคีย์ที่ลงท้ายด้วย _early
ที่วัด · หมวด ### 9_late_materialization · ทรง ร้าน ครัวคุณแม่ · payload total เข้ารหัสแบบ bitpack | คีย์ในผลของ probe | ค่า |
|---|---|---|
| แถวที่สแกนไปทั้งหมด | แถวที่สแกน | 200,000 |
| แถวที่ตรงเงื่อนไข | แถวที่ตรง | 11,934 |
| row group ที่มีแถวตรงอยู่ | row group ที่มีแถวตรงอยู่ | 20 |
| ค่าที่ถอดรหัส · ทางที่มันมาแทน | ค่าที่ถอดรหัส_early | 400,000 |
| ค่าที่ถอดรหัส · นับเฉพาะตัวรหัส | ค่าที่ถอดรหัส_late_เฉพาะรหัส | 211,934 |
| หน้าที่แตะที่ column ซึ่งดึงออกมา · ทางที่มันมาแทน | หน้าที่แตะ column ที่ดึงออกมา_early | 277 |
| หน้าที่แตะที่ column ซึ่งดึงออกมา · ทาง late | หน้าที่แตะ column ที่ดึงออกมา_late | 277 |
สองแถวล่างเป็นค่าเดียวกัน ในทรงนี้กลไกนี้ไม่ลดหน้าที่แตะสักหน้า ทั้งที่มันลดค่าที่ถอดรหัสลงจริง · อีกสามทรงอยู่ในหัวข้อถัดไป และสองในนั้นลดหน้าที่แตะลงจริง
เหตุผลอยู่ที่แถว row group ที่มีแถวตรงอยู่ ซึ่งเท่ากับจำนวนก้อนทั้งคลัง แถวที่ตรงเงื่อนไข
กระจายอยู่ทุกก้อน การข้ามก้อนจึงทำไม่ได้ และหน้าที่ต้องเปิดก็เท่าเดิม
dictionary กินกลไกนี้ทิ้ง และทรงที่มันช่วยคือทรง raw
หัวข้อที่มีชื่อว่า “dictionary กินกลไกนี้ทิ้ง และทรงที่มันช่วยคือทรง raw”เมื่อนับ dictionary ที่ต้องอ่านเข้าไปด้วย ค่าที่ถอดรหัสวิ่งกลับไปหาทางที่มันมาแทน และทรงหนึ่งวิ่งเลยไป
การถอดรหัสตัวรหัสหนึ่งตัวต้องมี dictionary ทั้งใบก่อน ตามที่ one_value บังคับไว้
probe จึงเก็บค่าที่นับ dictionary เข้าไปด้วยเป็นคีย์ของตัวเอง
ทรง · หมวด ### 9_late_materialization | ค่าที่ถอดรหัส_early | ค่าที่ถอดรหัส_late_เฉพาะรหัส | ค่าที่ถอดรหัส_late_รวม dictionary |
|---|---|---|---|
ร้าน ครัวคุณแม่ · payload total เข้ารหัสแบบ bitpack | 400,000 | 211,934 | 406,972 |
ร้าน ครัวคุณแม่ · payload total แบบ raw | 400,000 | 211,934 | 211,934 |
ลูกค้า 995 · payload total เข้ารหัสแบบ bitpack | 400,000 | 200,097 | 395,135 |
ลูกค้า 995 · payload total แบบ raw | 400,000 | 200,097 | 200,097 |
แถวที่เป็น bitpack ทั้งสองแถวมีช่องขวาสุดวิ่งกลับขึ้นไปหาช่องซ้ายสุด และแถวบนสุดวิ่งเลยไปแล้ว คือ 406,972 มากกว่า 400,000 ซึ่งแปลว่าในทรงนั้นกลไกนี้ถอดรหัสมากกว่าทางที่มันมาแทน ส่วนแถวที่เป็น raw มีช่องขวาสุดเท่ากับช่องกลาง เพราะทรงนั้นไม่มี dictionary ให้อ่าน
หน้าที่แตะบอกเรื่องเดียวกันด้วยหน่วยอีกหน่วย
ทรง · หมวด ### 9_late_materialization | แถวที่ตรง | หน้าที่แตะ column ที่ดึงออกมา_early | หน้าที่แตะ column ที่ดึงออกมา_late |
|---|---|---|---|
ร้าน ครัวคุณแม่ · payload total เข้ารหัสแบบ bitpack | 11,934 | 277 | 277 |
ร้าน ครัวคุณแม่ · payload total แบบ raw | 11,934 | 196 | 196 |
ลูกค้า 995 · payload total เข้ารหัสแบบ bitpack | 97 | 277 | 258 |
ลูกค้า 995 · payload total แบบ raw | 97 | 196 | 78 |
แถวล่างสุดคือแถวเดียวในตารางที่หน้าที่แตะลดลงอย่างเห็นได้ และมันเป็นทรง raw ที่เงื่อนไขตรงแค่ 97 แถว
ประโยคที่พบทั่วไปคือ ราคาของการประกอบแถวกลับคืนแก้ได้ด้วย late materialization
ค่าที่วัดได้บอกว่ากลไกนี้จ่ายคืนน้อยที่สุดตรงที่ตำราแนะนำให้ใช้การเข้ารหัสหนักที่สุด ทรง bitpack ที่ลูกค้า 995 ลดหน้าที่แตะจาก 277 เหลือ 258 ขณะที่ทรง raw ในคำถามเดียวกันลดจาก 196 เหลือ 78
การเข้ารหัสที่ทำให้การสแกนชนะในบทที่ 1 คือสิ่งที่กินกลไกนี้ทิ้งในบทนี้ และนั่นคือ ราคาอีกด้านของการเลือก ซึ่งคอร์สนี้เจอมาแล้วทุกบท
รวบธีสิสของคอร์ส
หัวข้อที่มีชื่อว่า “รวบธีสิสของคอร์ส”ทุกแถวในตารางนี้ชี้กลับไปที่บทที่วัดมัน และตารางถัดไปคือแถวที่คอร์สนี้ยังทำไม่ได้
ธีสิสของคอร์สคือการวางแนวกับการเข้ารหัสเป็นคนละเรื่อง สรุปตามตำรารวมสองอย่างนี้ไว้เป็น ก้อนเดียว และการรวมนั้นวัดได้ว่าผิดตั้งแต่บทแรก
| ข้ออ้างที่วัดได้ | บทที่พิสูจน์มัน |
|---|---|
| การวางแนวอย่างเดียวอ่านมากกว่าคลังแนวแถวที่มันมาแทน | บทที่ 1 |
| สิ่งที่การวางแนวซื้อกลับมาคือสิทธิ์ที่จะไม่อ่าน column ที่ไม่ได้ถาม | บทที่ 2 |
| dictionary encoding ไม่ได้ทำให้เล็กลงเสมอ เงื่อนไขอยู่ที่ขอบเขตของ row group | บทที่ 3 |
| ความกว้างของตัวรหัสเป็นตัวแปรที่เลือกได้ ไม่ใช่ byte เต็มเสมอ | บทที่ 4 |
| ชื่อของการเข้ารหัสไม่ได้บอกว่ามันทำอะไร รูปของข้อมูลต่างหากที่บอก | บทที่ 5 |
| zone map ตัดก้อนทิ้งได้ก็ต่อเมื่อค่าใน column นั้นเดินตามลำดับที่เขียน | บทที่ 6 |
| ตารางมีลำดับได้ลำดับเดียว การเลือก sort key จึงยกส่วนได้เปรียบจาก column หนึ่งไปให้อีก column | บทที่ 7 |
| ราคาของการวางแนวคือการประกอบแถวกลับคืน และยาแก้ของตำราถูกการเข้ารหัสกินทิ้ง | บทนี้ |
รูปแบบ ParquetApache Parquetรูปแบบของคลังแนว column ที่วางข้อมูลเป็น row group และเก็บ min/max กับตำแหน่งของทุก column chunk ไว้ใน metadata ท้ายคลัง · คอร์สนี้อ้างมันในฐานะรูปแบบเท่านั้น สิ่งที่วัดจริงคือคลังที่ colstore.py สร้างขึ้น ซึ่งไม่ใช่ Parquet วางข้อมูลเป็น row group และเก็บ metadata ไว้ท้ายคลัง คอร์สนี้ยืมรูป
กับศัพท์มาใช้ แต่คลังที่วัดคือคลังที่ colstore.py เขียน ไม่ใช่ Parquet
แถวข้างล่างเป็นสิ่งที่คอร์สนี้ไม่ได้วัด และเหตุผลของแต่ละแถวต่างกัน การเขียนถึงมันในฐานะ ข้ออ้างจึงเป็นการอ้างเกินสิ่งที่ probe พิสูจน์ได้
| แถวที่คอร์สนี้ยังทำไม่ได้ | เหตุผล |
|---|---|
| vectorized execution และ SIMD | probe ไม่ได้วัดมันสักทรง และการวัดมันต้องใช้หน่วยเวลา |
| การบีบอัดทั่วไปอย่าง Snappy · gzip · zstd | probe วัดการเข้ารหัส ไม่ได้วัดตัวบีบอัดสักตัว |
| Parquet · ORC · Arrow ของจริง | คอร์สนี้อ้างรูปแบบได้เฉพาะเรื่องกลไก ไม่ใช่ในฐานะผลการวัด |
| การเขียนและการอัปเดตแถวในคลังแนว column | probe เขียนคลังครั้งเดียวแล้ววัดการอ่านอย่างเดียว |
| การขนานและ I/O จริงของ disk | Pager นับครั้งที่อ่านหน้า · heapfile.py:74 เรียก seek และ heapfile.py:76 เรียก read จริง ส่วนคำถามว่าครั้งไหนถึงจานหมุนจริง เป็นเรื่องของ page cache ซึ่งคอร์สนี้ไม่วัด |
| ความเร็ว | ทั้งคอร์สไม่มีหน่วยเวลาสักตัว เหมือนคอร์สที่ 26 และ 28 ในเสาเดียวกัน |
| การประมาณจำนวนแถว · histogram · uniformity · independence | zone map เป็นการตัดทิ้ง ไม่ใช่การประมาณ · เรื่องนี้เป็นของคอร์สที่ 26 บทที่ 4 ซึ่งมีหัวข้อของมันเองอยู่แล้ว |
| transaction และ isolation บนคลังแนว column | เป็นของคอร์สที่ 28 ซึ่งเป็นคนละคำถาม |
คำถามสุดท้ายที่คอร์สนี้ทิ้งไว้ให้ไม่ใช่ ควรใช้คลังแบบไหน แต่เป็น คำถามของงานคุณ หน้าตาเหมือนการสแกน หรือเหมือนการหยิบทีละแถว เพราะตัวเลขสองชุดในบทนี้ตอบคนละอย่าง
-
Apache Parquet — File Format (เข้าถึง 2026-09-10) — ที่มาของศัพท์ row group กับ column chunk และของข้อที่ว่า ตำแหน่งของทุก column chunk ถูกเก็บไว้ใน metadata ท้ายคลัง
-
Apache Parquet — Metadata (เข้าถึง 2026-09-10) — ที่มาของข้อที่ว่า metadata ท้ายคลังถือตำแหน่งของทุก column chunk ทำให้ผู้อ่านกระโดดไปที่ chunk ที่ต้องการได้ · เป็นกลไกของรูปแบบ ไม่ใช่ผลการวัดของคอร์สนี้
-
ตัวเลขทุกตัวในบทนี้มาจาก
python3 scripts/column-store/probe.pyซึ่งสร้างคลังขึ้นใหม่ ทุกรอบจากfixture.pyของคอร์สที่ 26 · ค่าที่ตรึงไว้อยู่ที่scripts/column-store/expected/column-store.txtหมวด### 2_SELECT_ดาวและ### 9_late_materialization -
หน้าที่แตะทุกค่ามาจาก
Pager.readsตัวเดียวกับคอร์สที่ 26 ที่scripts/query-engine/qe/heapfile.pyบรรทัดที่ 71 และบรรทัดที่ 75 · คอร์สนี้ไม่แก้อะไรในscripts/query-engine/qe/แม้แต่ byte เดียว
เช็กความเข้าใจ — บทที่ 8
ข้อ 1 / 3หยิบหนึ่งแถวทั้งแถวจากคลัง column โดยมีตัวชี้ของแถวอยู่แล้ว แตะกี่หน้า เทียบกับ heap ที่แตะ 1 หน้า