Cơ sở dữ liệu Messenger (phần 2): phân vùng “vì lợi nhuận”

Chúng tôi đã thiết kế thành công cấu trúc cơ sở dữ liệu PostgreSQL để lưu trữ thư từ, một năm đã trôi qua, người dùng đang tích cực điền vào nó và bây giờ nó chứa hàng triệu hồ sơ, và... có gì đó bắt đầu chậm lại.

Cơ sở dữ liệu Messenger (phần 2): phân vùng “vì lợi nhuận”
Thực tế là Khi kích thước bảng tăng lên thì “độ sâu” của các chỉ mục cũng tăng theo. - mặc dù theo logarit. Nhưng theo thời gian, điều này buộc máy chủ phải thực hiện các tác vụ đọc/ghi tương tự xử lý nhiều trang dữ liệu hơnhơn lúc đầu.

Đây là nơi nó đến để giải cứu sự chia cắt.

Xin lưu ý rằng chúng ta không nói về sharding, tức là phân phối dữ liệu giữa các cơ sở dữ liệu hoặc máy chủ khác nhau. Bởi vì ngay cả việc chia dữ liệu thành một số máy chủ, bạn sẽ không thoát khỏi vấn đề chỉ số “sưng lên” theo thời gian. Rõ ràng là nếu bạn có đủ khả năng để đưa một máy chủ mới vào hoạt động hàng ngày, thì các vấn đề của bạn sẽ không còn nằm trong khuôn khổ của một cơ sở dữ liệu cụ thể nữa.

Chúng tôi sẽ xem xét không phải các tập lệnh cụ thể để triển khai phân vùng “trong phần cứng”, mà là bản thân cách tiếp cận - nên “cắt thành từng lát” cái gì và như thế nào, và mong muốn như vậy sẽ dẫn đến điều gì.

Khái niệm

Hãy xác định lại mục tiêu của chúng ta một lần nữa: chúng ta muốn đảm bảo rằng hôm nay, ngày mai và trong một năm, lượng dữ liệu được PostgreSQL đọc trong bất kỳ thao tác đọc/ghi nào vẫn gần như cũ.

Bất cứ gì dữ liệu được tích lũy theo trình tự thời gian (tin nhắn, tài liệu, nhật ký, kho lưu trữ, ...) sự lựa chọn tự nhiên làm khóa phân vùng là ngày/giờ sự kiện. Trong trường hợp của chúng tôi, một sự kiện như vậy là khoảnh khắc gửi tin nhắn.

Lưu ý rằng người dùng hầu như luôn luôn chỉ làm việc với những cái “mới nhất” dữ liệu đó - họ đọc tin nhắn mới nhất, phân tích nhật ký mới nhất,... Không, tất nhiên, họ có thể cuộn ngược thời gian xa hơn, nhưng họ rất hiếm khi làm điều này.

Từ những hạn chế này, rõ ràng là giải pháp thông điệp tối ưu sẽ là chuyên mục "hàng ngày" - xét cho cùng, người dùng của chúng tôi hầu như sẽ luôn đọc những gì đến với anh ấy “hôm nay” hoặc “hôm qua”.

Nếu chúng ta hầu như chỉ viết và đọc trong một phần trong ngày, thì điều này cũng mang lại cho chúng ta sử dụng bộ nhớ và đĩa hiệu quả hơn - vì tất cả các chỉ mục phần dễ dàng vừa với RAM, trái ngược với những chỉ mục "to và mập" trên toàn bảng.

bước-by-step

Nói chung, mọi thứ nói trên nghe có vẻ giống như một khoản lợi nhuận liên tục. Và nó có thể đạt được, nhưng để làm được điều này, chúng ta sẽ phải cố gắng rất nhiều - bởi vì quyết định phân chia một trong các thực thể dẫn đến nhu cầu “cưa” các thực thể liên quan.

Thông điệp, thuộc tính và hình chiếu của nó

Vì chúng tôi đã quyết định cắt thư theo ngày nên việc phân chia các thực thể-thuộc tính phụ thuộc vào chúng (tệp đính kèm, danh sách người nhận) và cũng theo ngày nhắn tin.

Vì một trong những nhiệm vụ điển hình của chúng tôi là xem chính xác các thanh ghi tin nhắn (chưa đọc, đến, tất cả), nên việc “rút chúng vào” để phân vùng theo ngày của tin nhắn cũng là điều hợp lý.

Cơ sở dữ liệu Messenger (phần 2): phân vùng “vì lợi nhuận”

Chúng tôi thêm khóa phân vùng (ngày nhắn) vào tất cả các bảng: người nhận, tệp, sổ đăng ký. Bạn không cần phải thêm nó vào tin nhắn mà hãy sử dụng DateTime hiện có.

đề

Vì chỉ có một chủ đề cho nhiều tin nhắn nên không có cách nào “cắt” nó theo cùng một mô hình mà bạn phải dựa vào một thứ khác. Trong trường hợp của chúng tôi, đó là lý tưởng ngày gửi tin nhắn đầu tiên - trên thực tế, đó là thời điểm sáng tạo của chủ đề.

Cơ sở dữ liệu Messenger (phần 2): phân vùng “vì lợi nhuận”

Thêm khóa phân vùng (ngày chủ đề) vào tất cả các bảng: chủ đề, người tham gia.

Nhưng bây giờ chúng tôi có hai vấn đề cùng một lúc:

  • Tôi nên tìm tin nhắn về chủ đề này ở phần nào?
  • Tôi nên tìm chủ đề trong tin nhắn ở phần nào?

Tất nhiên, chúng tôi có thể tiếp tục tìm kiếm trong tất cả các phần, nhưng điều này sẽ rất đáng buồn và sẽ phủ nhận tất cả số tiền thắng cược của chúng tôi. Do đó, để biết chính xác nơi cần tìm, chúng tôi sẽ tạo các liên kết/con trỏ hợp lý đến các phần:

  • chúng tôi sẽ thêm vào tin nhắn trường ngày chủ đề
  • hãy thêm vào chủ đề đặt ngày tin nhắn sự tương ứng này (có thể là một bảng riêng biệt hoặc một mảng ngày tháng)

Cơ sở dữ liệu Messenger (phần 2): phân vùng “vì lợi nhuận”

Vì sẽ có một vài sửa đổi đối với danh sách ngày gửi tin nhắn cho từng thư từ riêng lẻ (xét cho cùng, hầu hết tất cả các tin nhắn đều rơi vào 1-2 ngày liền kề), tôi sẽ tập trung vào tùy chọn này.

Tổng cộng, cấu trúc cơ sở dữ liệu của chúng tôi có dạng sau, có tính đến việc phân vùng:

Tables: RU, nếu bạn không thích bảng chữ cái Cyrillic trong tên bảng/trường thì tốt nhất không nên tìm

-- секции по дате сообщения
CREATE TABLE "Сообщение_YYYYMMDD"(
  "Сообщение"
    uuid
      PRIMARY KEY
, "Тема"
    uuid
, "ДатаТемы"
    date
, "Автор"
    uuid
, "ДатаВремя" -- используем как дату
    timestamp
, "Текст"
    text
);

CREATE TABLE "Адресат_YYYYMMDD"(
  "ДатаСообщения"
    date
, "Сообщение"
    uuid
, "Персона"
    uuid
, PRIMARY KEY("Сообщение", "Персона")
);

CREATE TABLE "Файл_YYYYMMDD"(
  "ДатаСообщения"
    date
, "Файл"
    uuid
      PRIMARY KEY
, "Сообщение"
    uuid
, "BLOB"
    uuid
, "Имя"
    text
);

CREATE TABLE "РеестрСообщений_YYYYMMDD"(
  "ДатаСообщения"
    date
, "Владелец"
    uuid
, "ТипРеестра"
    smallint
, "ДатаВремя"
    timestamp
, "Сообщение"
    uuid
, PRIMARY KEY("Владелец", "ТипРеестра", "Сообщение")
);
CREATE INDEX ON "РеестрСообщений_YYYYMMDD"("Владелец", "ТипРеестра", "ДатаВремя" DESC);

-- секции по дате темы
CREATE TABLE "Тема_YYYYMMDD"(
  "ДатаТемы"
    date
, "Тема"
    uuid
      PRIMARY KEY
, "Документ"
    uuid
, "Название"
    text
);

CREATE TABLE "УчастникТемы_YYYYMMDD"(
  "ДатаТемы"
    date
, "Тема"
    uuid
, "Персона"
    uuid
, PRIMARY KEY("Тема", "Персона")
);

CREATE TABLE "ДатыСообщенийТемы_YYYYMMDD"(
  "ДатаТемы"
    date
, "Тема"
    uuid
      PRIMARY KEY
, "Дата"
    date
);

Tiết kiệm một xu khá

Chà, nếu chúng ta sử dụng không tùy chọn phân chia cổ điển dựa trên sự phân phối các giá trị trường (thông qua trình kích hoạt và kế thừa hoặc PARTITION BY) và “thủ công” ở cấp ứng dụng, bạn sẽ nhận thấy rằng giá trị của khóa phân vùng đã được lưu trong tên của chính bảng đó.

Vì vậy nếu bạn như vậy Bạn đang rất lo lắng về lượng dữ liệu được lưu trữ?, thì bạn có thể loại bỏ các trường "phụ" này và giải quyết các bảng cụ thể. Đúng, tất cả các lựa chọn từ một số phần trong trường hợp này sẽ phải được chuyển sang phía ứng dụng.

Nguồn: www.habr.com

Thêm một lời nhận xét