วิธีการจัดเก็บข้อมูลอนุกรมเวลา


22

ฉันมีสิ่งที่ฉันเชื่อว่าเป็นชุดข้อมูลอนุกรมเวลา (โปรดแก้ไขให้ฉันถ้าฉันผิด) ซึ่งมีค่าที่เกี่ยวข้องมากมาย

ตัวอย่างจะเป็นแบบจำลองรถยนต์และติดตามคุณลักษณะต่าง ๆ ของมันในระหว่างการเดินทาง ตัวอย่างเช่น:

ประทับเวลา ความเร็ว | ระยะทางที่เดินทาง อุณหภูมิ ฯลฯ

อะไรจะเป็นวิธีที่ดีที่สุดในการจัดเก็บข้อมูลนี้เพื่อให้เว็บแอปพลิเคชันสามารถสืบค้นฟิลด์เพื่อหา max, mins และพล็อตข้อมูลแต่ละชุดได้อย่างมีประสิทธิภาพเมื่อเวลาผ่านไป

ฉันเริ่มต้นวิธีการแยกวิเคราะห์การถ่ายโอนข้อมูลและแคชผลลัพธ์ที่ไร้เดียงสาเพื่อไม่ให้ถูกจัดเก็บ อย่างไรก็ตามหลังจากเล่นไปสักครู่มันจะปรากฏขึ้นวิธีนี้จะไม่ขยายในระยะยาวเนื่องจากข้อ จำกัด ของหน่วยความจำและถ้าแคชจะถูกล้างข้อมูลทั้งหมดจะต้องมีการแยกวิเคราะห์และแคชอีกครั้ง

นอกจากนี้สมมติว่าข้อมูลถูกติดตามทุกวินาทีด้วยความเป็นไปได้ที่หาได้ยากของชุดข้อมูลมากกว่า 10 ชั่วโมงเราแนะนำให้ตัดทอนชุดข้อมูลโดยการสุ่มตัวอย่างทุก ๆ N วินาทีหรือไม่?

คำตอบ:


31

ไม่มีวิธีที่ดีที่สุดในการจัดเก็บข้อมูลอนุกรมเวลาและมันขึ้นอยู่กับปัจจัยหลายอย่าง อย่างไรก็ตามฉันจะมุ่งเน้นไปที่สองปัจจัยหลักโดยมี:

(1) โครงการนี้ร้ายแรงเพียงใดที่สมควรได้รับความพยายามของคุณในการเพิ่มประสิทธิภาพสคีมา

(2) สิ่งที่เป็นรูปแบบการเข้าถึงการค้นหาของคุณจริงๆจะเป็นอย่างไร

ด้วยคำถามเหล่านั้นในใจเราจะพูดถึงตัวเลือกสคีมาสองสามตัว

โต๊ะแบน

ตัวเลือกในการใช้งานตารางแบนนั้นมีประโยชน์มากสำหรับคำถาม(1)ซึ่งหากนี่ไม่ใช่โครงการที่จริงจังหรือโครงการขนาดใหญ่คุณจะพบว่ามันง่ายกว่าที่จะไม่คิดมากเกี่ยวกับสคีมาและ เพียงใช้โต๊ะแบนเนื่องจาก:

CREATE flat_table(
  trip_id integer,
  tstamp timestamptz,
  speed float,
  distance float,
  temperature float,
  ,...);

มีหลายกรณีที่ฉันจะแนะนำหลักสูตรนี้เฉพาะในกรณีที่เป็นโครงการเล็ก ๆ ที่ไม่รับประกันเวลาของคุณมาก

ขนาดและข้อเท็จจริง

ดังนั้นหากคุณได้ลบล้างสิ่งกีดขวางของคำถาม(1)และคุณต้องการสคีมาประสิทธิภาพมากขึ้นนี่เป็นหนึ่งในตัวเลือกแรกที่ต้องพิจารณา มันมีบางบรรทัดฐานพื้นฐาน normailization แต่แยกปริมาณ 'มิติ' จากปริมาณ 'ความจริง' วัด

โดยพื้นฐานแล้วคุณจะต้องการตารางเพื่อบันทึกข้อมูลเกี่ยวกับการเดินทาง

CREATE trips(
  trip_id integer,
  other_info text);

และตารางสำหรับบันทึกการประทับเวลา

CREATE tstamps(
  tstamp_id integer,
  tstamp timestamptz);

และในที่สุดข้อเท็จจริงทั้งหมดที่คุณวัดได้มีการอ้างอิงคีย์ต่างประเทศไปยังตารางมิติ (นั่นคือmeas_facts(trip_id)การอ้างอิงtrips(trip_id)และmeas_facts(tstamp_id)การอ้างอิงtstamps(tstamp_id))

CREATE meas_facts(
  trip_id integer,
  tstamp_id integer,
  speed float,
  distance float,
  temperature float,
  ,...);

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

ใช้กรณี: กรณีนี้จะดีถ้ามีการเดินทางพร้อมกันหลายครั้งที่คุณกำลังบันทึกข้อมูลและคุณไม่รังเกียจที่จะเข้าถึงการวัดทุกประเภทด้วยกัน

เนื่องจาก Postgres อ่านตามแถวทุกครั้งที่คุณต้องการตัวอย่างเช่นการspeedวัดในช่วงเวลาที่กำหนดคุณจะต้องอ่านทั้งแถวจากmeas_factsตารางซึ่งจะทำให้แบบสอบถามช้าลงอย่างแน่นอนแม้ว่าชุดข้อมูลที่คุณกำลังทำงานอยู่นั้นเป็นอย่างไร ไม่ใหญ่เกินไปจากนั้นคุณจะไม่สังเกตเห็นความแตกต่าง

แบ่งข้อเท็จจริงที่วัดได้ของคุณ

หากต้องการขยายส่วนสุดท้ายอีกเล็กน้อยคุณสามารถแยกการวัดของคุณออกเป็นตารางแยกต่างหากตัวอย่างเช่นฉันจะแสดงตารางสำหรับความเร็วและระยะทาง:

CREATE speed_facts(
  trip_id integer,
  tstamp_id integer,
  speed float);

และ

CREATE distance_facts(
  trip_id integer,
  tstamp_id integer,
  distance float);

แน่นอนคุณสามารถดูว่าสิ่งนี้อาจขยายไปยังการวัดอื่น ๆ

ใช้กรณี:ดังนั้นสิ่งนี้จะไม่ทำให้คุณมีความเร็วเพิ่มขึ้นอย่างมากสำหรับการค้นหาอาจเป็นเพียงการเพิ่มความเร็วเชิงเส้นเมื่อคุณทำการสอบถามเกี่ยวกับการวัดประเภทหนึ่ง เนื่องจากเมื่อคุณต้องการค้นหาข้อมูลเกี่ยวกับความเร็วคุณจะต้องอ่านเฉพาะแถวจากspeed_factsตารางแทนที่จะเป็นข้อมูลพิเศษที่ไม่จำเป็นทั้งหมดที่จะปรากฏในแถวของmeas_factsตาราง

ดังนั้นคุณต้องอ่านข้อมูลขนาดใหญ่จำนวนมากเกี่ยวกับประเภทการวัดเดียวเท่านั้นคุณจะได้รับประโยชน์ ด้วยกรณีที่คุณนำเสนอข้อมูล 10 ชั่วโมงในช่วงเวลาหนึ่งวินาทีคุณจะอ่านได้ 36,000 แถวดังนั้นคุณจะไม่ได้รับประโยชน์อย่างมากจากการทำเช่นนี้ อย่างไรก็ตามหากคุณต้องดูข้อมูลการวัดความเร็วสำหรับ 5,000 เที่ยวที่ใช้เวลาประมาณ 10 ชั่วโมงตอนนี้คุณกำลังอ่าน 180 ล้านแถว การเพิ่มความเร็วเชิงเส้นสำหรับแบบสอบถามดังกล่าวอาจให้ประโยชน์บางอย่างตราบใดที่คุณต้องการเข้าถึงประเภทการวัดครั้งละหนึ่งหรือสองครั้งเท่านั้น

อาร์เรย์ / HStore / & TOAST

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

ตัวอย่างการใช้งานอาจเป็น

CREATE uber_table(
  trip_id integer,
  tstart timestamptz,
  speed float[],
  distance float[],
  temperature float[],
  ,...);

ในตารางนี้tstartจะเก็บการประทับเวลาสำหรับรายการแรกในอาร์เรย์และแต่ละรายการที่ตามมาจะเป็นค่าของการอ่านสำหรับวินาทีถัดไป สิ่งนี้ต้องการให้คุณจัดการการประทับเวลาที่เกี่ยวข้องสำหรับแต่ละค่าอาร์เรย์ในชิ้นส่วนของแอพพลิเคชั่นซอฟต์แวร์

ความเป็นไปได้อีกอย่างก็คือ

CREATE uber_table(
  trip_id integer,
  speed hstore,
  distance hstore,
  temperature hstore,
  ,...);

โดยที่คุณเพิ่มค่าการวัดของคุณเป็นคู่ (คีย์, ค่า) ของ (การประทับเวลา, การวัด)

กรณีการใช้งาน:นี่เป็นการนำไปปฏิบัติอาจดีกว่าปล่อยให้คนที่คุ้นเคยกับ PostgreSQL และถ้าคุณแน่ใจเกี่ยวกับรูปแบบการเข้าถึงของคุณซึ่งจำเป็นต้องใช้รูปแบบการเข้าถึงจำนวนมาก

สรุปผลการวิจัย?

ว้าวนี่นานกว่าที่ฉันคาดไว้ขออภัย :)

โดยพื้นฐานแล้วมีตัวเลือกมากมาย แต่คุณอาจได้รับผลตอบแทนที่ใหญ่ที่สุดสำหรับเจ้าชู้ของคุณโดยใช้ตัวเลือกที่สองหรือสาม

ป.ล. :คำถามแรกของคุณบอกเป็นนัยว่าคุณจะโหลดข้อมูลจำนวนมากหลังจากรวบรวมข้อมูลทั้งหมดแล้ว หากคุณกำลังสตรีมข้อมูลไปยังอินสแตนซ์ PostgreSQL ของคุณคุณจะต้องดำเนินการเพิ่มเติมเพื่อจัดการทั้งปริมาณข้อมูลและปริมาณงานแบบสอบถาม แต่เราจะปล่อยให้เป็นเช่นนั้นอีกครั้ง ;)


ว้าวขอบคุณสำหรับคำตอบโดยละเอียด Chris! ฉันจะใช้ตัวเลือก 2 หรือ 3
guest82

ขอให้โชคดีกับคุณ!
Chris

ว้าวฉันจะโหวตคำตอบนี้ 1,000 ครั้งถ้าทำได้ ขอบคุณสำหรับคำอธิบายโดยละเอียด
kikocorreoso

1

มัน2562และคำถามนี้สมควรได้รับคำตอบการปรับปรุง

  • ไม่ว่าวิธีการนั้นดีที่สุดหรือไม่เป็นสิ่งที่ฉันจะปล่อยให้คุณไปทดสอบและทดสอบมาตรฐาน แต่นี่คือวิธีการ
  • ใช้ส่วนขยายฐานข้อมูลชื่อtimescaledb
  • นี่คือส่วนขยายที่ติดตั้งบน PostgreSQL มาตรฐานและจัดการปัญหาต่าง ๆ ที่พบในขณะที่จัดเก็บอนุกรมเวลาได้ดีพอสมควร

ยกตัวอย่างของคุณก่อนสร้างตารางง่ายๆใน PostgreSQL

ขั้นตอนที่ 1

CREATE TABLE IF NOT EXISTS trip (
    ts TIMESTAMPTZ NOT NULL PRIMARY KEY,
    speed REAL NOT NULL,
    distance REAL NOT NULL,
    temperature REAL NOT NULL
) 

ขั้นตอนที่ 2

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

    SELECT create_hypertable ('trip', 'ts', chunk_time_interval => ช่วงเวลา '1 ชั่วโมง', if_not_exists => TRUE);

  • สิ่งที่เราทำไปแล้วคือนำตารางการเดินทางของเราแบ่งออกเป็นตารางย่อย ๆ ทุก ๆ ชั่วโมงโดยใช้คอลัมน์ 'ts' หากคุณเพิ่มเวลาประทับของ 10:00 ถึง 10:59 พวกเขาจะถูกเพิ่มใน 1 ก้อน แต่ 11:00 จะถูกแทรกลงในกลุ่มใหม่และจะดำเนินต่อไปเรื่อย ๆ

  • หากคุณไม่ต้องการจัดเก็บข้อมูลอย่างไม่สิ้นสุดคุณสามารถ DROP ชิ้นที่เก่ากว่าพูดได้ 3 เดือน

    เลือก drop_chunks (ช่วงเวลา '3 เดือน', 'การเดินทาง');

  • นอกจากนี้คุณยังสามารถรับรายการชิ้นส่วนทั้งหมดที่สร้างจนถึงวันที่โดยใช้แบบสอบถามเช่น

    เลือก chunk_table, table_bytes, index_bytes, total_bytes จาก chunk_relation_size ('การเดินทาง');

  • นี่จะให้รายการของตารางขนาดเล็กทั้งหมดที่สร้างจนถึงวันที่และคุณสามารถเรียกใช้แบบสอบถามในตารางขนาดเล็กสุดท้ายหากคุณต้องการจากรายการนี้

  • คุณสามารถเพิ่มประสิทธิภาพการค้นหาของคุณเพื่อรวมไม่รวมชิ้นหรือทำงานเฉพาะใน N ชิ้นสุดท้ายและอื่น ๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.