4

性能考虑:在多个表中分散行与将所有行集中在一个表中。

你好。

我需要在 SQL DB 中记录有关应用程序中进行的每个步骤的信息。有某些表,我希望日志应该与:产品 - 应该在创建产品时记录更改等。订单 - 与上述相同 运输 - 相同等等等等等。

需要经常检索数据。

我对如何做到这一点没有什么想法:

  1. 有一个包含所有这些表的列的日志表,然后当我想在 UI 中表示某个产品的数据时,会从 Log 中选择 *,其中 LogId = Product.ProductId。我知道有很多列可能会很有趣,但我有这种感觉,性能会更好。另一方面,该表中将有大量行。
  2. 每种日志类型(ProductLogs、OrderLogs 等)都有很多日志表。我真的不喜欢这个想法,因为它不一致并且有许多具有相同结构的表没有意义,但是(?)搜索时可能会更快在行数较少的表中(我错了吗?)。
  3. 根据声明没有。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
4

4 回答 4

3

小心预优化数据库。大多数数据库都相当快并且有些复杂。您想首先进行效率测试。

其次,将所有内容放在一个表中,这样您想要的结果更有可能在缓存中,这将极大地提高性能。不幸的是,它也使您更有可能必须搜索一张巨大的桌子才能找到您要找的东西。这可以通过索引部分解决,但索引并不是免费的(它们使编写变得更加昂贵,一方面)。

我的建议是进行测试,看看性能是否真的很重要,然后测试不同的场景,看看哪个是最快的。

于 2009-07-17T09:43:27.430 回答
2

如果您谈论的是大量数据(数百万行+),那么您将受益于使用不同的表来存储它们。

例如基本示例 5000 万个日志条目,假设 5 种不同“类型”的日志表 最好有 5 x 1000 万行表而不是 1 x 5000 万行表

  • 单个表的 INSERT 性能会更好 - 每个表上的索引将更小,并且作为插入操作的一部分更新/维护更快速/更容易

  • 单个表的读取性能会更好 - 要查询的数据更少,要遍历的索引更小。此外,听起来您需要存储一个额外的列来识别记录的日志条目类型(产品、运输......)

  • 较小表上的维护不那么痛苦(统计、索引碎片整理/重建等)

本质上,这是关于分区数据。从 SQL 2005 开始,它内置了对分区的支持(请参阅此处),但您需要 Enterprise Edition,它基本上允许您将数据分区到一个表中以提高性能(例如,您将拥有一个 Log 表,然后定义其中的数据如何分区)

我最近听了一位 eBay 架构师的采访,他强调了在需要性能和可扩展性时分区的重要性,根据我的经验,我非常同意。

于 2009-07-17T09:53:58.203 回答
1

我肯定会选择选项 3,原因如下:

数据应该在表的字段中,而不是表名(选项 2)或字段名(选项 1)。这样,数据库变得更容易使用和维护。

较窄的表通常性能更好。行数对性能的影响小于字段数。

如果每个表都有一个字段(选项 1),当只有少数表受操作影响时,您可能会得到很多空字段。

于 2009-07-17T10:18:12.493 回答
0

尝试以某种方式实现您的数据访问层,以便您可以在需要时从一个数据库模型更改为另一个 - 这样您只需选择一个并担心以后的性能影响。

如果不进行一些性能测试并准确了解您将要获得的负载类型,将很难优化,因为性能取决于许多因素,例如读取次数、写入次数以及是否或不读取和写入可能会发生冲突并导致锁定。

我的偏好是选项 1 顺便说一句 - 它最简单,您可以进行许多调整来帮助解决您可能遇到的各种问题。

于 2009-07-17T09:49:15.670 回答