rya*_*zec 16 mysql innodb performance primary-key
我正在构建一个 Web 应用程序(项目管理系统),并且在性能方面我一直对此感到疑惑。
我有一个问题表,里面有 12 个外键链接到各种其他表。其中,我需要加入其中的 8 个以从其他表中获取标题字段,以便记录在 Web 应用程序中有意义,但这意味着进行 8 个连接,这似乎非常多,尤其是因为我只是在拉入每个连接有 1 个字段。
现在我还被告知要使用自动递增的主键(除非分片是一个问题,在这种情况下我应该使用 GUID)出于永久性原因,但是使用 varchar(最大长度 32)性能有多糟糕?我的意思是这些表中的大多数可能不会有很多记录(其中大多数应该低于 20)。此外,如果我使用标题作为主键,我将不必在 95% 的时间内进行连接,因此对于 95% 的 sql,我什至会发生任何性能下降(我认为)。我能想到的唯一缺点是我的磁盘空间使用量会更高(但一天下来真的很重要)。
我使用查找表来代替枚举的原因是因为我需要最终用户通过应用程序本身来配置所有这些值。
使用 varchar 作为表的主键的缺点是什么,除非有很多记录?
更新 - 一些测试
所以我决定对这些东西做一些基本的测试。我有 100000 条记录,这些是基本查询:
基本 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
Run Code Online (Sandbox Code Playgroud)
基础 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
Run Code Online (Sandbox Code Playgroud)
我还运行了这些查询,并添加了以下内容:
这些结果,其中:
查询类型:VARCHAR FK 时间/INT FK 时间
基本查询:~4ms / ~52ms
选择特定项目:~140ms / ~250ms
按 i.id 分组:~4ms / ~2.8sec
排序方式:~231ms / ~2sec
限制:~67ms / ~343ms
分组和限制在一起:~504ms / ~2sec
分组、排序和限制:~504ms /~2.3sec
现在我不知道我可以进行什么配置来使一个或另一个(或两者)更快,但似乎 VARCHAR FK 在数据查询中看到更快(有时快得多)。
我想我必须选择这种速度改进是否值得额外的数据/索引大小。
Ste*_*oke 12
我遵循以下主键规则:
a) 不应该有任何商业意义——它们应该完全独立于您正在开发的应用程序,因此我选择自动生成的数字整数。但是,如果您需要额外的列是唯一的,那么创建唯一索引来支持
b) 应该在连接中执行 - 随着主键长度的增加,加入 varchars 与整数的速度大约慢 2 到 3 倍,因此您希望将键作为整数。由于所有计算机系统都是二进制的,我怀疑它是因为字符串被更改为二进制然后与其他非常慢的相比
c) 尽可能使用最小的数据类型——如果你希望你的表只有很少的列,比如美国 52 个州,那么使用可能的最小类型可能是 CHAR(2) 作为 2 位代码,但我仍然会选择 tinyint (128) 对于列 vs 可以达到 20 亿的大整数
此外,如果例如项目名称更改(这并不少见),您将面临将更改从主键级联到其他表的挑战
为您的主键使用顺序自动递增整数,并获得数据库系统提供的内置效率以支持未来的更改
在您的测试中,您不是在比较 varchar 与 int 键的性能差异,而是多个连接的成本。查询 1 个表比加入多个表更快,这并不奇怪。
正如atxdba指出的那样,varchar 主键的一个缺点是增加索引大小。即使您的查找表没有除 PK 之外的任何其他索引(这不太可能,但可能),每个引用查找的表都会在此列上有一个索引。
自然主键的另一个坏处是它们的值可能会发生变化,从而导致大量级联更新。并非所有 RDMS,例如 Oracle,甚至都让您拥有on update cascade. 一般来说,更改主键值被认为是一种非常糟糕的做法。我不想说自然主键总是邪恶的;如果查找值很小并且永远不会改变,我认为这可能是可以接受的。
您可能要考虑的一种选择是实现物化视图。Mysql 不直接支持它,但您可以通过底层表上的触发器实现所需的功能。因此,您将拥有一张包含您需要显示的所有内容的表格。此外,如果性能是可以接受的,不要与目前不存在的问题斗争。
最大的缺点就是重复PK。您指出磁盘空间使用量增加,但需要明确的是,索引大小的增加是您更关心的问题。由于 innodb 是聚集索引,因此每个二级索引内部都会存储 PK 的副本,用于最终查找匹配的记录。
你说表应该是“小”的(20 行确实很小)。如果您有足够的 RAM 将 innodb_buffer_pool_size 设置为等于
select sum(data_length+index_length) from information_schema.tables where engine='innodb';
Run Code Online (Sandbox Code Playgroud)
然后这样做,你可能就会坐得很舒服。作为一般规则,您至少需要保留总系统内存的 30% - 40% 用于其他 mysql 开销和磁盘缓存。这是假设它是一个专用的数据库服务器。如果系统上正在运行其他东西,您还需要考虑它们的要求。
| 归档时间: |
|
| 查看次数: |
28980 次 |
| 最近记录: |