- ClickHouse เปิดตัวชนิด JSON ใหม่ที่นำค่าตามพาธ JSON ไปวางไว้ใน ที่เก็บข้อมูลแบบคอลัมน์จริง เพื่อหลีกเลี่ยงคอขวดจากการใส่เอกสาร JSON เป็นสตริงแล้วต้อง parse ทุกครั้ง
- แกนหลักของการใช้งานคือชนิด Variant และ Dynamic โดยแม้พาธ JSON เดียวกันจะมีชนิดต่างกัน เช่น จำนวนเต็ม สตริง หรืออาร์เรย์ ก็จะไม่ฝืนรวมให้เป็นชนิดร่วมต่ำสุด
- จำกัดจำนวนซับคอลัมน์และจำนวนไฟล์แยกตามชนิดด้วยค่าเริ่มต้น
max_dynamic_paths ที่ 1024 และ max_dynamic_types ที่ 32 เพื่อควบคุมการเพิ่มขึ้นของ file descriptor และต้นทุนการ merge
- สามารถปรับวิธีจัดเก็บรายพาธได้ด้วย type hint,
SKIP, SKIP REGEXP และอ่านค่าด้วย ไวยากรณ์ซับคอลัมน์ เช่น C.a.b
- ชนิดใหม่นี้มีเป้าหมายเพื่อแทนที่
Object('json') ที่ถูก deprecated แล้ว และยังมีการปรับปรุงใน roadmap สำหรับการใช้พาธคีย์ JSON ใน primary key หรือ data-skipping index
ความท้าทายในการปรับ JSON ให้เข้ากับที่เก็บข้อมูลแบบคอลัมน์
- JSON ถูกใช้เป็นรูปแบบร่วมสำหรับจัดการข้อมูลกึ่งมีโครงสร้างและไม่มีโครงสร้างใน log, observability, การสตรีมข้อมูลแบบเรียลไทม์, ที่เก็บข้อมูลของแอปมือถือ และ pipeline ด้าน machine learning
- ClickHouse เป็น ฐานข้อมูลแบบคอลัมน์อย่างแท้จริง โดยจัดเก็บตารางเป็นชุดไฟล์ข้อมูลคอลัมน์บนดิสก์ เพื่อทำ compression รวมถึง filter และ aggregation แบบ vectorized
- เพื่อให้ JSON ได้ประสิทธิภาพแบบเดียวกัน ไม่ควรเก็บเอกสารไว้ในคอลัมน์สตริงแล้วค่อย parse ภายหลัง แต่ควรเก็บค่าของ พาธ JSON ที่ไม่ซ้ำกันแต่ละพาธ เหมือนเป็นคอลัมน์
ข้อจำกัดสี่อย่างที่ชนิด JSON ใหม่จัดการ
-
การจัดเก็บแบบคอลัมน์รายพาธ
- ค่าตามพาธ JSON ก็ควรถูกบีบอัด และนำไป filter/aggregate แบบ vectorized ได้เหมือนคอลัมน์ทั่วไป เช่น คอลัมน์ชนิดตัวเลข
-
ชนิดข้อมูลที่เปลี่ยนแบบไดนามิก
- ในพาธ JSON เดียวกัน
a อาจมีชนิดต่างกัน เช่น จำนวนเต็ม จำนวนจริง หรืออาร์เรย์
- ClickHouse ไม่สามารถรู้ล่วงหน้าได้ และชนิดเหล่านี้อาจเข้ากันไม่ได้ ดังนั้นการรวมเป็นชนิดร่วมต่ำสุดอาจทำให้ข้อมูลสูญหาย
-
ป้องกันไฟล์คอลัมน์เพิ่มขึ้นแบบระเบิด
- หากสร้างไฟล์คอลัมน์ใหม่สำหรับทุกพาธ JSON ใหม่ จำนวนไฟล์บนดิสก์จะเพิ่มขึ้นอย่างรวดเร็วในข้อมูลที่มีคีย์ไม่ซ้ำกันจำนวนมาก
- file descriptor ใช้หน่วยความจำ และเมื่อมีไฟล์ที่ต้องประมวลผลมากขึ้น ก็ส่งผลต่อประสิทธิภาพการ merge ด้วย
-
การจัดเก็บคีย์แบบ sparse ให้แน่น
- เมื่อมีคีย์ JSON ที่ไม่ซ้ำกันแต่ปรากฏแบบ sparse จำนวนมาก ไม่ควรบันทึก
NULL หรือค่าเริ่มต้นซ้ำในทุกแถวที่ไม่มีค่า
- ต้อง จัดเก็บแบบหนาแน่น เฉพาะค่าที่มีจริง จึงจะขยายไปสู่การวิเคราะห์ระดับ PB ได้
ชนิด Variant: ฐานที่ไม่ฝืนรวมชนิดข้อมูล
- ชนิดข้อมูล Variant เป็นฟีเจอร์อิสระที่ใช้ได้แยกจาก JSON และสามารถจัดเก็บและอ่านค่าชนิดข้อมูลต่างกันภายในคอลัมน์เดียวของตาราง
- คอลัมน์ ClickHouse เดิมมีชนิดตายตัว และค่าที่ insert ต้องเป็นชนิดนั้น หรือถูกแปลงโดยนัย
- คอลัมน์
Nullable ใช้ ไฟล์ NULL mask นอกเหนือจากไฟล์ค่า
Array เก็บขนาดอาร์เรย์ไว้ในไฟล์แยก แล้วใช้สิ่งนี้คำนวณ offset
- คอลัมน์ Variant เก็บค่าที่มีชนิดจริงเหมือนกันไว้ใน ซับคอลัมน์ แยกตามชนิด
- ตัวอย่าง: ค่า
Int64 ทั้งหมดถูกเก็บใน C.Int64.bin และค่า String ทั้งหมดถูกเก็บใน C.String.bin
- แต่ละแถวใช้ชนิดใดจะถูกติดตามด้วยคอลัมน์ discriminator แบบ
UInt8
- ค่า discriminator คือดัชนีของรายการชื่อชนิดที่เรียงลำดับแล้ว
- discriminator
255 เป็นค่าที่สงวนไว้สำหรับ NULL
- ด้วยการออกแบบนี้ Variant จึงมีชนิดจริงได้สูงสุด 255 ชนิด
- ไฟล์ข้อมูลแยกตามชนิดเป็นโครงสร้าง จัดเก็บแบบหนาแน่น ที่เก็บเฉพาะแถวที่มีค่า
- ไฟล์แยกตามชนิดไม่เก็บค่า
NULL
- ใช้คอลัมน์ offset แบบ
UInt64 ในหน่วยความจำ เพื่อหาตำแหน่งแถวในไฟล์ชนิดจริงจากแถว discriminator
- offset นี้ไม่ได้เก็บบนดิสก์ และสามารถสร้างขึ้นทันทีจากไฟล์คอลัมน์ discriminator ได้
- Variant รองรับการซ้อนกันได้ตามอำเภอใจ
- ลำดับชนิดของ
Variant(T1, T2) และ Variant(T2, T1) มีความหมายเหมือนกัน
- สามารถใส่ Variant ซ้อนอยู่ภายใน Variant ได้อีก
- อ่านค่าของชนิดที่ซ้อนอยู่เฉพาะชนิดได้ด้วยการต่อชื่อชนิดเหมือนเป็นซับคอลัมน์
ชนิด Dynamic: จัดเก็บได้แม้ไม่รู้รายการชนิดล่วงหน้า
- ชนิด Dynamic เป็นฟีเจอร์อิสระที่สร้างบน Variant และใช้ได้นอกบริบท JSON เช่นกัน
- Dynamic เพิ่มความสามารถสองอย่างให้ Variant
- เก็บค่าของ ชนิดใดก็ได้ ในคอลัมน์เดียว โดยไม่ต้องระบุรายการชนิดล่วงหน้า
- จำกัดจำนวนชนิดที่จะเก็บเป็นไฟล์ข้อมูลคอลัมน์แยกได้
- โครงสร้างภายในเหมือน Variant แต่มีไฟล์
C.dynamic_structure.bin เพิ่มเข้ามา
- ไฟล์นี้มีรายการชนิดที่ถูกเก็บเป็นซับคอลัมน์ และสถิติขนาดไฟล์ข้อมูลคอลัมน์แยกตามชนิด
- metadata นี้ใช้สำหรับการอ่านซับคอลัมน์และการ merge data part
Dynamic(max_types=N) จำกัดจำนวนชนิดที่จะเก็บเป็นไฟล์แยก
0 <= N < 255
- ค่าเริ่มต้นคือ 32
- เมื่อถึงขีดจำกัด ค่าชนิดอื่นที่เหลือจะถูกเก็บในไฟล์คอลัมน์เดียว เช่น
C.SharedVariant.bin
- ชนิดของไฟล์นี้คือ
String
- แต่ละแถวมีค่าสตริงในรูปแบบ
<binary_encoded_data_type><binary_value>
- สามารถเก็บค่าหลายชนิดไว้ในไฟล์คอลัมน์เดียวแล้วอ่านกลับได้
- Dynamic ก็สามารถใช้ชื่อชนิดเป็นซับคอลัมน์เพื่ออ่านค่าชนิดเฉพาะได้เหมือน Variant
การประกาศชนิด JSON และโครงสร้างการจัดเก็บ
- ชนิด JSON ใหม่ใช้เก็บ object JSON ที่มีโครงสร้างใดก็ได้ และทำให้สามารถอ่านค่า JSON แต่ละค่าด้วยซับคอลัมน์ตามพาธ
- การประกาศชนิดสามารถมีพารามิเตอร์และ hint แบบเลือกได้
<column_name> JSON(
max_dynamic_paths=N,
max_dynamic_types=M,
some.path TypeName,
SKIP path.to.skip,
SKIP REGEXP 'paths_regexp')
max_dynamic_paths
- ค่าเริ่มต้นคือ 1024
- กำหนดจำนวนพาธคีย์ JSON ที่จะเก็บเป็นซับคอลัมน์แยก
- พาธที่เกินขีดจำกัดจะถูกเก็บรวมกันในซับคอลัมน์เดียวที่มีโครงสร้างพิเศษ
max_dynamic_types
- ค่าเริ่มต้นคือ 32
- ช่วงค่าคือ
0 ถึง 254
- กำหนดจำนวนชนิดข้อมูลที่จะเก็บเป็นไฟล์ข้อมูลคอลัมน์แยกภายในคอลัมน์พาธคีย์ JSON หนึ่งพาธ
- ชนิดใหม่ที่เกินขีดจำกัดจะถูกเก็บรวมกันในไฟล์ข้อมูลคอลัมน์เดียวที่มีโครงสร้างพิเศษ
some.path TypeName
- เป็น type hint สำหรับพาธ JSON เฉพาะ
- พาธนั้นจะถูกเก็บเป็นซับคอลัมน์ของชนิดที่ระบุเสมอ เพื่อให้รับประกันประสิทธิภาพ
SKIP path.to.skip
- ข้ามพาธ JSON เฉพาะระหว่างการ parse
- พาธนั้นจะไม่ถูกเก็บในคอลัมน์ JSON
- หากพาธที่ระบุเป็น object JSON ที่ซ้อนกัน object ที่ซ้อนอยู่ทั้งหมดจะถูกข้าม
SKIP REGEXP 'path_regexp'
- ข้ามพาธที่ตรงกับ regular expression ระหว่างการ parse JSON
- พาธที่ตรงกันจะไม่ถูกเก็บในคอลัมน์ JSON
วิธีอ่านพาธ JSON เหมือนคอลัมน์
- ค่า leaf path ที่ไม่ซ้ำกันแต่ละพาธของคอลัมน์ JSON จะถูกเก็บบนดิสก์ด้วยหนึ่งในสองวิธี
- พาธที่มี type hint จะถูกเก็บเป็นไฟล์ข้อมูลคอลัมน์ทั่วไป
- พาธที่ชนิดอาจเปลี่ยนแบบไดนามิกจะถูกเก็บเป็น ซับคอลัมน์ Dynamic
- ชนิด JSON ใช้ไฟล์พิเศษชื่อ
object_structure
- มี metadata สำหรับพาธแบบไดนามิก
- มีสถิติของค่าที่ไม่ใช่ null สำหรับแต่ละพาธแบบไดนามิก
- ใช้สำหรับการอ่านซับคอลัมน์และการ merge data part
- การเพิ่มขึ้นของไฟล์คอลัมน์ถูกควบคุมด้วยขีดจำกัดสองชั้น
max_dynamic_types จำกัดจำนวนชนิดที่จะเก็บเป็นไฟล์แยกภายในพาธคีย์ JSON หนึ่งพาธ
max_dynamic_paths จำกัดจำนวนพาธคีย์ JSON ที่จะเก็บเป็นซับคอลัมน์แยก
- พาธ JSON แบบไดนามิกเพิ่มเติมที่เกินขีดจำกัด
max_dynamic_paths จะถูกเก็บเป็น shared data
- ตัวอย่างไฟล์คือ
C.object_shared_data.size0.bin, C.object_shared_data.paths.bin, C.object_shared_data.values.bin
object_shared_data.values มีชนิดเป็น String
- แต่ละ entry มีโครงสร้าง
<binary_encoded_data_type><binary_value>
- สำหรับ shared data ก็มีการเก็บสถิติเพิ่มเติมใน
object_structure.bin เช่นกัน
- ปัจจุบันจะเก็บสถิติค่าที่ไม่ใช่ null สำหรับพาธ 10000 พาธแรก ในบรรดาพาธที่เก็บอยู่ในคอลัมน์ shared data
ไวยากรณ์พาธ JSON และ object ที่ซ้อนกัน
- ชนิด JSON สามารถอ่านค่า leaf ของแต่ละพาธเป็นซับคอลัมน์ตามชื่อพาธได้
- ค่าของพาธที่ไม่ได้ระบุ type hint จะมีชนิดเป็น Dynamic เสมอ
- ตัวอย่าง: ชนิดของ
C.a.d คือ Dynamic
- ซับคอลัมน์ของชนิดย่อยใน Dynamic อ่านด้วยไวยากรณ์ JSON พิเศษ
- object JSON ที่ซ้อนกันสามารถอ่านเหมือนซับคอลัมน์ชนิด JSON ด้วยไวยากรณ์
JSON_column.^some.path
- ปัจจุบันไวยากรณ์ dot จะไม่อ่าน object ที่ซ้อนกันด้วยเหตุผลด้านประสิทธิภาพ
- โครงสร้างการจัดเก็บปัจจุบันมีประสิทธิภาพสำหรับการอ่านค่าลิเทอรัลรายพาธ
- หากต้องการอ่าน object ย่อยทั้งก้อนตามพาธ จะต้องอ่านข้อมูลมากขึ้นและอาจช้าลง
- การคืนค่า object ต้องใช้ไวยากรณ์
.^
- ClickHouse วางแผนจะรวมไวยากรณ์
. สองแบบเข้าด้วยกัน
การ serialize discriminator แบบ compact
- พาธ JSON แบบไดนามิกจำนวนมากอาจมีชนิดค่าที่ส่วนใหญ่เหมือนกัน
- หากมีพาธ JSON ที่ไม่ซ้ำกันแต่ sparse จำนวนมาก ไฟล์ discriminator ของแต่ละพาธจะมีค่า
255 หรือ NULL เป็นหลัก
- ไฟล์ลักษณะนี้บีบอัดได้ดี แต่หากค่าของทุกแถวเหมือนกัน ก็ยังมีข้อมูลซ้ำจำนวนมากอยู่ดี
- ClickHouse จึง implement compact format สำหรับการ serialize discriminator
- โดยทั่วไป แทนที่จะเขียนค่า discriminator แบบ
UInt8 ทั้งหมด หาก discriminator ของ granule เป้าหมายเหมือนกันทั้งหมด จะ serialize เพียง 3 ค่า
- ตัวระบุ compact granule format
- ตัวระบุจำนวนค่าของ granule นั้น
- ค่า discriminator
- การ optimize นี้ควบคุมด้วยการตั้งค่า MergeTree
use_compact_variant_discriminators_serialization
- ค่าเริ่มต้นคือเปิดใช้งาน
- จึงอาจมีกรณีที่เก็บเพียง 3 ค่า แทน 8192 ค่า ตาม index granularity ทั่วไป
สถานะการปล่อยและขั้นตอนถัดไป
- ชนิด JSON ใหม่ถูกออกแบบมาเพื่อแทนที่ชนิด Object('json') ที่ถูก deprecated แล้ว
- การ implement นี้มีให้ใช้งานเป็นฟีเจอร์ experimental สำหรับการทดสอบใน ClickHouse 24.08 release
- JSON roadmap มีการปรับปรุงสำหรับการใช้พาธคีย์ JSON ภายใน primary key ของตารางหรือ data-skipping index
- ส่วนประกอบอย่าง Variant และ Dynamic จะเป็นฐานสำหรับรองรับชนิดกึ่งมีโครงสร้างเพิ่มเติม เช่น XML และ YAML นอกเหนือจาก JSON
- ผู้ใช้ ClickHouse Cloud ที่ต้องการทดสอบชนิดข้อมูล JSON ใหม่ควรติดต่อ ทีมสนับสนุน ClickHouse เพื่อขอสิทธิ์เข้าถึง private preview
ยังไม่มีความคิดเห็น