性能考虑:在多个表中分散行与将所有行集中在一个表中
Posted
技术标签:
【中文标题】性能考虑:在多个表中分散行与将所有行集中在一个表中【英文标题】:Performance consideration: Spread rows in multiple tables vs concentrate all rows in one table 【发布时间】:2009-07-17 09:36:28 【问题描述】:。
嗨。
我需要在 SQL DB 中记录有关应用程序中执行的每个步骤的信息。 有某些表,我希望日志应该与: 产品 - 应在创建产品时记录更改等。 顺序 - 同上 运输 - 相同 等等等等等等。
需要经常检索数据。
我对如何做到这一点没有什么想法:
-
有一个日志表,其中包含所有这些表的列,然后当我想在 UI 中表示某个产品的数据时,会从 Log 中选择 *,其中 LogId = Product.ProductId。
我知道有很多列可能会很有趣,但我有这种感觉,性能会更好。另一方面,此表中会有大量行。
每种日志类型都有很多日志表(ProductLogs、OrderLogs 等)在行数较少的表中搜索时更快(我错了吗?)。
根据声明号。 1,我可以做第二个多对一表,它将具有 LogId、TableNameId 和 RowId 列,并将日志行引用到数据库中的许多表行,而不是有一个 UDF 来检索数据(例如日志 id 234属于 CustomerId 345 处的表 Customer 和 productId = RowId) 的 Product 表;我认为这是最好的方法,但同样,可能会有大量的行,它会减慢搜索速度吗?或者这应该是怎么做的,怎么说呢?...
以上列表中的第 3 位示例:
CREATE TABLE [dbo].[Log](
[LogId] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NULL,
[Description] [varchar](1024) NOT NULL,
CONSTRAINT [PK_Log] PRIMARY KEY CLUSTERED
(
[LogId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Log] WITH CHECK ADD CONSTRAINT [FK_Log_Table] FOREIGN KEY([UserId])
REFERENCES [dbo].[Table] ([TableId])
GO
ALTER TABLE [dbo].[Log] CHECK CONSTRAINT [FK_Log_Table]
---------------------------------------------------------------------
CREATE TABLE [dbo].[LogReference](
[LogId] [int] NOT NULL,
[TableName] [varchar](32) NOT NULL,
[RowId] [int] NOT NULL,
CONSTRAINT [PK_LogReference] PRIMARY KEY CLUSTERED
(
[LogId] ASC,
[TableName] ASC,
[RowId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[LogReference] WITH CHECK ADD CONSTRAINT [FK_LogReference_Log] FOREIGN KEY([LogId])
REFERENCES [dbo].[Log] ([LogId])
GO
ALTER TABLE [dbo].[LogReference] CHECK CONSTRAINT [FK_LogReference_Log]
---------------------------------------------------------------------
CREATE FUNCTION GetLog
(
@TableName varchar(32),
@RowId int
)
RETURNS
@Log TABLE
(
LogId int not null,
UserId int not null,
Description varchar(1024) not null
)
AS
BEGIN
INSERT INTO @Log
SELECT [Log].LogId, [Log].UserId, [Log].Description
FROM [Log] INNER JOIN
LogReference ON [Log].LogId = LogReference.LogId
WHERE (LogReference.TableName = @TableName) AND (LogReference.RowId = @RowId)
RETURN
END
GO
【问题讨论】:
【参考方案1】:小心预优化数据库。大多数数据库都相当快并且有些复杂。您想先进行效率测试。
第二次将所有内容放在一个表中,您想要的结果更有可能在缓存中,这将极大地提高性能。不幸的是,它也使您更有可能必须搜索一张巨大的桌子才能找到您要找的东西。这可以通过索引部分解决,但索引并不是免费的(它们使编写成本更高,一方面)。
我的建议是进行测试,看看性能是否真的很重要,然后测试不同的场景,看看哪个是最快的。
【讨论】:
我对开发时间有点严格,您认为最推荐的方式是什么? 最干净和最有效的方法是列表中的第 3 位,问题是否会太慢。【参考方案2】:如果您谈论的是大量数据(数百万行以上),那么您将受益于使用不同的表来存储它们。
例如基本示例 5000 万条日志条目,假设有 5 种不同“类型”的日志表 拥有 5 x 1000 万行表比 1 x 5000 万行表更好
单个表的 INSERT 性能会更好 - 每个表上的索引会更小,因此作为插入操作的一部分更新/维护会更快/更容易
单个表的 READ 性能会更好 - 要查询的数据更少,要遍历的索引更小。此外,听起来您需要存储一个额外的列来识别记录的日志条目类型(产品、运输......)
在较小的表上进行维护的痛苦较小(统计信息、索引碎片整理/重建等)本质上,这是关于分区数据。从 SQL 2005 开始,它内置了对分区的支持(请参阅here),但您需要企业版,它基本上允许您将数据分区到一个表中以提高性能(例如,您将拥有一个日志表,然后定义其中的数据如何分区)
我最近听了一位 eBay 架构师的采访,他强调了在需要性能和可扩展性时分区的重要性,根据我的经验,我非常同意。
【讨论】:
【参考方案3】:出于以下几个原因,我肯定会选择选项 3:
数据应该在表的字段中,而不是表名(选项 2)或字段名(选项 1)。这样,数据库变得更容易使用和维护。
较窄的表格通常表现更好。行数对性能的影响小于字段数。
如果每个表都有一个字段(选项 1),当只有少数表受操作影响时,您可能会得到很多空字段。
【讨论】:
你看,我同意你的观点,这个问题特别是关于插入和搜索,我不在乎检索会很慢。【参考方案4】:尝试以某种方式实现您的数据访问层,以便您可以在需要时从一种数据库模型更改为另一种 - 这样您只需选择一个并担心以后会影响性能。
如果不进行一些性能测试并准确了解您将要获得的负载类型,将很难对其进行优化,因为性能取决于许多因素,例如读取次数、写入次数,以及读写是否可能发生冲突并导致锁定。
顺便说一句,我更喜欢选项 1 - 它最简单,您可以进行许多调整来帮助解决您可能遇到的各种问题。
【讨论】:
以上是关于性能考虑:在多个表中分散行与将所有行集中在一个表中的主要内容,如果未能解决你的问题,请参考以下文章