Cách lưu trữ dữ liệu chuỗi thời gian


22

Tôi có những gì tôi tin là tập dữ liệu chuỗi thời gian (vui lòng sửa cho tôi nếu tôi sai) có một loạt các giá trị liên quan.

Một ví dụ sẽ là mô hình một chiếc xe hơi và theo dõi các thuộc tính khác nhau của nó trong một chuyến đi. Ví dụ:

dấu thời gian | tốc độ | quãng đường đi được | nhiệt độ | v.v.

Điều gì sẽ là cách tốt nhất để lưu trữ dữ liệu này để ứng dụng web có thể truy vấn hiệu quả các trường để tìm tối đa, phút và vẽ từng dữ liệu được đặt theo thời gian?

Tôi bắt đầu một cách tiếp cận ngây thơ về phân tích cú pháp dữ liệu và lưu trữ kết quả để chúng không bao giờ phải lưu trữ. Tuy nhiên, sau khi chơi với nó một chút, có vẻ như giải pháp này sẽ không mở rộng được lâu dài do hạn chế về bộ nhớ và nếu bộ nhớ cache bị xóa, thì tất cả dữ liệu sẽ cần được phân tích lại và lưu lại.

Ngoài ra, giả sử rằng dữ liệu được theo dõi mỗi giây với khả năng hiếm hoi của các bộ dữ liệu hơn 10 giờ, thông thường có nên cắt bớt bộ dữ liệu bằng cách lấy mẫu mỗi N giây không?

Câu trả lời:


31

Thực sự không có ai "cách tốt nhất" để lưu trữ dữ liệu chuỗi thời gian, và nó thực sự phụ thuộc vào một số yếu tố. Tuy nhiên, tôi sẽ tập trung vào hai yếu tố chủ yếu, với chúng là:

(1) Dự án này nghiêm trọng đến mức nào mà nó xứng đáng với nỗ lực của bạn để tối ưu hóa lược đồ?

(2) Các mẫu truy cập truy vấn của bạn thực sự sẽ như thế nào?

Với những câu hỏi đó, hãy thảo luận về một vài lựa chọn lược đồ.

Bàn phẳng

Tùy chọn sử dụng bảng phẳng có liên quan nhiều đến câu hỏi (1) , nếu đây không phải là một dự án nghiêm trọng hoặc quy mô lớn, bạn sẽ thấy dễ dàng hơn nhiều khi không nghĩ quá nhiều về lược đồ và chỉ cần sử dụng một bàn phẳng, như:

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

Không có nhiều trường hợp tôi muốn giới thiệu khóa học này, chỉ khi đây là một dự án nhỏ không đảm bảo nhiều thời gian của bạn.

Kích thước và sự kiện

Vì vậy, nếu bạn đã xóa rào cản của câu hỏi (1) và bạn muốn có một lược đồ hiệu suất cao hơn, đây là một trong những lựa chọn đầu tiên để xem xét. Nó bao gồm một số tiêu chuẩn hóa cơ bản, nhưng trích xuất các đại lượng 'chiều' từ các đại lượng 'thực tế' được đo.

Về cơ bản, bạn sẽ muốn một bảng để ghi thông tin về các chuyến đi,

CREATE trips(
  trip_id integer,
  other_info text);

và một bảng để ghi lại dấu thời gian,

CREATE tstamps(
  tstamp_id integer,
  tstamp timestamptz);

và cuối cùng là tất cả các sự kiện được đo của bạn, với các tham chiếu khóa ngoài đến các bảng thứ nguyên (đó là meas_facts(trip_id)tài liệu tham khảo trips(trip_id)& meas_facts(tstamp_id)tài liệu tham khảo tstamps(tstamp_id))

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

Điều này có vẻ không hoàn toàn hữu ích lúc đầu, nhưng nếu bạn có hàng ngàn chuyến đi đồng thời, thì tất cả họ có thể thực hiện các phép đo một lần mỗi giây, vào giây. Trong trường hợp đó, bạn phải ghi lại dấu thời gian mỗi lần cho mỗi chuyến đi, thay vì chỉ sử dụng một mục trong tstampsbảng.

Ca sử dụng: Trường hợp này sẽ tốt nếu có nhiều chuyến đi đồng thời mà bạn đang ghi dữ liệu và bạn không ngại truy cập tất cả các loại đo lường cùng nhau.

Vì Postgres đọc theo hàng, bất cứ lúc nào bạn muốn, ví dụ, các speedphép đo trong một khoảng thời gian nhất định, bạn phải đọc toàn bộ hàng từ meas_factsbảng, điều này chắc chắn sẽ làm chậm một truy vấn, mặc dù nếu tập dữ liệu bạn đang làm việc là không quá lớn, thậm chí bạn sẽ không nhận thấy sự khác biệt.

Chia nhỏ sự kiện đo lường của bạn

Để mở rộng phần cuối cùng thêm một chút nữa, bạn có thể chia các số đo của mình thành các bảng riêng biệt, ví dụ như tôi sẽ hiển thị các bảng về tốc độ và khoảng cách:

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

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

Tất nhiên, bạn có thể thấy làm thế nào điều này có thể được mở rộng cho các phép đo khác.

Trường hợp sử dụng: Vì vậy, điều này sẽ không cung cấp cho bạn một tốc độ cực lớn cho một truy vấn, có lẽ chỉ tăng tốc độ tuyến tính khi bạn truy vấn về một loại đo lường. Điều này là do khi bạn muốn tìm kiếm thông tin về tốc độ, bạn chỉ cần đọc các hàng từ speed_factsbảng, thay vì tất cả các thông tin bổ sung, không cần thiết sẽ có trong một hàng của meas_factsbảng.

Vì vậy, bạn chỉ cần đọc số lượng lớn dữ liệu về một loại đo duy nhất, bạn có thể nhận được một số lợi ích. Với trường hợp đề xuất 10 giờ dữ liệu của bạn trong một khoảng thời gian một giây, bạn chỉ đọc được 36.000 hàng, vì vậy bạn sẽ không bao giờ thực sự tìm thấy lợi ích đáng kể từ việc này. Tuy nhiên, nếu bạn đang xem dữ liệu đo tốc độ cho 5.000 chuyến đi trong khoảng 10 giờ, thì bây giờ bạn đang xem đọc 180 triệu hàng. Việc tăng tốc độ tuyến tính cho một truy vấn như vậy có thể mang lại một số lợi ích, miễn là bạn chỉ cần truy cập một hoặc hai loại đo lường tại một thời điểm.

Mảng / HStore / & TOAST

Có lẽ bạn không cần phải lo lắng về phần này, nhưng tôi biết những trường hợp có vấn đề. Nếu bạn cần phải truy cập HUGE lượng dữ liệu chuỗi thời gian, và bạn biết bạn cần phải truy cập vào tất cả của nó trong một khối lớn, bạn có thể sử dụng một cấu trúc mà sẽ làm cho việc sử dụng Bàn TOAST , trong đó chủ yếu lưu trữ dữ liệu của bạn trong lớn hơn, nén phân khúc. Điều này dẫn đến việc truy cập dữ liệu nhanh hơn, miễn là mục tiêu của bạn là truy cập tất cả dữ liệu.

Một ví dụ thực hiện có thể là

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

Trong bảng này, tstartsẽ lưu trữ dấu thời gian cho mục nhập đầu tiên trong mảng và mỗi mục tiếp theo sẽ là giá trị của lần đọc cho giây tiếp theo. Điều này đòi hỏi bạn phải quản lý dấu thời gian có liên quan cho từng giá trị mảng trong một phần mềm ứng dụng.

Một khả năng khác là

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

nơi bạn thêm các giá trị đo lường của mình dưới dạng các cặp (khóa, giá trị) của (dấu thời gian, phép đo).

Ca sử dụng: Đây là một triển khai có lẽ tốt hơn dành cho ai đó cảm thấy thoải mái hơn với PostgreSQL và chỉ khi bạn chắc chắn về các mẫu truy cập của mình cần phải là các mẫu truy cập hàng loạt.

Kết luận?

Wow, điều này đã lâu hơn tôi mong đợi, xin lỗi. :)

Về cơ bản, có một số tùy chọn, nhưng có lẽ bạn sẽ nhận được khoản tiền lớn nhất cho đồng tiền của mình bằng cách sử dụng thứ hai hoặc thứ ba, vì chúng phù hợp với trường hợp tổng quát hơn.

PS: Câu hỏi ban đầu của bạn ngụ ý rằng bạn sẽ tải số lượng lớn dữ liệu của mình sau khi tất cả được thu thập. Nếu bạn đang truyền dữ liệu vào ví dụ PostgreQuery của mình, bạn sẽ cần thực hiện thêm một số công việc để xử lý cả việc nhập dữ liệu và khối lượng công việc truy vấn, nhưng chúng ta sẽ để việc đó trong một thời gian khác. ;)


Wow, cảm ơn vì câu trả lời chi tiết, Chris! Tôi sẽ xem xét sử dụng tùy chọn 2 hoặc 3.
guest82

Chúc bạn may mắn!
Chris

Ồ, tôi sẽ bỏ phiếu cho câu trả lời này 1000 lần nếu tôi có thể. Cảm ơn vì lời giải thích chi tiết.
kikocorreoso

1

Năm 2019 và câu hỏi này xứng đáng được trả lời cập nhật.

  • Cho dù cách tiếp cận là tốt nhất hay không là điều tôi sẽ đưa bạn đến điểm chuẩn và kiểm tra nhưng đây là một cách tiếp cận.
  • Sử dụng một phần mở rộng cơ sở dữ liệu được gọi là timescaledb
  • Đây là một tiện ích mở rộng được cài đặt trên PostgreSQL tiêu chuẩn và xử lý một số vấn đề gặp phải trong khi lưu trữ chuỗi thời gian hợp lý

Lấy ví dụ của bạn, trước tiên hãy tạo một bảng đơn giản trong PostgreSQL

Bước 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
) 

Bước 2

  • Biến điều này thành thứ được gọi là hypertable trong thế giới của timescaledb.
  • Nói một cách đơn giản, đó là một bảng lớn được liên tục chia thành các bảng nhỏ hơn trong một khoảng thời gian, giả sử một ngày trong đó mỗi bảng nhỏ được gọi là một bảng
  • Bảng mini này không rõ ràng khi bạn chạy truy vấn mặc dù bạn có thể bao gồm hoặc loại trừ nó trong các truy vấn của mình

    CHỌN created_hypertable ('trip', 'ts', chunk_time_interval => khoảng '1 giờ', if_not_exists => TRUE);

  • Những gì chúng tôi đã làm ở trên là lấy bảng chuyến đi của chúng tôi, chia nó thành các bảng nhỏ mỗi giờ trên cơ sở cột 'ts'. Nếu bạn thêm dấu thời gian từ 10:00 đến 10:59, chúng sẽ được thêm vào 1 chunk nhưng 11:00 sẽ được chèn vào một đoạn mới và điều này sẽ tiếp diễn vô tận.

  • Nếu bạn không muốn lưu trữ dữ liệu vô hạn, bạn cũng có thể DROP chunk cũ hơn 3 tháng bằng cách sử dụng

    CHỌN drop_chunks (khoảng '3 tháng', 'chuyến đi');

  • Bạn cũng có thể nhận được một danh sách tất cả các khối được tạo cho đến ngày bằng cách sử dụng một truy vấn như

    CHỌN chunk_table, table_bytes, index_bytes, Total_bytes TỪ chunk_relation_size ('trip');

  • Điều này sẽ cung cấp cho bạn một danh sách tất cả các bảng nhỏ được tạo cho đến ngày và bạn có thể chạy truy vấn trên bảng mini cuối cùng nếu bạn muốn từ danh sách này

  • Bạn có thể tối ưu hóa các truy vấn của mình để bao gồm, loại trừ các khối hoặc chỉ hoạt động trên các khối N cuối cùng, v.v.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.