Hiệu suất UUID trong MySQL?


82

Chúng tôi đang xem xét sử dụng các giá trị UUID làm khóa chính cho cơ sở dữ liệu MySQL của mình. Dữ liệu đang được chèn được tạo ra từ hàng chục, hàng trăm hoặc thậm chí hàng nghìn máy tính từ xa và được chèn với tốc độ 100-40.000 lần chèn mỗi giây và chúng tôi sẽ không bao giờ thực hiện bất kỳ cập nhật nào.

Bản thân cơ sở dữ liệu thường sẽ nhận được khoảng 50 triệu bản ghi trước khi chúng tôi bắt đầu thu thập dữ liệu, vì vậy không phải là một cơ sở dữ liệu lớn, nhưng cũng không phải là nhỏ. Chúng tôi cũng đang có kế hoạch chạy trên InnoDB, mặc dù chúng tôi sẵn sàng thay đổi điều đó nếu có một công cụ tốt hơn cho những gì chúng tôi đang làm.

Chúng tôi đã sẵn sàng sử dụng UUID Kiểu 4 của Java, nhưng trong quá trình thử nghiệm đã thấy một số hành vi lạ. Đầu tiên, chúng tôi đang lưu trữ dưới dạng varchar (36) và bây giờ tôi nhận ra rằng chúng tôi tốt hơn nên sử dụng nhị phân (16) - mặc dù tốt hơn bao nhiêu thì tôi không chắc.

Câu hỏi lớn hơn là: dữ liệu ngẫu nhiên này làm hỏng chỉ mục tệ đến mức nào khi chúng ta có 50 triệu bản ghi? Chúng ta sẽ tốt hơn nếu chúng ta sử dụng, chẳng hạn như UUID loại 1 trong đó các bit ngoài cùng bên trái được đánh dấu thời gian? Hoặc có thể chúng ta nên loại bỏ hoàn toàn các UUID và xem xét các khóa chính auto_increment?

Tôi đang tìm kiếm những suy nghĩ / mẹo chung về hiệu suất của các loại UUID khác nhau khi chúng được lưu trữ dưới dạng chỉ mục / khóa chính trong MySQL. Cảm ơn!


2
còn thiếu một chi tiết quan trọng: các khóa chính được tạo bởi máy chủ ghi nhật ký hay do chính máy khách tạo ra?

1
@hop chúng đang được tạo ra bởi 10-1000 khách hàng chèn dữ liệu
Patrick Lightbody

Bạn cần sự độc đáo phổ quát trong kịch bản của mình ở đâu? Lời khuyên của tôi là bám vào auto_increment và sử dụng một trường riêng biệt để mô tả máy tính từ xa gửi dữ liệu. Không cần phải phát minh lại bánh xe ở đây.
Theodore Zographos

Câu trả lời:


35

UUID là một ID phổ biến duy nhất. Đó là phần phổ biến mà bạn nên xem xét ở đây.

Bạn có thực sự cần ID phải là duy nhất trên toàn cầu không? Nếu vậy, UUID có thể là lựa chọn duy nhất của bạn.

Tôi mạnh mẽ sẽ đề nghị rằng nếu bạn làm UUIDs sử dụng, bạn lưu trữ chúng như là một số lượng và không phải là một chuỗi. Nếu bạn có hơn 50 triệu bản ghi, thì việc tiết kiệm dung lượng lưu trữ sẽ cải thiện hiệu suất của bạn (mặc dù tôi không thể nói rõ là bao nhiêu).

Nếu ID của bạn không cần phải là duy nhất trên toàn cầu, thì tôi không nghĩ rằng bạn có thể làm tốt hơn nhiều khi chỉ sử dụng auto_increment, điều này đảm bảo rằng các ID sẽ là duy nhất trong một bảng (vì giá trị sẽ tăng lên mỗi lần)


2
Điểm thú vị; điều này sẽ song song với việc tạo ra các khóa. Tôi tin rằng điều này sẽ làm tăng hiệu suất của quá trình tạo khóa. Tuy nhiên, bạn đang chọn CHÈN hiệu suất thay vì CHỌN hiệu suất nếu bạn sử dụng VARCHAR để lưu trữ UUID. Bạn chắc chắn nhất nên chọn VARBINARY để lưu trữ để đảm bảo hiệu suất CHỌN. Bước bổ sung có thể ảnh hưởng đến hiệu suất INSERT, nhưng bạn sẽ được đền đáp với việc cải thiện hiệu suất CHỌN.
Dancrumb

12
Cuối cùng, chúng tôi đã thực hiện một số đo điểm chuẩn trên dữ liệu thực và GUIDs w / o key khá nhanh, GUIDs w / key rất khủng khiếp (ngay cả khi được lưu trữ dưới dạng BINARY) và int w / AUTO_COMPLETE là nhanh nhất. Tôi nghĩ rằng trong trường hợp của chúng tôi, chúng tôi đã thực sự mất tích rừng từ cây cối, như thế hệ thứ tự dường như không quan trọng so với các chi phí lưu trữ nhiều dữ liệu hơn + có một BTREE thực sự không hấp dẫn do tính ngẫu nhiên của các GUID
Patrick Lightbody

1
lưu trữ dưới dạng số có nghĩa là lưu trữ ở định dạng nhị phân? nhưng định dạng nhị phân không thể đọc được đối với con người. Nó chậm vì số byte lớn của khóa chính uuid? Nếu có, thì tôi có thể lưu trữ số tự động tăng với một cột khác cho uuid. Sau đó, hiệu suất sẽ không bị ảnh hưởng. Tôi nói đúng chứ?
Chamnap

4
Nói một cách chính xác, UUID là duy nhất trên toàn cầu , có nghĩa là nó sẽ không bao giờ xuất hiện ở bất kỳ nơi nào khác trên thế giới. Bạn chỉ cần điều này nếu bạn đang chia sẻ dữ liệu của mình một cách công khai. Đối với việc lưu trữ UUID dưới dạng số, tôi không có ý binaryđịnh dạng. Ý tôi là một số 128 bit, thay vì một chuỗi 288 bit. Ví dụ, từ 'xin chào' trong ASCII là 68 65 6C 6C 6F, là số 448,378,203,247. Lưu trữ chuỗi '68656C6C6F' yêu cầu 10 byte. Số 448.378.203.247 chỉ đòi hỏi 5. Tất cả trong tất cả, trừ khi bạn thực sự cần U đầu tiên trong UUID, bạn không thể làm tốt hơn nhiều soauto_increment
Dancrumb

1
@Chamnap: Đề nghị bạn đặt một câu hỏi Stack Overflow: o)
Dancrumb

77

Trong công việc của tôi, chúng tôi sử dụng UUID làm PK. Điều tôi có thể nói với bạn từ kinh nghiệm là KHÔNG SỬ DỤNG CHÚNG làm PK (nhân tiện, SQL Server).

Đó là một trong những điều mà khi bạn có ít hơn 1000 bản ghi thì không sao, nhưng khi bạn có hàng triệu, đó là điều tồi tệ nhất bạn có thể làm. Tại sao? Bởi vì UUID không tuần tự, vì vậy mỗi khi một bản ghi mới được chèn MSSQL cần phải xem trang chính xác để chèn bản ghi vào, sau đó chèn bản ghi. Hậu quả thực sự tồi tệ của việc này là các trang có tất cả các kích thước khác nhau và chúng bị phân mảnh, vì vậy bây giờ chúng ta phải khử phân mảnh định kỳ.

Khi bạn sử dụng autoincrement, MSSQL sẽ luôn chuyển đến trang cuối cùng và bạn kết thúc với các trang có kích thước bằng nhau (trên lý thuyết) vì vậy hiệu suất để chọn các bản ghi đó tốt hơn nhiều (cũng vì INSERT sẽ không chặn bảng / trang đối với dài).

Tuy nhiên, lợi thế lớn của việc sử dụng UUID làm PK là nếu chúng ta có các cụm DB, sẽ không có xung đột khi hợp nhất.

Tôi muốn giới thiệu mô hình sau: 1. PK INT Identity 2. Cột bổ sung được tạo tự động dưới dạng UUID.

Bằng cách này, quá trình hợp nhất có thể thực hiện được (UUID sẽ là khóa THỰC của bạn, trong khi PK chỉ là thứ tạm thời mang lại cho bạn hiệu suất tốt).

LƯU Ý: Đó là giải pháp tốt nhất là sử dụng NEWSEQUENTIALID (như tôi đã nói trong phần bình luận), nhưng đối với ứng dụng cũ không có nhiều thời gian để cấu trúc lại (và thậm chí tệ hơn, không kiểm soát tất cả các lần chèn), thì không thể thực hiện được. Nhưng thực sự vào năm 2017, tôi muốn nói giải pháp tốt nhất ở đây là NEWSEQUENTIALID hoặc thực hiện Guid.Comb với NHibernate.

Hi vọng điêu nay co ich


Tôi thực sự không biết những thuật ngữ đó có nghĩa là gì, nhưng thực tế là các chỉ mục cần được lập chỉ mục lại hàng tháng. Nếu những gì bạn đề cập đến loại bỏ tác vụ lập chỉ mục lại, tôi không biết nhưng tôi có thể hỏi.
Kat Lim Ruiz

3
Điều mà tôi đang nghĩ là điều này có thể không hiệu quả với mối quan hệ cha mẹ - con cái. Trong trường hợp này, tôi nghĩ bạn phải thêm vào bảng con: parent-pk, parent-Guid. Nếu không, bạn có thể mất các tham chiếu giữa các cơ sở dữ liệu. Tôi đã không nghĩ đến việc này quá nhiều, cũng không phải thực hiện bất kỳ ví dụ, nhưng điều này có thể cần thiết
Kat Lim Ruiz

4
@KatLimRuiz trong sql server bạn có thể sử dụng NEWSEQUENTIALID () technet.microsoft.com/en-us/library/ms189786.aspx để tránh các vấn đề hiệu suất
giammin

Thật vậy, nhưng NEWSEQUENTIALID chỉ hoạt động như DEFAULT. Vì vậy, bạn cần phải thiết kế toàn bộ Dal của bạn xung quanh này, đó là ok cho các dự án mới, nhưng không dễ dàng như vậy cho di sản lớn
Kat Lim Ruiz

@KatLimRuiz thiên tài. Đó là một sự thỏa hiệp lớn
jmgunn87

26

Một điều cần lưu ý là các Tự động gia tăng được tạo ra lần lượt và không thể được giải quyết bằng giải pháp song song. Cuộc chiến để sử dụng UUID cuối cùng đi đến những gì bạn muốn đạt được so với những gì bạn có thể hy sinh.

Về hiệu suất, ngắn gọn :

UUID như ở trên dài 36 ký tự, bao gồm cả dấu gạch ngang. Nếu bạn lưu trữ VARCHAR (36) này, bạn sẽ giảm hiệu suất so sánh đáng kể. Đây là khóa chính của bạn, bạn không muốn nó bị chậm.

Ở cấp độ bit, UUID là 128 bit, có nghĩa là nó sẽ vừa với 16 byte, lưu ý rằng điều này không thể đọc được cho con người, nhưng nó sẽ giữ cho bộ nhớ thấp và chỉ lớn hơn 4 lần so với int 32 bit, hoặc 2 lớn hơn lần int 64 bit. Tôi sẽ sử dụng VARBINARY (16) Về mặt lý thuyết, điều này có thể hoạt động mà không tốn nhiều chi phí.

Tôi khuyên bạn nên đọc hai bài viết sau:

Tôi nghĩ giữa hai, họ trả lời câu hỏi của bạn.


2
Trên thực tế, tôi đã đọc cả hai bài báo đó trước khi đăng câu hỏi này, và tôi vẫn chưa có câu trả lời tốt ở đây. Ví dụ: không nói về UUIDS loại 1 với loại 4 :(
Patrick Lightbody

Công bằng mà nói, tôi đã cập nhật câu trả lời của mình một cách liên lạc. Tuy nhiên, tôi không nghĩ rằng nó cung cấp quá nhiều thông tin chi tiết.
Kyle Rosendo

@Patrick: bạn đặt quá nhiều chủ đề khác nhau vào câu hỏi của mình.

1
9 năm sau, nhưng cũng cần lưu ý cho hậu thế rằng không giống như các ID số nguyên, các ứng dụng có thể tạo UUID một cách an toàn, loại bỏ hoàn toàn việc tạo ra khỏi cơ sở dữ liệu. Thao tác các UUID để tối ưu hóa hiệu suất (dựa trên dấu thời gian nhưng được sửa đổi để chúng có thể được sắp xếp nguyên bản) dễ dàng hơn đáng kể trong bất kỳ ngôn ngữ nào khác ngoài SQL. May mắn thay, hầu hết tất cả các cơ sở dữ liệu ngày nay (bao gồm MySQL) đều xử lý các khóa chính của UUID tốt hơn nhiều so với trước đây.
Miles Elam

5

Tôi có xu hướng tránh UUID đơn giản vì nó là một khó khăn để lưu trữ và khó sử dụng làm khóa chính nhưng có những lợi thế. Cái chính là chúng ĐỘC ĐÁO.

Tôi thường giải quyết vấn đề và tránh UUID bằng cách sử dụng các trường khóa kép.

COLLECTOR = DUY NHẤT ĐƯỢC GỬI VÀO MỘT MÁY

ID = BẢN GHI ĐƯỢC THU THẬP BỞI BỘ SƯU TẬP (trường auto_inc)

Điều này cung cấp cho tôi hai điều. Tốc độ của các trường tự động nhập và tính duy nhất của dữ liệu được lưu trữ ở vị trí trung tâm sau khi được thu thập và nhóm lại với nhau. Tôi cũng biết trong khi duyệt dữ liệu nơi dữ liệu được thu thập thường khá quan trọng đối với nhu cầu của tôi.

Tôi đã gặp nhiều trường hợp trong khi xử lý các tập dữ liệu khác cho khách hàng mà họ đã quyết định sử dụng UUID nhưng sau đó vẫn có một trường để thu thập dữ liệu, điều này thực sự rất lãng phí công sức. Chỉ cần sử dụng hai (hoặc nhiều trường nếu cần) vì khóa của bạn thực sự hữu ích.

Tôi vừa thấy quá nhiều lượt truy cập hiệu suất sử dụng UUID. Họ cảm thấy như một kẻ lừa đảo ...


3

Thay vì tạo tập trung các khóa duy nhất cho mỗi lần chèn, làm thế nào về việc phân bổ các khối khóa cho các máy chủ riêng lẻ? Khi họ dùng hết khóa, họ có thể yêu cầu một khối mới. Sau đó, bạn giải quyết vấn đề chi phí bằng cách kết nối cho mỗi lần chèn.

Keyserver duy trì id khả dụng tiếp theo

  • Máy chủ 1 yêu cầu khối id.
  • Keyserver trả về (1,1000)
    Máy chủ 1 có thể chèn 1000 bản ghi cho đến khi nó cần yêu cầu một khối mới
  • Máy chủ 2 yêu cầu khối chỉ mục.
  • Keyserver trả về (1001,2000)
  • Vân vân...

Bạn có thể đưa ra một phiên bản phức tạp hơn, trong đó máy chủ có thể yêu cầu số lượng khóa cần thiết hoặc trả lại các khối chưa sử dụng cho máy chủ khóa, sau đó tất nhiên sẽ cần duy trì bản đồ các khối đã sử dụng / chưa sử dụng.


Gợi ý lý thuyết thú vị. Điều này sẽ phức tạp để quản lý trong thực tế. Một giải pháp thực tế hơn có lẽ sẽ là câu trả lời được đặt ra bởi schworak.
Simon East

2

Tôi sẽ gán cho mỗi máy chủ một ID số theo cách giao dịch. Sau đó, mỗi bản ghi được chèn vào sẽ chỉ tự động tăng bộ đếm của chính nó. Sự kết hợp của ServerID và RecordID sẽ là duy nhất. Trường ServerID có thể được lập chỉ mục và hiệu suất lựa chọn trong tương lai dựa trên ServerID (nếu cần) có thể tốt hơn nhiều.


2

Câu trả lời ngắn gọn là nhiều cơ sở dữ liệu gặp vấn đề về hiệu suất (đặc biệt với khối lượng INSERT cao) do xung đột giữa phương pháp lập chỉ mục của chúng và entropy có chủ ý của UUID trong các bit bậc cao. Có một số cách hack phổ biến:

  • chọn một loại chỉ mục khác (ví dụ: không có trên MSSQL) mà không thấy phiền
  • trộn dữ liệu để di chuyển entropy xuống các bit bậc thấp hơn (ví dụ: sắp xếp lại thứ tự các byte của V1 UUID trên MySQL)
  • đặt UUID thành khóa phụ với khóa chính int tự động tăng

... nhưng tất cả đều là hack - và có thể là những bản hack dễ vỡ.

Câu trả lời tốt nhất nhưng không may là câu trả lời chậm nhất là yêu cầu nhà cung cấp của bạn cải thiện sản phẩm của họ để họ có thể xử lý UUID làm khóa chính giống như bất kỳ loại nào khác. Họ không nên ép bạn tung ra bản hack nửa vời của riêng bạn để bù đắp cho việc họ không giải quyết được những gì đã trở thành trường hợp sử dụng phổ biến và sẽ chỉ tiếp tục phát triển.


1

Còn đối với một số UID được làm thủ công thì sao? Cung cấp cho mỗi máy chủ trong số hàng nghìn máy chủ một ID và đặt khóa chính trở thành khóa kết hợp của tự động gia tăng, MachineID ???


Tôi đã nghĩ về điều đó và có thể cần chạy một số điểm chuẩn. Ngay cả một chuỗi cục bộ tạm thời trên mỗi máy trong số 1000 máy, kết hợp với dấu thời gian, có thể là đủ. Ví dụ: machine_id + temp_seq + timestamp
Patrick Lightbody

Có thể có một temp_sequence đặt lại mỗi lần đánh dấu thời gian không? Tôi không chắc.
MindStalker

1

Vì khóa chính được tạo phi tập trung, bạn không có tùy chọn sử dụng auto_increment.

Nếu bạn không phải ẩn danh tính của các máy từ xa, hãy sử dụng UUID Loại 1 thay vì UUID. Chúng dễ tạo hơn và ít nhất có thể không ảnh hưởng đến hiệu suất của cơ sở dữ liệu.

Tương tự đối với varchar (thực sự là char) so với binary: nó chỉ có thể giúp ích cho vấn đề. Nó có thực sự quan trọng không, hiệu suất được cải thiện đến mức nào?

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.