โหลดข้อมูลสภาพอากาศ 1 ล้านล้านแถวเข้าสู่ TimescaleDB
(aliramadhan.me)- หากต้องการวิเคราะห์การเปลี่ยนแปลงสภาพอากาศในอดีตทั่วโลกได้อย่างรวดเร็ว จำเป็นต้องนำข้อมูลรีอะนาไลซิส ERA5 เข้าไปไว้ใน PostgreSQL/TimescaleDB และเมื่ออิงตามตัวแปรและกริดที่เลือก จะมีขนาดประมาณ 754 พันล้านแถว
- ERA5 ให้ข้อมูลตั้งแต่ปี 1940 ด้วยความละเอียดรายชั่วโมงและกริดละติจูด-ลองจิจูด 0.25 องศา แต่ในโครงสร้าง NetCDF การ ดึง time series ระยะยาวของจุดเดียวก็อาจใช้เวลา 20–30 นาที
insertแบบแถวเดียวทำได้ราว 3 พันแถวต่อวินาที จึงต้องใช้เวลาประมาณ 8 ปีในการโหลดทั้งหมด ส่วนinsertหลายแถวตามเกณฑ์ของ psycopg3 ทำได้ 25,000–30,000 แถวต่อวินาที หรือราว 10 เดือน- PostgreSQL
copyและ psycopg3cursor.copy()มีความต่างกันตามว่ามีการเตรียม CSV ไว้แล้วหรือไม่ โดยเมื่อรวมโอเวอร์เฮดแล้วจะอยู่ที่ราว 100,000 แถวต่อวินาที และยังดันต่อได้อีกด้วยการรันแบบขนานและการปรับตั้งค่า - หากใส่ข้อมูลจากดาต้าเฟรมโดยตรง ควรใช้ copy ด้วย psycopg3 ลง hypertable โดยตรง แต่ถ้ามีไฟล์ CSV อยู่แล้ว
timescaledb-parallel-copyจะเหมาะกว่า และในระบบนี้ 12–16 workers ดูเป็นจุดที่เหมาะสมสำหรับการทำงานแบบขนาน
สร้างคลังข้อมูลสภาพอากาศทั่วโลก
- เป้าหมายคือสร้างคลังข้อมูลสำหรับ query ข้อมูลสภาพอากาศในอดีตทั่วโลก เพื่อวิเคราะห์ สัญญาณการเปลี่ยนแปลงสภาพภูมิอากาศ ที่เกิดขึ้นแล้ว
- ตัวอย่างการวิเคราะห์ เช่น จาการ์ตาร้อนขึ้นจริงหรือพายุถี่ขึ้นหรือไม่, ทั้งประเทศชิลีอุ่นขึ้นหรือมีเมฆมากขึ้นหรือไม่, และการเปลี่ยนแปลงในแต่ละภูมิภาคมีลักษณะอย่างไร
- หากต้องการวิเคราะห์ระดับโลกได้เร็ว query ของคลังข้อมูลก็ต้องเร็วด้วย และขั้นตอนแรกคือการโหลดข้อมูลจำนวนมหาศาลเข้าสู่ฐานข้อมูล
- ระบบนี้ใช้ PostgreSQL เป็นฐาน โดยมี TimescaleDB เป็นตัวเลือกที่น่าสนใจสำหรับเร่ง query ตามเวลา และในอนาคต PostGIS ก็น่าสนใจสำหรับเร่ง query เชิงภูมิสารสนเทศ
ข้อมูลรีอะนาไลซิส ERA5 และขนาด 754 พันล้านแถว
- ใช้ข้อมูล ERA5 climate reanalysis แทนค่าการสังเกตสภาพอากาศจริง
- ข้อมูลสังเกตย้อนหลังอาจมีน้อยในบางพื้นที่และบางช่วงเวลา
- ERA5 คือผลลัพธ์จากแบบจำลองสภาพภูมิอากาศที่ถูกจำกัดให้สอดคล้องกับข้อมูลสังเกต และถูกใช้อย่างกว้างขวางในงานวิจัยด้านอุตุนิยมวิทยาและภูมิอากาศ
- ERA5 ครอบคลุมทั้งโลกที่ความละเอียด 0.25 องศา และให้ข้อมูลรายชั่วโมงตั้งแต่ปี 1940
- สแนปช็อตต่อเวลามี 727,080 ค่าในแต่ละตัวแปร
- จุดกริดมี 1,038,240 จุด ประกอบด้วยลองจิจูด 1,440 ค่า และละติจูด 721 ค่า รวมขั้วโลกด้วย
- เมื่อนำมาทำดัชนีตามเวลาและตำแหน่ง จะได้ 753,836,544,000 แถวต่อหนึ่งตัวแปร หรือราว 754 พันล้านแถว
- ตัวแปรที่จะโหลด ได้แก่ อุณหภูมิ, ความเร็วลม 10 เมตรในแนวตะวันออก-ตะวันตกและเหนือ-ใต้, ปริมาณเมฆรวม, ปริมาณฝน และปริมาณหิมะสะสม
- ในตารางจะมีคอลัมน์
time,location_id,latitude,longitudeและคอลัมน์ของตัวแปรสภาพอากาศแต่ละตัว- ที่เก็บทั้ง
location_idและคอลัมน์ละติจูด-ลองจิจูดไว้พร้อมกัน เพราะต้องการใช้สำหรับ benchmark query และดัชนีในภายหลัง
- ที่เก็บทั้ง
จุดที่โครงสร้างไฟล์ NetCDF ทำให้ช้าลง
- ERA5 ถูกแจกจ่ายเป็นไฟล์ NetCDF โดยทั่วไปข้อมูลจะถูกเก็บแยกเป็นไฟล์รายวัน รายเดือน หรือรายปี
- โครงสร้างแบบ chunk ตามเวลาเหมาะกับการเรียกดูข้อมูล ณ ช่วงเวลาหนึ่ง เพราะเร็วและเรียบง่าย
- แต่ถ้าต้องการดูรูปแบบตามเวลา เช่น time series ระยะยาวของจุดใดจุดหนึ่ง จะต้องอ่านหลายไฟล์มาก จึงช้า
- ตัวอย่างเช่น การดึง time series อุณหภูมิของตำแหน่งหนึ่งอาจใช้เวลา 20–30 นาที
- query เชิงภูมิสารสนเทศที่ซับซ้อน โดยเฉพาะ query ที่มีแกนเวลา มักช้าและทำได้ยาก
- เครื่องมืออย่าง xarray, dask, Pangeo ช่วยเพิ่มความเร็วได้ แต่กระบวนการก็ยังช้าอยู่ดี
insert: จากแถวเดียวไปสู่หลายแถว
- วิธีที่ง่ายที่สุดคือใช้
insertเพื่อใส่ทีละแถว insertแบบแถวเดียวมีต้นทุนหลายอย่าง- PostgreSQL ต้อง parse คำสั่ง ตรวจสอบชื่อตารางและคอลัมน์ และสร้าง execution plan
- อาจต้องมี lock เพื่อรักษาความถูกต้องของข้อมูล
- ต้องเขียนข้อมูลลงบัฟเฟอร์สำหรับ WAL (write-ahead logging)
- ต้องแทรกข้อมูลลงพื้นที่ดิสก์ของตารางจริง
- เมื่อ commit ธุรกรรม การเปลี่ยนแปลงจึงจะถูกทำให้ถาวร
- ใน Python มีการ benchmark 3 วิธีสำหรับการแทรกแถวเดียว
- ใช้ pandas
df.to_sql()พร้อมchunksize=1 - ใช้ parameterized query ของ psycopg3
- ใช้ parameterized query ของ SQLAlchemy
- ใช้ pandas
- ผลการแทรกแถวเดียวพบว่า psycopg3 เร็วกว่าเล็กน้อย และ SQLAlchemy ช้าที่สุด
- hypertable ของ TimescaleDB ช้ากว่าตาราง PostgreSQL ปกติเล็กน้อย
- ประสิทธิภาพสูงสุดอยู่ที่ราว 3 พันแถวต่อวินาที ซึ่งหมายความว่าต้องใช้เวลาประมาณ 8 ปีในการใส่ข้อมูลทั้งหมด
insertแบบหลายแถวจะใส่หลายแถวในคำสั่งเดียว เพื่อลดต้นทุนจาก network round-trip, การ parse และการวางแผน- psycopg3 เร็วที่สุดที่ 25,000–30,000 แถวต่อวินาที
- pandas อาจช้ากว่าเพราะโครงสร้างการแทรกใช้ dictionary แทน tuple
- SQLAlchemy อาจมีโอเวอร์เฮดเพิ่มจากการจัดการ session และ abstraction ของ SQL expression
- ถึงอย่างนั้นการโหลดทั้งหมดก็ยังต้องใช้เวลาราว 0.8 ปี หรือเกือบ 10 เดือน
copy: เส้นทางโหลดข้อมูลจำนวนมากของ PostgreSQL
- PostgreSQL
copyเป็นความสามารถสำหรับโหลดข้อมูลจำนวนมากโดยอ่านแถวจากไฟล์ CSV หรือไบนารี - เพราะออกแบบมาสำหรับการโหลดข้อมูลจำนวนมาก จึงปรับ parse, การวางแผน และการใช้ WAL ให้เหมาะสม ทำให้เร็วกว่าการ
insertหลายแถว - มีการเปรียบเทียบ 2 เส้นทาง
- บันทึกข้อมูล NetCDF ออกมาเป็น CSV แล้วค่อยโหลดด้วย
copy - ไม่สร้างไฟล์ CSV แต่สตรีมตรงเข้า PostgreSQL ด้วย psycopg3
cursor.copy()
- บันทึกข้อมูล NetCDF ออกมาเป็น CSV แล้วค่อยโหลดด้วย
- หากมีไฟล์ CSV เตรียมไว้อยู่แล้ว
copyจะทำความเร็วการแทรกได้เกือบ 400,000 แถวต่อวินาที - แต่เมื่อรวมโอเวอร์เฮดจากการเขียนไฟล์ CSV หรือการสร้าง tuple แล้ว ทั้ง
copyและ psycopg3 จะอยู่ที่ประมาณ 100,000 แถวต่อวินาที โดย psycopg3 เร็วกว่าเล็กน้อย - แม้ที่ความเร็วนี้ การโหลดข้อมูลทั้งหมดก็ยังต้องใช้เวลาประมาณ 3 เดือน
อัตราการโหลดต่อเนื่องและ copy แบบขนาน
- เมื่อใส่ข้อมูลจำนวนมาก จะเกิดคอขวดได้จากการเขียนดิสก์, การแย่ง I/O ระหว่าง WAL กับการแทรกลงตาราง, autovacuum และ checkpoint
- ในการทดลองใส่ข้อมูลราว 772 ล้านแถวเป็น 744 ชุดงาน ไม่พบการชะลอตัวหนักสำหรับกรณี worker เดียว
copy csvมีการตกลงของความเร็วบ่อยกว่าและไวต่อความผันผวนมากกว่า- psycopg3 โดยรวมเร็วกว่า
- ความต่างระหว่างตารางปกติกับ hypertable ไม่มาก
- ใช้ joblib เพื่อรันงาน
copyหลายชุดหรือรัน psycopg3 cursor หลายตัวแบบขนาน - การแทรกลงตารางเดียวไม่ใช่งานที่ขยายแบบขนานได้ดีนัก และประสิทธิภาพมัก เริ่มตันหลัง 16 workers
pg_bulkload และ timescaledb-parallel-copy
- นอกจาก PostgreSQL
copyแล้ว ยัง benchmark pg_bulkload และ timescaledb-parallel-copy ด้วย - pg_bulkload ดูเหมือนจะเร็วกว่าในการตั้งค่าปริยาย แต่ค่าเริ่มต้นของมันจะข้าม shared buffers และข้าม WAL logging ทำให้หลังเกิด crash อาจกู้ข้อมูลไม่ได้
- เมื่อทดสอบภายใต้เงื่อนไขเดียวกันโดยปิด
fsyncพบว่า timescaledb-parallel-copy ที่ใช้หลาย workers ทำได้ดีกว่า pg_bulkload - timescaledb-parallel-copy สามารถระบุจำนวน workers เพื่อแทรกแบบขนานได้
- ช่วงแรกประสิทธิภาพดี แต่ในระบบนี้จะชนคอขวดก่อนถึง 100 ล้านแถว ทำให้อัตราการแทรกลดฮวบแล้วค่อยฟื้นกลับมาเป็นคลื่น
- อัตราการโหลดต่อเนื่องอยู่ที่ประมาณ 600,000–700,000 แถวต่อวินาทีสำหรับตารางปกติ และราว 300,000 แถวต่อวินาทีสำหรับ hypertable
- pg_bulkload แม้จะไม่กำหนดจำนวน workers โดยตรง แต่มีตัวเลือก
writer=parallelเพื่อใช้หลายเธรดกับการอ่าน การ parse และการเขียน
การปรับตั้งค่า PostgreSQL และการแลกความทนทาน
- ประสิทธิภาพเพิ่มเติมสามารถได้มาจากการปรับ การตั้งค่าที่ไม่รับประกันความทนทาน ของ PostgreSQL
- ค่าหลักคือการปิด
fsyncเพื่อลดการ flush ลงดิสก์ และปิดfull_page_writesเพื่อลดการป้องกัน partial page write - การตั้งค่าเหล่านี้อาจทำให้ความสมบูรณ์ของฐานข้อมูลตกอยู่ในความเสี่ยงหากเกิด crash
- unlogged table ไม่สร้าง WAL จึงเขียนได้เร็วกว่า แต่หลังการกู้คืนจาก crash อาจถูกตัดข้อมูลทิ้งได้
- หลังจากนั้นต้องแปลงกลับเป็น logged table ปกติ ซึ่งกระบวนการนี้ช้าและอาจทำงานแบบเธรดเดียว
- hypertable ไม่สามารถเป็น unlogged ได้ ดังนั้นหากต้องการ hypertable จะต้องมีขั้นตอนแปลงหรือย้ายข้อมูลเพิ่ม
ตัวเลือกสุดท้าย: ใส่ลง hypertable โดยตรง
- หากเป้าหมายคือ hypertable การใส่ข้อมูลลง hypertable โดยตรงจะเร็วกว่าวิธีใส่ลงตารางปกติก่อนแล้วค่อยแปลงเป็น hypertable
- จากการทดสอบแบบง่ายด้วยข้อมูลราว 772 ล้านแถว โดยใช้ psycopg3
copyและ 16 workers การแทรกลง hypertable โดยตรงใช้เวลาน้อยกว่าการแทรกลงตารางปกติแล้วค่อยแปลง- ในกรณีนี้ การแทรกลง hypertable โดยตรงใช้เวลาประมาณ 80% ของอีกวิธี
- กระบวนการแปลงและ migration ไม่ได้เร็ว และดูเหมือนทำงานแบบเธรดเดียว
- วิธีที่แนะนำมีดังนี้
- หากโหลดจากดาต้าเฟรมโดยตรง ให้ใช้ psycopg3
copyลง hypertable โดยตรง - หากมีไฟล์ CSV อยู่แล้ว ให้ใช้
timescaledb-parallel-copy - สำหรับการทำงานแบบขนาน ในระบบนี้ 12–16 workers ดูเป็นจุดที่เหมาะสม
- หากโหลดจากดาต้าเฟรมโดยตรง ให้ใช้ psycopg3
สรุปผล benchmark และเวลาที่ต้องใช้ทั้งหมด
- ในกรณี worker เดียวและเปิดการตั้งค่าป้องกันข้อมูลไว้ อัตราการโหลดต่อเนื่องสูงสุดเมื่อรวมโอเวอร์เฮดบนฮาร์ดแวร์นี้อยู่ที่ประมาณ 140,000 แถวต่อวินาที
- หากใช้หลาย workers จะสามารถดันอัตราการโหลดต่อเนื่องด้วย psycopg3
copycursor ไปได้ถึงราว 250,000 แถวต่อวินาที โดยยังคงการตั้งค่าป้องกันข้อมูลไว้ - กระบวนการแทรกไม่ได้ขยายแบบขนานได้มากนัก และช่วงที่เหมาะสมอยู่ที่ 4–16 workers
- หากยอมรับความเสี่ยงและปิด
fsyncจะสามารถทำต่อเนื่องได้ราว 462,000 แถวต่อวินาที ด้วย psycopg3 - pg_bulkload ปิด
fsyncโดยปริยาย จึงต้องใช้อย่างระมัดระวัง - หากทำได้ต่อเนื่องที่ประมาณ 462,000 แถวต่อวินาที การโหลดข้อมูลราว 754 พันล้านแถวจะใช้เวลาประมาณ 20 วัน
โค้ดและสภาพแวดล้อม benchmark
- โค้ดสำหรับดาวน์โหลด ERA5, สร้างตาราง, แทรกข้อมูลและ
copy, ทำ benchmark และสร้างกราฟ อยู่ที่ timescaledb-insert-benchmarks - ในแต่ละ benchmark จะเปิด Docker container ใหม่เพื่อคงสภาพแวดล้อมให้สม่ำเสมอ
- ไม่มีการคง storage ข้ามระหว่าง Docker container
- อ่านไฟล์ NetCDF และ CSV จาก HDD
- ฐานข้อมูลเก็บไว้บน NVMe SSD
- สเปกฮาร์ดแวร์
- CPU: 2x 12-core Intel Xeon Silver 4214
- RAM: 16x 16 GiB Samsung ECC DDR4 2666 MT/s
- SSD: Intel 2 TB NVMe
- HDD: Seagate Exos X16 14TB 7200 RPM
- ซอฟต์แวร์ที่ใช้
- Ubuntu 20.04, Linux kernel 5.15
- PostgreSQL 15.5
- TimescaleDB 2.13.0
- pg_bulkload 3.1.20
- การตั้งค่า PostgreSQL ใช้ค่าที่
timescaledb-tuneแนะนำสำหรับหน่วยความจำ 250.57GB และ CPU 48 ตัว และในการ benchmark มีการปรับขนาด WAL แยกต่างหาก
2 ความคิดเห็น
ความเห็นของ GN+ เป็นภาษาสุภาพเหรอครับ..?
ความคิดเห็นบน Hacker News
เคยทำงานด้านการวิเคราะห์เชิงภูมิสารสนเทศมาพอสมควร และข้อมูลภูมิสารสนเทศมีความละเอียดอ่อนกว่าที่คิดมาก เพราะต้องเข้าใจทั้งระบบพิกัดอ้างอิง (CRS) และการฉายภาพเพื่อการแสดงผลให้ถูกต้อง
ถ้าไม่มีโครงสร้างพื้นฐานที่พาเมทาดาทาไปพร้อมกับข้อมูลอย่างเหมาะสม ก็จะรู้สึกไม่มั่นใจกับเรื่อง CRS อยู่ตลอด
เคยใช้มาหมดแล้วทั้งฟีเจอร์ที่เกี่ยวข้องของ AWS, Postgres/PostGIS, Spark/Databricks, Snowflake, Trino และ ArcGIS แต่สำหรับงานภูมิสารสนเทศขนาดใหญ่ มองว่า Google BigQuery เหนือกว่าชัดเจน
คิวรีที่เคยใช้เวลาหลายชั่วโมงและมีค่าใช้จ่ายสูงบน PostGIS ที่รันบน EC2 m6a ขนาดใหญ่ กลับใช้เวลาไม่ถึง 5 วินาทีบน BigQuery free tier
ตอนนั้นใช้ข้อมูลสาธารณะของ FEMA โดย Snowflake และบริการของ AWS ติดปัญหาเพราะคอลัมน์ geometry มีขนาดเกินไบต์สูงสุดที่รองรับ, Spark ก็ไม่มีชนิดข้อมูลภูมิสารสนเทศ และส่วนขยายโอเพนซอร์สก็ยังน่าผิดหวัง
ถ้าเป็น on-premises สถานการณ์อาจต่างออกไป แต่ที่ขนาด 20TB ค่าเก็บข้อมูลของ BigQuery ก็น่าจะต่ำกว่า $100 ต่อเดือนอยู่ดี เลยไม่ค่อยอยากดูแลระบบเอง
ตอนแรกใช้ดัชนี H3 และเพราะชุดข้อมูลระหว่างทางทั้งหมดขึ้นมาอยู่ในหน่วยความจำได้ จึงทำแบบนั้นได้
สแตกที่ใช้คือ 128GB Mac Studio + Asahi Linux + ไฟล์ parquet แบบ mmap + DuckDB และยังใช้รัน Airflow รวมถึงใช้ Nix เพื่อเร่งบิลด์สำหรับนักพัฒนาและรันงาน Airflow ของทีมข้อมูลด้วย
GCP นั้นดีมากตอนที่ยังฟรีหรือราคาถูก แต่ถึงการใช้งานจะไม่ได้เพิ่มขึ้น สักวันหนึ่งก็อาจทำให้ตกใจกับบิลที่ใหญ่ขึ้นได้
เคยลองเทียบกับ Apache Sedona ไหม และมีอะไรที่รู้สึกว่ายังขาดไปบ้าง
ทำงานอยู่ที่ Wherobots ซึ่งก่อตั้งโดยผู้สร้าง Apache Sedona เลยอยากฟังฟีดแบ็ก
https://sedona.apache.org/latest/
https://wherobots.com/
เห็นด้วยว่าค่าที่กว้างมากอาจทำให้หลายเครื่องมือพังได้ แต่คิดว่าระบบแบบ Postgres เชิงคอลัมน์ตัวอื่น ๆ น่าจะรองรับได้โดยไม่มีปัญหา
แปลกใจที่เหมือนไม่ได้ลอง Redshift ซึ่งแข่งกับ BigQuery โดยตรง และคอลัมน์
superของ Redshift ก็รองรับขนาดได้ใหญ่กว่าค่าสูงสุดของ BigQuery เสียอีกมักเห็นผู้คนค่อย ๆ เรียนรู้ด้วยความยากลำบากว่าเอาชนะ PostGIS ได้ยากแค่ไหน
การที่ Trino/Presto และ Spark แทบไม่คืบหน้าในด้านนี้ก็ชวนให้คิดมากเป็นพิเศษ
บน Postgres มีการแคชบางส่วนของตารางใหญ่ แต่บน BigQuery สร้างใหม่ตั้งแต่ต้นทั้งหมดก็ยังได้ผลแบบนั้น
สำหรับเครื่องมือที่ดูแลเองได้ยังชอบ Postgres มากกว่า แต่พอประสิทธิภาพดีขึ้นเกินระดับหลายเท่าตัวแบบเลขหลักเดียว ก็เถียงได้ยากจริง ๆ
เป็นบทความที่ยอดเยี่ยมมาก
ผมดูแลงาน DevRel ที่ Timescale และชอบมากเวลาได้เห็นบทความจากคอมมูนิตี้ที่เขียนออกมาดีแบบนี้
หนึ่งในเหตุผลที่ hypertable ช้ากว่า น่าจะเป็นเพราะมันสร้างดัชนีบนคอลัมน์ timestamp ให้โดยอัตโนมัติเกือบแน่นอน
ส่วนตารางปกติไม่มีดัชนี จึงอาจเร็วกว่า
ใน
create_hypertableสามารถใช้create_default_indexes=>falseเพื่อข้ามการสร้างดัชนีได้ หรือจะลบดัชนีก่อนใส่ข้อมูลก็ได้สุดท้ายยังไงก็น่าจะต้องใช้ดัชนีนั้นอยู่ดี แต่สำหรับการโหลดข้อมูลแบบเป็นชุด การค่อยสร้างทีเดียวหลังโหลดเสร็จน่าจะดีกว่า
แล้วก็สงสัยเหมือนกันว่า HDD ที่ใช้อ่านข้อมูลจะรับมือไหวแค่ไหนในระบบที่มี parallelism สูง
create_default_indexes=>falseและ hypertable จะสร้าง ดัชนีเวลา โดยอัตโนมัติ จะเพิ่มหมายเหตุอธิบายส่วนนี้เข้าไปอยากลองทำเบนช์มาร์กแบบแทรกข้อมูลโดยไม่มีดัชนีเวลา แล้วค่อยสร้างดัชนีเองภายหลังด้วย
แม้จะมี worker 32 ตัว HDD ก็ดูเหมือนจะรับไหว
ตอนดูการใช้งานดิสก์ด้วย btop กลับรู้สึกว่า SSD ที่ติดตั้ง Postgres อยู่ใกล้จะเป็นคอขวดมากกว่า HDD เลยสรุปว่าการอัปเกรด SSD สำหรับ Postgres ให้เร็วขึ้นน่าจะคุ้มกว่าย้ายข้อมูลจาก HDD ไป SSD
ไม่ค่อยเข้าใจว่าทำไมต้องทำแบบนั้นด้วย
ชุดข้อมูลสภาพอากาศ·ภูมิอากาศส่วนใหญ่รวมถึง ERA5 มีโครงสร้างสูงมากอยู่บนกริดละติจูด-ลองจิจูดที่สม่ำเสมอ
แม้จะดึงออกมาแค่อนุกรมเวลาของตำแหน่งใดตำแหน่งหนึ่ง จุดแข็งของชุดข้อมูลแบบนี้ก็อยู่ที่โครงสร้างและบริบทเชิงพื้นที่-เวลาที่มีอยู่ในตัว และถ้าไม่ได้มีเป้าหมายแค่สกัดอนุกรมเวลารายจุด การทำลายโครงสร้างนั้นทิ้งทั้งหมดก็ดูไม่ค่อยสมเหตุสมผล
ต่อให้เป็นกรณีที่ดึงเฉพาะอนุกรมเวลารายจุดจริง ๆ เช่น อนุกรมเวลาอุณหภูมิผิวน้ำกลางมหาสมุทร ก็มักแทบไม่มีโอกาสได้ใช้งาน ดังนั้นคงอยากลดข้อมูลลงอย่างค่อนข้างหนัก
การใช้งานชุดข้อมูลอย่าง ERA5 ทั้งด้านวิจัยและงานปฏิบัติการส่วนใหญ่ ดูเหมือนจะเหมาะกับการใช้สำเนาที่ปรับให้เหมาะกับคลาวด์และยังคงโครงสร้างต้นฉบับไว้ เช่น ARCO-ERA5 ของ Google Public Datasets มากกว่า
เวอร์ชันแบบนี้ยังรักษาโครงสร้างต้นฉบับไว้ ขณะเดียวกันก็แบ่งชังก์ให้เหมาะกับการเข้าถึงแบบขนานขนาดใหญ่บนคลาวด์สตอเรจ
แทบทุกกรณีที่ผมเคยเห็นมาในสายอาชีพ แค่อาร์ไคฟ์ที่ทำชังก์แบบทั่วไปบนฐานของ Zarr ก็เร็วพอแล้วสำหรับการใช้งานส่วนใหญ่ที่น่าสนใจ
https://cloud.google.com/storage/docs/public-datasets/era5
แต่ก็อย่างที่ rabernat พูดไว้ การดึงอนุกรมเวลายาว ๆ จากสำเนาบนคลาวด์ก็ยังช้า
สุดท้ายแล้วผมก็อยากทำคิวรีเชิงพื้นที่-เวลาที่ซับซ้อนด้วย เช่น คำนวณเปอร์เซ็นไทล์ที่ 99 ของอุณหภูมิฤดูร้อนในชิลีช่วงปี 1940~1980
ผมไม่สงสัยเลยว่าสำเนาบนคลาวด์อาจเร็วกว่า แต่ก็ขัดกับงบประมาณ $0
มันถูกปรับให้เหมาะกับคิวรีเชิงพื้นที่ และไม่ได้ปรับให้เหมาะกับคิวรีอนุกรมเวลา
ผมเพิ่งรันเบนช์มาร์กไป และใช้เวลา 20 นาทีในการดึงอนุกรมเวลาของตัวแปรเดียว ณ จุดเดียว
มันแสดงให้เห็นชัดเจนว่าถ้าแพตเทิร์นการใช้งานที่คาดไว้คืออนุกรมเวลา ก็จำเป็นต้องทำชังก์ให้เหมาะกับอนุกรมเวลา
ห้องแล็บบางแห่งมีไปป์ไลน์ที่อิง RDBMS ซึ่งผูกพันกับอัลกอริทึมและข้อมูลที่เปิดเผยอยู่แล้ว ไม่มีใครอยากมาเขียนใหม่ และก็ไม่มีงบสำหรับงานนั้น
การปรับปรุงที่ดีที่สุดที่เราได้มาก็คือย้ายจาก MySQL รุ่นเก่าไปเป็น Postgres + PostGIS
Timescale ก็น่าจะช่วยได้เหมือนกัน
ยังมีเหตุผลด้วยว่าทำไมต้องรันในเครื่อง เช่น เรื่องความเป็นส่วนตัว การเข้าถึงคลัสเตอร์ และงบประมาณ
เป็นบทความที่ดี
สิ่งที่ขาดไปตรงนี้ ในความเห็นผม คือการวิเคราะห์ว่าการย้ายข้อมูลอากาศไปไว้ในฐานข้อมูลเชิงสัมพันธ์จะได้อะไรกลับมา
แรงจูงใจคือทำให้คิวรีเร็วขึ้น ดังนั้นจึงต้องรู้ว่าค่าฐานเปรียบเทียบคืออะไร
ในฐานะผู้ดูแล Xarray และ Zarr และผู้ก่อตั้ง https://earthmover.io/ ผมค่อนข้างคุ้นเคยกับภูมิทัศน์เทคโนโลยีนี้ และถ้าทำชังก์ข้อมูลใน Zarr อย่างเหมาะสม ก็สามารถรองรับคิวรีอนุกรมเวลาของข้อมูลอากาศด้วยโซลูชันแบบ serverless + object storage เพียงอย่างเดียวได้ที่ latency ต่ำกว่า 1 วินาที
ซึ่งเร็วกว่าที่บทความบอกไว้ว่า 30 นาทีมาก
เมื่อคิดถึงความยากของการนำเข้าข้อมูลตามที่กล่าวในบทความ ก็คุ้มที่จะประเมินแนวทางแบบนั้นอย่างจริงจังก่อนจะไปสาย RDBMS
จะเอา API มาครอบด้านหน้าจนดูเหมือนเป็นคิวรีก็ได้
และก็จริงด้วยว่าผมยังให้เหตุผลสนับสนุนแนวทาง RDBMS ได้ไม่พอ
เหตุผลหลักที่ผมไปทาง Postgres + Timescale คืออยากเรียนรู้มัน และการเล่นกับข้อมูล ERA5 ก็ดูน่าสนุกที่สุด
ขนาดของข้อมูลอากาศที่ใหญ่พอจะเป็นความท้าทายก็ดูจะมีเสน่ห์อยู่เหมือนกัน
ผมไม่มีหลักฐาน แต่ก็สงสัยว่า TimescaleDB + PostGIS ที่จูนและทำดัชนีอย่างดี จะช่วยกับคิวรีเชิงพื้นที่-เวลาที่ซับซ้อนอย่างเปอร์เซ็นไทล์ที่ 99 ของอุณหภูมิฤดูร้อนในชิลีช่วงปี 1940~1980 ได้ไหม
เพราะในกรณีนี้อาจต้องอ่าน Zarr หลายชังก์
ผมก็ชอบไอเดียการมีตารางแยกไว้แคชสถิติพวกนี้ แต่กับ Zarr เองก็ดูไม่ได้ทำยากขนาดนั้น
ครั้งหน้าผมตั้งใจจะเบนช์มาร์กคิวรีและดัชนี ก็น่าจะได้รู้อะไรมากขึ้น
ดูเหมือนว่าจะมีความตึงเครียดเชิงพื้นฐานที่ว่า ชังก์ใหญ่เหมาะกับการแสดงภาพพื้นที่กว้างและคิวรีขนาดใหญ่ ส่วนชังก์เล็กเหมาะกับคิวรีแบบอิงจุดหรืออนุกรมเวลา
การเก็บชุดข้อมูลภูมิสารสนเทศขนาดใหญ่แบบนี้แยกเป็นหลายเวอร์ชันที่ทำชังก์ต่างกันก็พอทำได้ แต่ไม่ค่อยคุ้มค่าใช้จ่าย
เคยได้ยินว่า
kerchunkถูกใช้เพื่อพยายามเอาข้อดีของทั้งสองแบบ แต่ผมรู้สึกว่ามันทำให้เสียทางเลือกเรื่องการบีบอัดข้อมูลไป และยังเพิ่มความซับซ้อนพอสมควรผมสงสัยว่าควรหาจุดสมดุลระหว่างหลายกรณีการใช้งานแบบนี้อย่างไรดี
ฉันเคยมีส่วนร่วมกับ เอกสารการโหลดข้อมูลจำนวนมากของ Postgres ที่อ้างถึงตรงนี้ ซึ่งเป็นบทความที่รวบรวมเทคนิคหลายอย่างไว้ได้ดี
ฉันเคยทำงานแนวนี้อยู่พอสมควรเพื่อเร่งการโหลดฐานข้อมูล OpenStreetMap และการอัปเดตสาธารณะครั้งสุดท้ายอยู่ที่ https://www.youtube.com/watch?v=BCMnu7xay2Y
หลังจากนั้น ด้วยพัฒนาการของฮาร์ดแวร์, การปรับปรุง GIS ใน PG15 และการที่ osm2pgsql นำเทคนิค middle-way-node-index-id-shift มาใช้ ทำให้เวลาโหลดชุดข้อมูล planet ลดลงมาต่ำกว่า 4 ชั่วโมง
ถ้าจะเสนอแนะผู้เขียน การทดลองบางส่วนกำลังกำจัดการเขียน WAL ทางอ้อมผ่าน pg_bulkload และ COPY
ตอนที่ Craig Ringer เขียนโพสต์ SO ที่ลิงก์ไว้นั้นยังไม่มีการบันทึกไว้ในเอกสาร แต่จริง ๆ แล้ว สามารถปิด WAL ได้เลย ในการตั้งค่า
แน่นอนว่าถ้าเกิด crash ก็จะเสียตารางที่กำลังทำอยู่ และถ้างานกินเวลาหลายสัปดาห์ เรื่องแบบนั้นก็อาจเกิดขึ้นได้
แต่สำหรับข้อมูลอนุกรมเวลา ถ้าออกแบบโครงสร้างการโหลดดี ๆ ก็ไม่ยากที่จะทำให้เสียแค่ chunk สุดท้าย
สำหรับการโหลดข้อมูลจำนวนมาก จริง ๆ แล้วแทบไม่จำเป็นต้องมีข้อมูล WAL
ถ้า crash ก็แค่เก็บกวาดด้านขวาสุดของข้อมูลที่โหลดไว้แล้ว แล้วเริ่มใหม่
ค่าตั้งใน
postgresql.confที่ใช้เพื่อปิด WAL และ overhead อื่น ๆ มีดังนี้:wal_level = minimalmax_wal_senders = 0synchronous_commit = offfsync = offfull_page_writes = offautovacuum = offcheckpoint_timeout = 60minสุดท้าย เวลาจะโหลดเป็น chunk ใหญ่ ๆ เพื่อลดงาน vacuum โดยทั่วไปก็จะปิด autovacuum แบบข้างบน และรัน
VACUUM FREEZEเป็นระยะ ๆ กับพาร์ทิชันวันที่ที่อยู่ถัดจากพาร์ทิชันที่กำลังโหลดอยู่นี่เป็นแนวทางของ PG ปกติ และช่วยให้ฐานข้อมูลข้ามงานบางส่วนที่เกี่ยวกับการดูแลสถานะกึ่งกลางซึ่งมีธุรกรรมใหม่ถูกเขียนแล้วแต่ยังมองเห็นได้ไม่ครบทุกคน
โดยเฉพาะกับการโหลดเป็นระดับ chunk การได้ยินจากผู้เชี่ยวชาญว่าข้อมูล WAL ไม่จำเป็นนักสำหรับการโหลดข้อมูลจำนวนมากนี่เป็นข่าวดีมาก
ตอนนี้ยังไม่มี UPS แต่หวังว่าจะผ่านเวลาประมาณ 20 วัน ที่ใช้โหลดข้อมูลไปได้โดยไม่ไฟดับ และในกรณีเลวร้ายที่สุดก็น่าจะโหลดต่อจากเดิมได้
ถ้า OP คือผู้เขียน การทดลองคล้าย ๆ กันที่ฉันทำไว้เมื่อราว 4 ปีก่อนอาจน่าสนใจด้วย
เป็นชุดข้อมูลเดียวกัน เป้าหมายเดียวกัน และวัตถุประสงค์คล้ายกัน
https://rdrn.me/optimising-sql/
ลำดับการสำรวจก็คล้ายกัน แต่ฉันใช้ Postgres ปกติแทน Timescale และในการตั้งค่าของฉัน ถ้าสมมติว่าข้อมูลอยู่ในหน่วยความจำอยู่แล้ว การ คัดลอกข้อมูลไบนารีโดยตรง เร็วกว่า COPY ราว 3 เท่า
ฉันทิ้งเชิงอรรถไว้ถึงเหตุผลที่ไม่ได้ลอง binary COPY ซึ่งหลัก ๆ คือมีคนอื่นบอกว่าประสิทธิภาพน่าผิดหวัง
แต่ถึงอย่างนั้นก็ดูเหมือนว่าควรลองด้วยตัวเอง
https://aliramadhan.me/2024/03/31/trillion-rows.html#fn:copy-binary-note
ฉันชอบรูปแบบที่มีหมายเหตุไว้ตรงขอบ
และการที่คุณให้ฟังก์ชันสำหรับเขียน structured array ของ numpy เป็นไบนารีของ Postgres ก็ช่วยได้มาก เพราะก่อนหน้านี้ฉันหาวิธีนี้ไม่เจอ
“ฐานข้อมูลเชิงสัมพันธ์เหมาะกับข้อมูลสภาพอากาศแบบกริดจริงหรือ? ไม่รู้เหมือนกัน แต่ลองทำดูก็เดี๋ยวรู้”
ฉันชอบท่าทีแบบนี้
มันตรงกันข้ามกับสไตล์ “ความจริงก็คือ” ของบทความสายเทคกระแสหลักอื่น ๆ เลย เลยยิ่งถูกใจ
และยังชอบที่มันดึงผู้อ่านให้อยู่กับเรื่องราวได้ตลอดการเดินทาง
ฉันค้นมามากแล้วแต่หาคำตอบชี้ขาดสำหรับกรณีใช้งานของตัวเองไม่ได้ เลยตัดสินใจรัน benchmark เอง
เป็นบทความที่น่าสนใจ
ประโยคที่ว่า “ถ้า insert ได้ต่อเนื่องที่ประมาณ 462k ครั้งต่อวินาที ก็จะใช้เวลาราว 20 วันสำหรับประมาณ 754 พันล้านแถว ซึ่งถือว่าไม่เลว แถมยังสั้นกว่าเวลาที่ใช้เขียนบทความนี้” ทำให้ฉันขำ
ฉันเองก็เริ่มเอนเอียงไปทางการเขียนบล็อกโพสต์ที่ยาวและลึกขึ้นเหมือนกัน เลยเข้าใจดีว่ามันใช้แรงมากกว่าที่คิด
ถ้าคุณอยากวาดกราฟอนุกรมเวลาหรือกราฟหลายแบบได้ตรงจาก SQL query เลย qStudio เป็น SQL IDE ฟรีที่ทำงานได้กับหลายฐานข้อมูลรวมถึง TimescaleDB
https://www.timestored.com/qstudio/database/timescale
ข้อชี้แจง: ฉันเป็นคนสร้างเครื่องมือนี้เอง
ฉันคิดว่าอาจเพิ่มการรองรับ Timeplus ได้
Timeplus เป็นฐานข้อมูลแบบสตรีมมิงที่สร้างบน ClickHouse และเอนจินฐานข้อมูลหลัก Timeplus Proton ก็เป็นโอเพนซอร์ส
qStudio ก็เป็นโอเพนซอร์สและเขียนด้วย Java ดังนั้นดูเหมือนว่าการรองรับ RDBMS ใหม่คงต้องใช้ JDBC driver
ถ้าเข้าใจถูก Timeplus Proton มี JDBC driver แบบโอเพนซอร์สที่ต่อยอดจาก ClickHouse driver และเพิ่มการปรับแก้สำหรับงานสตรีมมิง
https://www.timeplus.com/
https://github.com/timeplus-io/proton
https://github.com/timeseries/qstudio
https://github.com/timeplus-io/proton-java-driver
ตอนนี้ฉันยังทำแค่การ insert ข้อมูล แต่เดี๋ยวจะลอง query และ plotting เร็ว ๆ นี้
ผมก็ใช้ ข้อมูลวิเคราะห์ย้อนหลัง ERA5 และต้องการ time series ที่รวดเร็ว
ข้อมูลมาในรูปกริด
[lat, lon]ที่สะสมตามช่วงเวลาที่เลือก เช่น รูปแบบ[ข้อมูลรายชั่วโมงของหนึ่งเดือน, lat, lon]ดังนั้นถ้าต้องการมากกว่า 20 ปี มันจะกลายเป็นปัญหา transpose เมทริกซ์ขนาดมหึมาวิธีที่ผมทำคือดาวน์โหลดไฟล์ netCDF แต่ละไฟล์ จากนั้น transpose แล้วใส่ลงในไฟล์ HDF แบบ 3D ขนาดใหญ่ที่จัดเป็น
[lat, lon, hour]บนเวิร์กสเตชันของผม การสร้างข้อมูล 1 ปีสำหรับตัวแปรหนึ่งตัวใช้เวลาประมาณ 30 นาที แต่หลังจากนั้นการดึงตำแหน่ง
(lat, lon)เพียงจุดเดียวทำได้ในระดับมิลลิวินาทีเป็นวิธีที่ลำบากตอนต้นแต่คุ้มค่าในระยะยาว
มันเรียบง่ายดี แต่ผมไม่ใช่ผู้เชี่ยวชาญด้านฐานข้อมูล เป็นแค่นักภูมิอากาศวิทยาเท่านั้น
ดูเหมือนทั้ง rabernat ที่คอมเมนต์ที่นี่และ open-meteo ก็ใช้วิธีคล้ายกันและมองว่ามันเร็ว