Làm thế nào để thiết kế một cơ sở dữ liệu để lưu trữ một danh sách được sắp xếp?


42

Tôi đang tìm cách lưu trữ một danh sách được sắp xếp bên trong cơ sở dữ liệu. Tôi muốn thực hiện các hoạt động sau đây một cách hiệu quả.

  1. Chèn (x) - Chèn bản ghi x vào bảng
  2. Xóa (x) - Xóa bản ghi x khỏi bảng
  3. Trước (x, n) - Trả về các bản ghi 'n' trước bản ghi x trong danh sách được sắp xếp.
  4. Sau (x, n) - Trả về các bản ghi 'n' thành công bản ghi x trong danh sách được sắp xếp.
  5. Đầu tiên (n) - Trả về các bản ghi 'n' đầu tiên từ danh sách được sắp xếp.
  6. Lần cuối (n) - Trả về các bản ghi 'n' cuối cùng từ danh sách đã sắp xếp.
  7. So sánh (x, y) - Cho hai bản ghi x và y từ bảng, tìm nếu x> y.

Phương pháp đơn giản tôi có thể nghĩ đến là lưu trữ một loại thuộc tính 'thứ hạng' trong bảng và truy vấn bằng cách sắp xếp trên thuộc tính đó. Nhưng trong phương pháp này, việc chèn / sửa đổi một bản ghi với thứ hạng sẽ trở thành một hoạt động tốn kém. Có một phương pháp tốt hơn?

Cụ thể, tôi đang tìm cách triển khai bảng bằng SimpleDB của Amazon. Nhưng một câu trả lời chung cho cơ sở dữ liệu quan hệ cũng sẽ hữu ích.

Cập nhật hồ sơ tải:

Vì tôi đang lập kế hoạch này cho một ứng dụng web, nó phụ thuộc vào số lượng người dùng sử dụng ứng dụng đó.

Nếu có 100k người dùng hoạt động (siêu lạc quan: P), thì ước tính rất gần đúng của tôi mỗi ngày sẽ là

500k chọn, 100k chèn và xóa, cập nhật 500k

Tôi hy vọng bảng sẽ tăng tổng cộng lên tới 500k.

Tôi đang tìm cách tối ưu hóa các bản cập nhật, chèn và các hoạt động So sánh. Thứ hạng của các vật phẩm sẽ liên tục thay đổi và tôi cần cập nhật bảng.


Xây dựng một chút về hồ sơ tải dự kiến ​​của bạn. Có bao nhiêu lựa chọn / chèn / cập nhật mỗi ngày? Những hoạt động nào bạn muốn nhất để tối ưu hóa cho? Làm thế nào lớn để bạn mong đợi bảng tăng trưởng mỗi ngày hoặc tổng cộng?
Nick Chammas

Đây có phải là một bảng xếp hạng người chơi? Dù sao, tôi đã cập nhật câu trả lời của tôi dưới đây với phản hồi dựa trên hồ sơ tải dự kiến ​​của bạn.
Nick Chammas

không, đó không phải là bảng xếp hạng người chơi.
chitti

Cách tiếp cận nào bạn đã sử dụng?
Nick Chammas

Tôi thậm chí không chắc chắn về những gì được hỏi ở đây hoặc những gì bạn không cần phải làm từ danh sách giặt là những việc bạn cần làm.
Evan Carroll

Câu trả lời:


22

Nếu thứ hạng không hoàn toàn tùy ý mà thay vào đó có thể lấy được từ một số tài sản khác (ví dụ như tên, điểm người chơi, v.v.) thì hãy xem câu trả lời của Joel .

Nếu đó một thuộc tính tùy ý của dữ liệu của bạn, thì đó nên được lưu trữ dưới dạng một cột trong bảng hồ sơ của bạn. Giả sử SimpleDB của Amazon tương tự như RDBMS điển hình, sau đó bạn có thể lập chỉ mục cột này và nhanh chóng đáp ứng tất cả các truy vấn trên của bạn bằng chiến lược lập chỉ mục phù hợp. Điều này là bình thường đối với RDBMS.

Cho rằng bạn mong đợi hoạt động chèn và cập nhật cao, nhưng hoạt động đọc tương đối cao, tôi khuyên bạn nên làm như sau:

  • Phân cụm bảng trên thứ hạng, đặc biệt nếu phần lớn các truy vấn của bạn chống lại thứ hạng. Nếu không, hoặc nếu chọn khóa phân cụm không có sẵn trong SimpleDB, thì chỉ cần tạo một chỉ mục có thứ hạng là cột hàng đầu. Điều này sẽ đáp ứng các truy vấn 3-6.
  • Một chỉ mục trên bản ghi trước rồi xếp hạng (hoặc, trong thế giới Máy chủ SQL, chỉ cần ghi và INCLUDExếp hạng hoặc chỉ ghi nếu bạn phân cụm theo thứ hạng) sẽ đáp ứng truy vấn 7.
  • Hoạt động 1 và 2 có thể được tối ưu hóa bằng cách sắp xếp dữ liệu của bạn một cách thích hợp (nghĩa là cài đặt FILLFACTORtrong SQL Server). Điều này đặc biệt quan trọng nếu bạn co cụm về thứ hạng.
  • Khi bạn chèn hoặc cập nhật thứ hạng, hãy duy trì càng nhiều khoảng cách giữa các số thứ hạng càng tốt để giảm thiểu khả năng đó là bạn sẽ cần phải xếp hạng lại một bản ghi hiện có để phù hợp với việc chèn hoặc cập nhật xếp hạng. Ví dụ: nếu bạn xếp hạng các bản ghi của mình theo các bước 1000, bạn sẽ có đủ chỗ cho khoảng một nửa số thay đổi và chèn với cơ hội tối thiểu bạn sẽ cần xếp hạng lại một bản ghi không liên quan trực tiếp đến những thay đổi đó.
  • Mỗi đêm xếp hạng lại tất cả các hồ sơ để thiết lập lại khoảng cách xếp hạng giữa chúng.
  • Bạn có thể điều chỉnh tần suất của bảng xếp hạng lại hàng loạt cũng như kích thước khoảng cách xếp hạng để phù hợp với số lần chèn hoặc cập nhật dự kiến ​​của bạn so với số lượng hồ sơ hiện có. Vì vậy, nếu bạn có 100 nghìn hồ sơ và mong muốn các bản chèn và cập nhật của bạn là 10% trong số đó, hãy chừa đủ chỗ cho 10 nghìn cấp bậc mới và xếp hạng lại hàng đêm.
  • Xếp hạng lại các bản ghi 500K là một hoạt động tốn kém, nhưng được thực hiện một lần một ngày hoặc một tuần ngoài giờ sẽ tốt cho cơ sở dữ liệu như vậy. Việc xếp hạng lại hàng giờ ngoài giờ này để duy trì khoảng cách xếp hạng là điều giúp bạn tiết kiệm được nhiều thứ hạng cho mỗi lần cập nhật thứ hạng hoặc chèn trong giờ bình thường và giờ cao điểm.

Nếu bạn mong đợi 100K + lần đọc trên bảng có kích thước 100K + tôi không khuyên bạn nên sử dụng phương pháp tiếp cận danh sách được liên kết. Nó sẽ không mở rộng quy mô tốt.


Cấp bậc có thể sửa đổi. Tôi hy vọng các cấp bậc sẽ thay đổi liên tục và hồ sơ mới được chèn liên tục. Tôi lo lắng về trường hợp khi tôi chèn một phần tử mới có thứ hạng thì thứ hạng của tất cả các bản ghi bên dưới bản ghi mới theo thứ tự cần phải thay đổi. Đó không phải là một hoạt động tốn kém khi tôi có hàng ngàn hồ sơ trong cơ sở dữ liệu của mình?
chitti

@chitti - À, đó là một mối quan tâm. Bạn có thể loại bỏ thứ hạng của mình (ví dụ 0, 1000, 2000, 3000, ...) và định kỳ xếp hạng lại tất cả các bản ghi khi khoảng trống xếp hạng được lấp đầy. Điều này sẽ không mở rộng nếu bạn mong đợi nhiều hơn một vài chục ngàn hồ sơ, mặc dù.
Nick Chammas

1
@chitti - Thật là buồn cười. Đây chính xác là vấn đề mà các công cụ cơ sở dữ liệu xử lý khi lập chỉ mục dữ liệu, bởi vì họ đang đặt hàng và sắp xếp lại nó khi dữ liệu được thêm hoặc thay đổi. Nếu bạn nhìn lên, FILLFACTORbạn sẽ thấy về cơ bản là tạo không gian thừa đó cho các bản ghi trong một chỉ mục, giống như các khoảng trống xếp hạng mà tôi mô tả tạo không gian cho thay đổi thứ hạng và chèn thêm.
Nick Chammas

2
Cảm ơn câu trả lời cập nhật. 'Thứ hạng' là một thuộc tính tùy ý trong dữ liệu của tôi. Tôi gần như bị thuyết phục rằng một cột chỉ mục tùy chỉnh là những gì tôi yêu cầu. Kiểm tra liên kết SO này với một câu hỏi tương tự. Câu trả lời hàng đầu cung cấp các khuyến nghị về cách xử lý một cột xếp hạng như vậy.
chitti

@chitti - Câu trả lời được chấp nhận cho câu hỏi SO đó là tuyệt vời. Nó gợi ý cách tiếp cận tương tự mà tôi đã nêu chi tiết ở đây, với gợi ý bổ sung về việc sử dụng số thập phân thay vì số nguyên để mở rộng đáng kể tính linh hoạt của bạn trong việc gán và thay đổi thứ hạng. Tuyệt vời tìm thấy.
Nick Chammas

13

Tôi thường sử dụng phương pháp "xếp hạng" mà bạn mô tả. Thay vì loay hoay với việc cập nhật các hàng khi các mục cần sắp xếp lại, tôi thường có thể thoát khỏi việc xóa tất cả các bản ghi trong danh sách và chèn lại các mục mới theo đúng thứ tự. Phương pháp này được tối ưu hóa rõ ràng để lấy.

Một cách tiếp cận khác là mô hình hóa các bản ghi dưới dạng một danh sách được liên kết bằng cách sử dụng cột khóa ngoại tiếp theo phản xạ "tiền thân" trên bảng:

ID   setID   item       predecessor
---  ------  ------     ------------
1    1       Apple      null
2    1       Orange     1
3    2       Cucumber   null
4    1       Pear       2
5    1       Grape      4
6    2       Carrot     3

Bạn có thể dễ dàng truy xuất danh sách và thêm và xóa các mục với ít chi phí, nhưng việc lấy các bản ghi theo thứ tự phù hợp sẽ rất khó khăn. Có lẽ có một cách thông minh để thực hiện nó trong một truy vấn duy nhất, có thể có rất nhiều phép nối bảng bí danh.

Tôi thường sử dụng phương pháp sau này khi tôi lập mô hình mối quan hệ kiểu cây (danh mục, thư mục, bộ và tập hợp con). Tôi thường có một hàm đệ quy nào đó để tái tạo lại toàn bộ cây trong ứng dụng của mình.


2
Các mô hình danh sách liên kết là gọn gàng. Để lấy thứ bậc như vậy theo thứ tự trong SQL Server, bạn sẽ sử dụng CTE đệ quy .
Nick Chammas

Xây dựng hệ thống phân cấp đó sẽ khá tốn kém cho một bàn cao. Ưu điểm là thay đổi thứ hạng / chèn / vv có thể được thực hiện dễ dàng. Tùy thuộc vào hồ sơ tải dự kiến ​​của chitti, đây thực sự có thể là cách tiếp cận tốt nhất.
Nick Chammas

Tùy chọn danh sách được liên kết trông giống như ý tưởng tốt nhất cho tất cả các hoạt động ngoại trừ So sánh. Bất cứ ý tưởng nào tôi sẽ thực hiện So sánh mà không phải theo dõi đường dẫn giữa hai yếu tố được so sánh?
chitti

Nếu bạn có ID của các mục tôi nghĩ So sánh () sẽ đơn giản, trừ khi tôi hiểu nhầm ý của bạn khi so sánh (). Khi bạn nói: "find if x> y" có nghĩa là "find if x before y"? Tôi không thể thấy rằng việc dễ dàng mà không có chỉ mục tùy chỉnh hoặc quy trình được lưu trữ sẽ đi theo danh sách (hoặc tính năng CTE thú vị được đề cập bởi @Nick).
bpanulla

5
Loại giải pháp này cũng xấp xỉ một mô hình dữ liệu đồ thị ( en.wikipedia.org/wiki/Graph_theory ). Một hệ thống lưu trữ được tối ưu hóa để lưu trữ các nút và cạnh của đồ thị có thể là một giải pháp tốt hơn RDBMS. Cơ sở dữ liệu ba và bốn cửa hàng và đồ thị như Neo4J khá tốt về điều này.
bpanulla

6

Tôi sẽ nghĩ rằng điều cần làm là lưu trữ các thuộc tính hoặc các thuộc tính được sử dụng để tính thứ hạng và sau đó xây dựng một chỉ mục trên chúng. Thay vì cố gắng buộc cơ sở dữ liệu lưu trữ dữ liệu theo thứ tự vật lý hoặc sử dụng danh sách được liên kết được quản lý thủ công, tại sao không để công cụ cơ sở dữ liệu làm những gì nó được thiết kế để làm?


2
Điều gì xảy ra nếu 'thuộc tính được sử dụng để tính thứ hạng' là tùy ý? Ví dụ: Một tập hợp các mục trong giỏ hàng được sắp xếp lại dựa trên các hành động tùy ý của người dùng.
chitti

Khi bạn nói thứ hạng là tùy ý, bạn có ý gì? Phải có một thuật toán mà bạn sử dụng để tính thứ hạng nên là gì. Ví dụ: "dựa trên các mục trong giỏ hàng" - Dựa vào đâu? Phải có một cái gì đó được lưu trữ trong cơ sở dữ liệu là trình điều khiển để tính thứ hạng. Nó có thể là sự kết hợp của một số thứ, nhưng những thứ này bằng cách nào đó phải được lưu trữ trong bảng khách hàng hoặc trong các bảng liên quan đến khách hàng. Nếu nó nằm trong dữ liệu thì bạn có thể tạo một hàm tính toán nó. Nếu bạn có thể tính toán nó, bạn có thể lưu trữ và lập chỉ mục cho nó.
Joel Brown

Giả sử rằng chúng ta cần duy trì thứ tự các mặt hàng trong giỏ hàng và thứ tự có thể được thay đổi tùy ý bởi người dùng bằng cách sử dụng web ui. Làm thế nào bạn sẽ lưu trữ một danh sách các mục như vậy trong cơ sở dữ liệu và làm thế nào bạn sẽ duy trì thứ tự sắp xếp?
chitti

Nếu tôi hiểu bạn một cách chính xác, bằng cách "tùy ý thay đổi" thứ tự các mặt hàng trong giỏ hàng, bạn có nghĩa là người dùng có thể kéo các mục lên xuống trong danh sách và thả chúng vào nơi họ muốn. Tôi đoán điều đó gây ấn tượng với tôi như một chút giả tạo. Tại sao người dùng sẽ làm điều đó? Nếu họ có thể làm điều đó, họ sẽ làm điều đó rất nhiều? Việc sử dụng một chuỗi các mặt hàng đơn giản trong một giỏ hàng có thực sự quan tâm đến hiệu suất không? Dường như với tôi, một số thứ tự từ một đến số lượng vật phẩm trong giỏ hàng + FK cho đơn hàng sẽ cung cấp cho bạn chỉ số bạn cần. Chỉ cần cập nhật các mục khi một người bị kéo xung quanh.
Joel Brown

3
Giỏ hàng chỉ là một ví dụ tôi đưa ra để chỉ ra rằng có những trường hợp 'thứ hạng' có thể tùy ý. Có thể đó không phải là một ví dụ tuyệt vời. Hàng đợi dvd Netflix có thể là một ví dụ tốt hơn. Chỉ để tranh luận, hãy tưởng tượng một hàng Netflix với 100 nghìn mục có thể được người dùng sắp xếp lại một cách tùy tiện và anh ta làm điều đó mỗi một phút. Làm thế nào bạn sẽ thiết kế một cơ sở dữ liệu để lưu trữ danh sách các bộ phim trong ứng dụng giả định này?
chitti

1

Đây là những hạn chế của một RDBMS không đơn giản như SimpleDB. Các tính năng bạn yêu cầu không thể được triển khai ở phía DB trong SimpleDB, chúng phải được triển khai từ phía lập trình / ứng dụng.

Đối với RDBMS như SQL server, các tính năng bạn yêu cầu thô sơ đối với chỉ mục được nhóm.

  • Chèn (x) - Chèn bản ghi x vào bảng> Chèn đơn giản.
  • Xóa (x) - Xóa bản ghi x khỏi bảng> Xóa đơn giản.
  • Trước (x, n) - Trả về các bản ghi 'n' trước bản ghi x trong danh sách được sắp xếp. > Chọn kết quả top n trong đó x nhỏ hơn giá trị và sắp xếp theo mệnh đề.

  • Sau (x, n) - Trả về các bản ghi 'n' thành công bản ghi x trong danh sách được sắp xếp. > Chọn kết quả top n trong đó x lớn hơn giá trị và thứ tự theo mệnh đề.

  • Đầu tiên (n) - Trả về các bản ghi 'n' đầu tiên từ danh sách được sắp xếp. > Chọn kết quả n hàng đầu.

  • Lần cuối (n) - Trả về các bản ghi 'n' cuối cùng từ danh sách đã sắp xếp. > Chọn kết quả n hàng đầu sau khi đặt hàng theo desc.

  • So sánh (x, y) - Cho hai bản ghi x và y từ bảng, tìm nếu x> y. > Tuyên bố TSQL IF.

SimpleDB cung cấp các chỉ mục tự động, sắp xếp và ngôn ngữ truy vấn cơ bản . Vấn đề của tôi sẽ vẫn còn ngay cả khi tôi chọn RDBMS. Vấn đề là do thứ hạng của dữ liệu trong cơ sở dữ liệu của tôi thay đổi tùy ý và chúng không thể được ghi lại dưới dạng một thuộc tính (trừ khi tôi sử dụng cột xếp hạng tùy chỉnh) có thể được lập chỉ mục.
chitti

0

Đây là những gì tôi đã sử dụng để xếp hạng lại bảng Postgres của mình sau mỗi lần chèn:

CREATE OR REPLACE FUNCTION re_rank_list() RETURNS trigger AS $re_rank_list$
DECLARE
    temprow record;
    row_idx integer := 1;    
BEGIN
    FOR temprow IN
    SELECT * FROM your_schema.your_list WHERE list_id = NEW.list_id ORDER BY rank ASC
    LOOP
        UPDATE your_schema.your_list SET rank = row_idx * 100 WHERE id = temprow.id;
        row_idx := row_idx + 1;
    END LOOP;
    RETURN NEW;
END;
$re_rank_list$ LANGUAGE plpgsql;


CREATE TRIGGER re_rank_list AFTER UPDATE ON your_schema.your_list_value
    FOR EACH ROW 
    WHEN (pg_trigger_depth() = 0)
    EXECUTE PROCEDURE re_rank_list();

Đối với trường hợp sử dụng của tôi, hiệu suất không phải là vấn đề đáng lo ngại, nhưng sự tự tin rằng nó sẽ không bao giờ phá vỡ hoặc hành động kỳ quặc là điều quan trọng.

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.