Điều gì có thể sai khi sử dụng cùng một chuỗi trên nhiều bảng trong postgres?


11

Chúng tôi đang xem xét sử dụng một chuỗi được chia sẻ để gán id cho các khóa chính cho tất cả các bảng trong cơ sở dữ liệu của chúng tôi. Có khoảng 100 người trong số họ. Chỉ có một cặp vợ chồng được chèn vào thường xuyên và thường xuyên. Chúng tôi muốn loại trừ nó là "một ý tưởng khủng khiếp vì một lý do rõ ràng" trước khi chúng tôi chuyển sang giai đoạn thực sự thử nó và thử nghiệm nó khi tải.

Tải trọng cao nhất của chúng tôi là thứ tự 1000 chèn một giây, trên một vài bảng.

Nghiên cứu của chúng tôi cho đến nay chỉ ra rằng - tốc độ tạo chuỗi không phải là vấn đề - phân mảnh chuỗi (khoảng trống) sẽ xảy ra, nhưng không nên là vấn đề - cạn kiệt id sẽ không thành vấn đề

Chúng tôi không chắc chắn nếu chúng ta thiếu những thứ lớn khác. Chúng tôi rất biết ơn ý kiến ​​của mọi người, đặc biệt là từ những người đã thử nó trước đây và có những trải nghiệm tích cực hoặc tiêu cực.

Đối với bối cảnh - chúng tôi có hai động lực chính để làm điều này.

Một động lực để làm điều này là để chúng ta có thể định nghĩa một loạt các từ điển (chúng ta gọi chúng là phạm vi) và có các từ có thể đọc được của con người được gán cho các id đó, vì vậy chúng tôi muốn đảm bảo rằng các id trong các bảng khác nhau không bao giờ trùng nhau. Vì vậy, trong một phạm vi, id 12345 có thể được gán giá trị "Xanh" và trong phạm vi khác, nó có thể được gán "Verde". (Trên thực tế, chúng tôi không sử dụng nó để quốc tế hóa, nhưng chúng tôi có thể một ngày nào đó).

Động lực khác là làm cho nó dễ dàng có một số triển khai trong trường và biết (bằng cách đặt duy nhất một vài chuỗi chữ số quan trọng nhất của triển khai) rằng các triển khai của chúng tôi sẽ không chồng lấp các khóa chính. (Giống như một GUID lite).


Bạn tự tin đến mức nào khi 12345 sẽ không bao giờ được sử dụng trong nhiều bảng (nghĩa là vì một id được cập nhật thủ công vì một số lý do trong tương lai)? Tôi muốn có sự an toàn hơn khi biết cơ sở dữ liệu đang thực thi ràng buộc đó, rằng gánh nặng của việc phải tự thực thi nó.
Jack nói hãy thử topanswers.xyz

Câu trả lời:


5

Ba vấn đề có thể nảy sinh trong đầu là:

  1. Với bất kỳ tài nguyên được chia sẻ nào, bạn đang tạo ra một nút cổ chai tiềm năng. Ruột của tôi nói rằng đối với tải trọng cao nhất của bạn thì đây không phải là vấn đề nhưng tôi thực sự khuyên bạn nên điểm chuẩn bất kỳ giải pháp nào như vậy trong một môi trường có quy mô sản xuất giống như sản xuất.

  2. Về cơ bản, bạn đang gán ý nghĩa cho việc thay thế các khóa đánh bại một phần mục đích của chúng trong lý thuyết RDB. Một khóa thay thế theo bản chất của nó không nên có ý nghĩa ngoài việc là một khóa để xác định các bộ dữ liệu trong mối quan hệ đó. Nếu các thực thể có thể có ý nghĩa với nhau và vì vậy cần các khóa không va chạm, có đúng là chúng đang được mô hình riêng biệt hoặc có một cái gì đó bị bỏ sót trong các yêu cầu và / hoặc thiết kế mô hình dữ liệu không?

  3. Bạn đang giới thiệu một điểm tiềm năng của sự thất bại. Điều gì xảy ra nếu việc triển khai không được thiết lập điểm bắt đầu trình tự ban đầu? Sau đó, bạn có lỗi chặn triển khai hoặc triển khai bắt đầu từ cùng một nơi "phá vỡ" tính năng của bạn. Ngoài ra, bạn sẽ làm gì nếu ở đâu đó, ai đó nghĩ rằng đó là một ý tưởng tốt để phân nhánh triển khai (trong sản xuất có lẽ một công ty thuê sẽ thoái một phần của chính họ và cần tách dữ liệu). Điều gì xảy ra nếu hạt giống bằng cách nào đó được thiết lập lại bởi một triển khai nâng cấp xấu hoặc di chuyển khác? [0]

Nếu không có vấn đề nào liên quan đến bạn thì hãy tiếp tục, ý tưởng sẽ không phá vỡ bất cứ điều gì IMO. Tất nhiên có thể có những cách tốt hơn ngay cả khi cách này không sai.


Khi bạn nói "UUID-lite", bạn ngụ ý rằng bạn đã xem xét và giảm giá các UUID. Đó có phải là trường hợp không, và nếu vậy có những lý do đặc biệt để quyết định rằng chúng không phù hợp với dự án này?

Một lý do có thể cho việc không sử dụng UUID là phân mảnh chỉ mục mặc dù tầm quan trọng của điều đó thường được nêu quá nhiều [1] . Câu trả lời của SQL Server cho điều này là "GUID tuần tự" tương đối giống với những gì bạn đang đề xuất nếu chúng tôi giảm giá gán ý nghĩa cho các giá trị chính - có lẽ postgres có tương đương với điều đó không? Tất nhiên, luôn luôn tăng chỉ số có thể có các vấn đề về hiệu suất của riêng họ (sự tranh chấp ở trang cuối, chỉ số ngày càng tăng) trong một số khối lượng công việc lớn rất cụ thể [2] .

Một đối số phổ biến khác chống lại UUID là độ dài khóa: tại sao sử dụng 16 byte cho mỗi giá trị khi 4 hoặc 8 sẽ đủ? Nếu tính duy nhất thực sự là một tài sản hữu ích thì điều này thường sẽ gây ra những lo ngại về kích thước khóa đáng kể. Nếu kích thước khóa là một mối quan tâm nhưng bạn rất vui khi sử dụng INT 64 bit thay vì cần giữ bên trong 32 bit, bạn có thể sử dụng kỹ thuật của mình mà không cần thêm vấn đề tranh chấp tài nguyên chia sẻ tiềm năng bằng cách thực hiện ý tưởng khóa số nguyên có hạt giống của bạn trên mỗi bảng [3] bằng cách sử dụng định nghĩa cột INT IDENTITY(<start>, 1)[4] bình thường , mặc dù một lần nữa, đây là thêm độ phức tạp triển khai (một lượng nhỏ, nhưng chắc chắn không phải bằng không).

Khả năng đọc của con người đôi khi được trích dẫn là một vấn đề, nhưng điều đó quay trở lại việc gán ý nghĩa cho các khóa thay thế.

Khả năng nén là một mối quan tâm ít phổ biến hơn nhưng bạn có thể gặp phải. Đối với bất kỳ thuật toán nén nào, UUID có thể trông giống như dữ liệu ngẫu nhiên (không thể nén) trừ khi bạn đang sử dụng một cái gì đó như UUID tuần tự của máy chủ SQL. Đây có thể là mối quan tâm đối với một tập hợp liên kết rất lớn (hoặc khối dữ liệu khác) có chứa nhiều ID thực thể được cung cấp cho một ứng dụng qua mạng chậm hoặc nếu cần sử dụng một cái gì đó như các tính năng nén chỉ mục của SQL Server, mặc dù cả hai vấn đề này về cơ bản chỉ là khôi phục mối quan tâm về kích thước khóa theo một cách hơi khác và các UUID tuần tự cũng có thể giúp ích ở đây.


[0] điều này cũng có thể xảy ra đối với các cột nhận dạng thông thường, nhưng vì bạn đang sử dụng một tính năng ít phổ biến hơn, bạn sẽ tăng cơ hội cho một DBA ít kinh nghiệm hơn sau khi bạn bỏ lỡ vấn đề nếu nó xảy ra khi bạn đang làm điều gì đó mới mẻ và thú vị nơi khác

[1] Tôi là một người máy chủ SQL, tôi nghi ngờ vấn đề tiềm ẩn là giống nhau ở postgres nhưng đối với tất cả tôi biết nó có thể có bố cục chỉ mục khác nhau có thể làm giảm hiệu ứng.

[2] Mặc dù một lần nữa, đây có thể là SQL Server cụ thể, đặc biệt là ví dụ sau trong hai ví dụ tôi liệt kê

[3] Hai byte trên cùng: thay đổi theo cơ sở dữ liệu, hai tiếp theo: thay đổi theo bảng, bốn còn lại: các bit tăng dần

[4] Đó là cú pháp MS SQL Server, cú pháp postgres có thể khác nhau nhưng bạn nên xem ý tôi là gì và có thể dịch


tl; dr: nếu bạn thấy mình đang phát minh lại bánh xe, hãy đảm bảo rằng tất cả các thiết kế hiện có thực sự không phù hợp trước khi bắt đầu xem xét lý do tại sao một cái mới có thể hoặc không thể.


1
Giới thiệu về [1]: Vì (từ những gì tôi nghe được), vấn đề trong SQL Server chủ yếu là sự phân mảnh của CI khi UUID được sử dụng làm CI, điều đó không thể xảy ra trong Postgres, trong đó tất cả các bảng đều là đống. Các chỉ mục btree không được nhóm, nên hoạt động giống nhau trong cả hai nền tảng. Tôi cho rằng sự phân mảnh được coi là ít vấn đề hơn.
ypercubeᵀᴹ

Postgres cũng có một số loại chỉ mục khác (băm, trigram, gin, gist, brin, ...). Không chắc chắn làm thế nào chúng có thể bị ảnh hưởng hoặc thậm chí nếu chúng có thể hữu ích cho cột UUID.
ypercubeᵀᴹ

Các chỉ mục không được phân cụm (trong SQL Server và thường ở nơi khác) vẫn là b-cây dựa trên phân đoạn do phân chia trang với sự có mặt của dữ liệu đến theo thứ tự ngẫu nhiên. Liên kết giữa UUID và các TCTD trong nhiều cuộc thảo luận về các chiến lược lập chỉ mục của SQL Server là do câu hỏi lựa chọn chính (dữ liệu thực / luôn tăng thay thế / thay thế ngẫu nhiên một cách hiệu quả) và thực tế là phân cụm theo PK thường là sự sắp xếp mặc định của mọi người. Như bạn nói, các chỉ mục dựa trên các cấu trúc khác nhau đáng kể như băm có những cân nhắc hoàn toàn khác nhau.
David Spillett

Cảm ơn đống, câu trả lời rất toàn diện. Nó giúp hướng dẫn suy nghĩ của chúng tôi. Chỉ cần trả lời các câu hỏi của bạn ở trên: một trong những yếu tố khiến chúng tôi quyết định chống lại UUID là việc có ánh xạ tự nhiên tới Java từ các cột ID hiện tại của chúng tôi rất phổ biến thông qua cơ sở mã của chúng tôi. Trên khung thời gian hiện tại của chúng tôi, việc chuyển tất cả mã đó được đánh giá là rắc rối hơn giá trị của nó.
Gấu Burleigh

1
Ah, vấn đề "phụ thuộc bên ngoài hiện tại vượt quá phạm vi của dự án này để thay đổi"! Điều đó có ý nghĩa như một lý do để quyết định chống lại UUID.
David Spillett

3

Chúng tôi đang xem xét sử dụng một chuỗi được chia sẻ để gán id cho các khóa chính cho tất cả các bảng trong cơ sở dữ liệu của chúng tôi. Có khoảng 100 người trong số họ. Chỉ có một cặp vợ chồng được chèn vào thường xuyên và thường xuyên. Chúng tôi muốn loại trừ nó là "một ý tưởng khủng khiếp vì một lý do rõ ràng" trước khi chúng tôi chuyển sang giai đoạn thực sự thử nó và thử nghiệm nó khi tải.

Đó là một ý tưởng khủng khiếp: loại trừ nó. Chỉ cần sử dụng GUID / UUID. Tại sao bạn loại trừ ý tưởng đó? Trong PostgreSQL chúng tôi sử dụng uuid-ossp,

uuid_generate_v4() Hàm này tạo ra một UUID phiên bản 4, được lấy hoàn toàn từ các số ngẫu nhiên.

Như thế này,

CREATE EXTENSION uuid-ossp;
CREATE TABLE f ( f_id uuid DEFAULT uuid_generate_v4() );

Bạn đưa ra rất nhiều giả định trong câu trả lời của mình để nó có giá trị,

  • tốc độ "không nên là một vấn đề"
  • những khoảng trống "không nên là một vấn đề"
  • Kiệt sức id sẽ không xảy ra

Bạn không cần phải thừa nhận điều đó. Điều gì sẽ xảy ra nếu bạn nhận được một DOS trên ID tạo ra một khoảng cách lớn và đẩy rollover trên một mảnh vỡ? Tại sao không chỉ sử dụng giải pháp công nghiệp cho vấn đề này? Không rõ ràng rằng có một nhược điểm duy nhất. Có khả năng tất cả đều thắng. Ngoại trừ một vài byte lưu trữ.


1
+1 để trích dẫn "Đó là một ý tưởng khủng khiếp" quay lại với tôi :)
Gấu Burleigh

0

Một động lực để làm điều này là để chúng ta có thể định nghĩa một loạt các từ điển (chúng ta gọi chúng là phạm vi) và có các từ có thể đọc được của con người được gán cho các id đó, vì vậy chúng tôi muốn đảm bảo rằng các id trong các bảng khác nhau không bao giờ trùng nhau. Vì vậy, trong một phạm vi, id 12345 có thể được gán giá trị "Xanh" và trong phạm vi khác, nó có thể được gán "Verde". (Trên thực tế, chúng tôi không sử dụng nó để quốc tế hóa, nhưng chúng tôi có thể một ngày nào đó).

Theo cách riêng của tôi, tôi sẽ không để đây là lý do để chọn một thiết kế kỳ quặc và dễ vỡ. Nếu bạn đi theo tuyến đường, sẽ không có cách nào để tận dụng các tính năng cơ sở dữ liệu để đảm bảo tính toàn vẹn tham chiếu, ví dụ. Một cách chuẩn hóa truyền thống để đạt được điều tương tự sẽ có lợi ích vượt ra ngoài RI:

create table tab1(tab1_id serial primary key);
create table tab2(tab2_id serial primary key);
create table scope(scope_id serial primary key, scope_name text);
create table scope_tab1(scope_id integer references scope, tab1_id integer references tab1, val text, primary key(scope_id,tab1_id));
insert into scope(scope_name) values ('English'),('French');
insert into tab1(tab1_id) select generate_series(1,5);
insert into tab2(tab2_id) select generate_series(1,5);
insert into scope_tab1(scope_id,tab1_id,val) values (1,1,'Green'),(2,1,'Verde');
select tab1_id
     , (select val from scope_tab1 where scope_id=1 and tab1_id=tab1.tab1_id) val_s1
     , (select val from scope_tab1 where scope_id=2 and tab1_id=tab1.tab1_id) val_s2
from tab1;
tab1_id | val_s1 | val_s2
------: | : ----- | : -----
      1 | Xanh | Verde
      2 | null    | null   
      3 | null    | null   
      4 | null    | null   
      5 | null    | vô giá trị  

dbfiddle ở đây

Động lực khác là làm cho nó dễ dàng có một số triển khai trong trường và biết (bằng cách đặt duy nhất một vài chuỗi chữ số quan trọng nhất của triển khai) rằng các triển khai của chúng tôi sẽ không chồng lấp các khóa chính. (Giống như một GUID lite).

Tôi muốn đề xuất, như những người khác đã làm, sử dụng UUID sẽ tốt hơn nhiều (nghĩa là ít bị lỗi hơn) so với việc phát minh ra một UUID-lite mới.

Tôi vẫn không nghĩ rằng đó là đặt cược tốt nhất của bạn tuy nhiên - bạn không bảo vệ nên không thực sự cần phải có ID không chồng chéo giữa các triển khai mà tôi có thể thấy từ thông tin bạn đã cung cấp. Có lẽ bạn có các cách khác để xác định việc triển khai trong cơ sở dữ liệu hơn là xem ID trong các bảng này.


0

Tôi đã sử dụng mẫu mà bạn đề xuất với một bảng id trung tâm bổ sung mà tất cả các khóa ngoại id khác. Nó làm việc trong một hệ thống sản xuất lớn hoàn toàn tốt.

Tôi nghĩ lý do thực sự để làm điều này là nếu id của bạn có phạm vi ngoài cơ sở dữ liệu của bạn. Ví dụ, trong ví dụ của tôi, các id này liệt kê các công ty và chứng khoán tài chính độc đáo. Bạn có thể hỏi, tại sao không tạo một tập hợp nếu id cho các công ty và bộ thứ hai cho chứng khoán, làm khóa chính tự động trên mỗi bảng? Bởi vì chúng tôi muốn các bản ghi chuỗi thời gian khác đề cập đến chứng khoán hoặc công ty. Vì vậy, bảng chuỗi thời gian nước ngoài được khóa vào bảng id trung tâm.

Với những điều trên, GUID / UUID cũng sẽ hoạt động tốt. Tuy nhiên, các định dạng này thường có kích thước 128 bit có thể có tác động vì được sử dụng trong hầu hết mọi chỉ mục, khóa chính và khóa ngoại trong cơ sở dữ liệu và giảm thiểu vị trí không tuần tự của chúng trong toàn bộ phạm vi id có thể gây khó khăn, dẫn đến để tối ưu hóa hiệu suất chọn. Cơ sở dữ liệu của chúng tôi đã rất hướng đến việc lựa chọn hiệu suất.

GUID / UUID có một lợi thế, đó là chúng dễ dàng hơn nhiều để tạo các quy trình tạo liên kết. Điều đó có nghĩa là, bạn có thể có nhiều quá trình tạo / gán id trong doanh nghiệp của mình mà không cần phối hợp, chỉ bằng cách giả định rằng chúng sẽ không bao giờ đụng độ. Nếu các quy trình tạo id duy nhất của bạn nằm trong cơ sở dữ liệu của bạn, thì điều đó ít đáng quan tâm, nhưng nó đáng được đề cập.

Lưu ý rằng việc tạo UUID phụ thuộc vào việc duy nhất các địa chỉ MAC của bạn, vì vậy bạn sẽ phải chú ý đến điều đó trong môi trường ảo / container.

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.