Mysql int vs varchar làm khóa chính (InnoDB Storage Engine?


13

Tôi đang xây dựng một ứng dụng web (hệ thống quản lý dự án) và tôi đã tự hỏi về điều này khi nói đến hiệu suất.

Tôi có một bảng Vấn đề và bên trong nó có 12 khóa ngoại liên kết với nhiều bảng khác. Trong số đó, 8 trong số đó tôi sẽ cần tham gia để lấy trường tiêu đề từ các bảng khác để bản ghi có ý nghĩa trong ứng dụng web nhưng sau đó có nghĩa là thực hiện 8 lần tham gia có vẻ thực sự quá mức đặc biệt là vì tôi chỉ tham gia 1 trường cho mỗi tham gia.

Bây giờ tôi cũng đã được yêu cầu sử dụng khóa chính tăng tự động (trừ khi shending là mối lo ngại trong trường hợp nào tôi nên sử dụng GUID) vì lý do lâu dài nhưng việc sử dụng varchar (max length 32) có tệ đến mức nào? Ý tôi là hầu hết các bảng này có thể sẽ không có nhiều hồ sơ (hầu hết trong số chúng phải dưới 20). Ngoài ra, nếu tôi sử dụng tiêu đề làm khóa chính, tôi sẽ không phải tham gia 95% thời gian vì vậy với 95% sql, tôi thậm chí sẽ xảy ra bất kỳ cú đánh hiệu suất nào (tôi nghĩ). Nhược điểm duy nhất tôi có thể nghĩ là tôi có là tôi sẽ sử dụng dung lượng ổ đĩa cao hơn (nhưng xuống một ngày thì đó thực sự là một vấn đề lớn).

Lý do tôi sử dụng bảng tra cứu cho rất nhiều thứ này thay vì enums là vì tôi cần tất cả các giá trị này để người dùng cuối có thể định cấu hình thông qua chính ứng dụng.

Nhược điểm của việc sử dụng varchar làm khóa chính cho bảng không ngoại trừ có nhiều bản ghi là gì?

CẬP NHẬT - Một số bài kiểm tra

Vì vậy, tôi quyết định làm một số thử nghiệm cơ bản về công cụ này. Tôi có 100000 hồ sơ và đây là các truy vấn cơ bản:

Truy vấn cơ sở VARCHAR FK

SELECT i.id, i.key, i.title, i.reporterUserUsername, i.assignedUserUsername, i.projectTitle, 
i.ProjectComponentTitle, i.affectedProjectVersionTitle, i.originalFixedProjectVersionTitle, 
i.fixedProjectVersionTitle, i.durationEstimate, i.storyPoints, i.dueDate, 
i.issueSecurityLevelId, i.creatorUserUsername, i.createdTimestamp, 
i.updatedTimestamp, i.issueTypeId, i.issueStatusId
FROM ProjectManagement.Issues i

Truy vấn cơ sở INT FK

SELECT i.id, i.key, i.title, ru.username as reporterUserUsername, 
au.username as assignedUserUsername, p.title as projectTitle, 
pc.title as ProjectComponentTitle, pva.title as affectedProjectVersionTitle, 
pvo.title as originalFixedProjectVersionTitle, pvf.title as fixedProjectVersionTitle, 
i.durationEstimate, i.storyPoints, i.dueDate, isl.title as issueSecurityLevelId, 
cu.username as creatorUserUsername, i.createdTimestamp, i.updatedTimestamp, 
it.title as issueTypeId, is.title as issueStatusId
FROM ProjectManagement2.Issues i
INNER JOIN ProjectManagement2.IssueTypes `it` ON it.id = i.issueTypeId
INNER JOIN ProjectManagement2.IssueStatuses `is` ON is.id = i.issueStatusId
INNER JOIN ProjectManagement2.Users `ru` ON ru.id = i.reporterUserId
INNER JOIN ProjectManagement2.Users `au` ON au.id = i.assignedUserId
INNER JOIN ProjectManagement2.Users `cu` ON cu.id = i.creatorUserId
INNER JOIN ProjectManagement2.Projects `p` ON p.id = i.projectId
INNER JOIN ProjectManagement2.`ProjectComponents` `pc` ON pc.id = i.projectComponentId
INNER JOIN ProjectManagement2.ProjectVersions `pva` ON pva.id = i.affectedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvo` ON pvo.id = i.originalFixedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvf` ON pvf.id = i.fixedProjectVersionId
INNER JOIN ProjectManagement2.IssueSecurityLevels isl ON isl.id = i.issueSecurityLevelId

Tôi cũng đã chạy các truy vấn này với các bổ sung sau:

  • Chọn mục cụ thể (trong đó i.key = 43298)
  • Nhóm bởi i.id
  • Sắp xếp theo (it.title cho int FK, i.suTypeId cho varchar FK)
  • Giới hạn (50000, 100)
  • Nhóm và giới hạn cùng nhau
  • Nhóm, thứ tự và giới hạn cùng nhau

Kết quả cho những điều này:

NHIỀU LOẠI: VARCHAR FK TIME / INT FK TIME


Truy vấn cơ sở: ~ 4ms / ~ 52ms

Chọn mục cụ thể: ~ 140ms / ~ 250ms

Nhóm theo i.id: ~ 4ms / ~ 2.8 giây

Đặt hàng theo: ~ 231ms / ~ 2 giây

Giới hạn: ~ 67ms / ~ 343ms

Nhóm và giới hạn cùng nhau: ~ 504ms / ~ 2 giây

Nhóm, thứ tự và giới hạn cùng nhau: ~ 504ms /~2.3 giây

Bây giờ tôi không biết cấu hình nào tôi có thể thực hiện để làm cho cái này hoặc cái kia (hoặc cả hai) nhanh hơn nhưng có vẻ như VARCHAR FK thấy nhanh hơn trong các truy vấn dữ liệu (đôi khi nhanh hơn rất nhiều).

Tôi đoán tôi phải lựa chọn liệu cải thiện tốc độ đó có xứng đáng với kích thước chỉ mục / dữ liệu bổ sung hay không.


Thử nghiệm của bạn chỉ ra một cái gì đó. Tôi cũng sẽ kiểm tra các cài đặt InnoDB khác nhau (nhóm bộ đệm, v.v.) vì các cài đặt MySQL mặc định không thực sự được tối ưu hóa cho InnoDB.
ypercubeᵀᴹ

Bạn cũng nên kiểm tra hiệu năng Chèn / Cập nhật / Xóa vì điều này cũng có thể bị ảnh hưởng bởi kích thước chỉ mục. Khóa một cụm của mỗi bảng InnoDB thường là PK và cột (PK) này cũng được bao gồm trong mọi chỉ mục khác. Đây có lẽ là một nhược điểm lớn của các PK lớn trong InnoDB và nhiều chỉ mục trên bảng (nhưng 32 byte là khá trung bình, không lớn, vì vậy nó có thể không phải là vấn đề).
ypercubeᵀᴹ

Bạn cũng nên kiểm tra với các bảng lớn hơn (trong phạm vi khoảng 10 - 100M hàng hoặc lớn hơn), nếu bạn dự đoán các bảng của mình có thể tăng cao hơn 100K (không thực sự lớn).
ypercubeᵀᴹ

@ypercube Vì vậy, tôi tăng dữ liệu lên 2 triệu và câu lệnh chọn cho int FK trở nên chậm hơn theo cấp số nhân trong đó khóa ngoại varchar vẫn khá ổn định. Một suy nghĩ rằng varchar đáng giá trong các yêu cầu đĩa / bộ nhớ để đạt được trong các truy vấn chọn (điều này sẽ rất quan trọng trên bảng cụ thể này và một vài cái khác).
ryanzec

Chỉ cần kiểm tra cài đặt db (và đặc biệt là InnoDB) của bạn trước khi đưa ra kết luận. Với các bảng tham chiếu nhỏ, tôi sẽ không mong đợi tăng theo cấp số nhân
ypercubeᵀᴹ

Câu trả lời:


9

Tôi làm theo các quy tắc sau cho các khóa chính:

a) Không nên có bất kỳ ý nghĩa kinh doanh nào - chúng nên hoàn toàn độc lập với ứng dụng bạn đang phát triển, do đó tôi sử dụng các số nguyên được tạo tự động số. Tuy nhiên, nếu bạn cần các cột bổ sung là duy nhất thì hãy tạo các chỉ mục duy nhất để hỗ trợ điều đó

b) Nên thực hiện trong các phép nối - nối với varchars vs số nguyên chậm hơn khoảng 2 đến 3 lần khi chiều dài của khóa chính tăng lên, vì vậy bạn muốn có các khóa của mình dưới dạng số nguyên. Vì tất cả các hệ thống máy tính là nhị phân, tôi nghi ngờ vì chuỗi của nó được thay đổi thành nhị phân sau đó so với các hệ thống khác rất chậm

c) Sử dụng loại dữ liệu nhỏ nhất có thể - nếu bạn cho rằng bảng của mình có rất ít cột nói 52 trạng thái của Hoa Kỳ, thì hãy sử dụng loại nhỏ nhất có thể là CHAR (2) cho mã 2 chữ số, nhưng tôi vẫn sẽ sử dụng một số nhỏ (128) cho cột so với số nguyên lớn có thể lên tới 2 tỷ

Ngoài ra, bạn sẽ có một thách thức với xếp tầng thay đổi của bạn từ các khóa chính sang các bảng khác nếu ví dụ tên dự án thay đổi (không phổ biến)

Sử dụng các số nguyên tăng tự động tuần tự cho các khóa chính của bạn và đạt được hiệu quả sẵn có mà các hệ thống cơ sở dữ liệu cung cấp với sự hỗ trợ cho các thay đổi trong tương lai


1
Chuỗi không được thay đổi thành nhị phân; chúng được lưu trữ trong hệ nhị phân ngay từ đầu. Làm thế nào khác họ sẽ được lưu trữ? Có lẽ bạn đang nghĩ về các hoạt động để cho phép so sánh không phân biệt chữ hoa chữ thường?
Jon của tất cả các giao dịch

6

Trong các thử nghiệm của bạn, bạn không so sánh sự khác biệt về hiệu năng của varchar và int key mà là chi phí của nhiều phép nối. Không có gì đáng ngạc nhiên khi truy vấn 1 bảng nhanh hơn so với việc tham gia nhiều bảng.
Một nhược điểm của khóa chính varchar là tăng kích thước chỉ mục như atxdba đã chỉ ra. Ngay cả khi bảng tra cứu của bạn không có bất kỳ chỉ mục nào khác ngoại trừ PK (điều này khá khó xảy ra, nhưng có thể), mỗi bảng tra cứu tham chiếu sẽ có một chỉ mục trên cột này.
Một điều tồi tệ khác về các khóa chính tự nhiên, là giá trị của chúng có thể thay đổi gây ra nhiều cập nhật xếp tầng. Không phải tất cả RDMS, ví dụ như Oracle, thậm chí cho phép bạn cóon update cascade. Nói chung, thay đổi giá trị khóa chính được coi là một thực tiễn rất xấu. Tôi không muốn nói rằng các khóa chính tự nhiên luôn xấu; nếu giá trị tra cứu là nhỏ và không bao giờ thay đổi, tôi nghĩ có thể chấp nhận được.

Một tùy chọn bạn có thể muốn xem xét là triển khai chế độ xem cụ thể hóa. Mysql không hỗ trợ trực tiếp, nhưng bạn có thể đạt được chức năng mong muốn với các kích hoạt trên các bảng bên dưới. Vì vậy, bạn sẽ có một bảng có mọi thứ bạn cần để hiển thị. Ngoài ra, nếu hiệu suất có thể chấp nhận được, đừng đấu tranh với vấn đề không tồn tại vào lúc này.


3

Nhược điểm lớn nhất là sự lặp lại của PK. Bạn đã chỉ ra sự gia tăng trong việc sử dụng dung lượng ổ đĩa nhưng để rõ ràng kích thước chỉ mục tăng là mối quan tâm lớn hơn của bạn. Vì innodb là một chỉ mục được nhóm, mỗi chỉ mục phụ lưu trữ bên trong một bản sao của PK mà nó sử dụng để cuối cùng tìm thấy các bản ghi khớp.

Bạn nói rằng các bảng được dự kiến ​​là "nhỏ" (20 hàng thực sự rất nhỏ). Nếu bạn có đủ RAM để đặt innodb_buffer_pool_size bằng với

select sum(data_length+index_length) from information_schema.tables where engine='innodb';

Sau đó, làm điều đó và bạn có thể sẽ ngồi đẹp. Như một quy tắc chung mặc dù bạn muốn để lại ít nhất 30% - 40% tổng bộ nhớ hệ thống cho các bộ nhớ cache và bộ nhớ cache khác của mysql. Và đó là giả sử đó là một máy chủ DB chuyên dụng. Nếu bạn có những thứ khác đang chạy trên hệ thống, bạn cũng cần phải xem xét các yêu cầu của chúng.


1

Ngoài câu trả lời @atxdba - đã giải thích cho bạn lý do tại sao sử dụng số sẽ tốt hơn cho không gian đĩa tôi muốn thêm hai điểm:

  1. Nếu bảng Vấn đề của bạn dựa trên VARCHAR FK và giả sử bạn có 20 VARCHAR (32) FK nhỏ, bản ghi của bạn có thể có độ dài 20x32byte, trong khi các bảng khác được đề cập của bạn là các bảng tra cứu, do đó INT FK có thể là TINYINT FK. cho 20 trường một bản ghi 20 byte. Tôi biết trong vài trăm hồ sơ, nó sẽ không thay đổi nhiều nhưng khi bạn đạt tới vài triệu thì tôi đoán bạn sẽ đánh giá cao việc tiết kiệm không gian

  2. Đối với vấn đề tốc độ, tôi sẽ cân nhắc sử dụng các chỉ mục che phủ, vì dường như đối với truy vấn này, bạn không truy xuất được lượng dữ liệu đó từ các bảng tra cứu mà tôi sẽ thực hiện để kiểm tra chỉ mục và thực hiện lại một lần nữa để kiểm tra VARCHAR FK / W / COVERING của bạn INDEX VÀ INT FK thường xuyên.

Hy vọng nó có thể giúp,

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.