2009-08-08 66 views
2

后面的故事我有一个存储缓存时间的表,目前有大约1米行。当我用新版本的缓存项目更新表格时,我需要删除旧的缓存项目(约3k项目)。对于这些商品马上就被剔除并不重要,但我会认为这一点,因为当客户拿出缓存物品时,我希望他们获得最新版本。删除表格中的选定行(我如何提高速度)

但是删除仍然是“缓慢”,需要几秒钟才能让最终用户等待,有什么方法可以让这个过程更快吗? ATM即时通讯做一个简单的SQL

DELETE FROM cache where cache_event_id = X 

我的问题是: 我可以让查询更快(我预计缓存表只有扩大规模,所以这个问题会变得更糟)? 我应该让删除SQL运行自己的线程,并与用户可能会暂时旧物品的事实生活?

Pr请求表格的其余信息。

CREATE TABLE [dbo].[cache](
    [cache_id] [int] IDENTITY(1,1) NOT NULL, 
    [cache_name] [nchar](128) NOT NULL, 
    [cache_event_id] [int] NOT NULL, 
    [cache_encounter_id] [int] NOT NULL, 
    [cache_type_id] [tinyint] NOT NULL, 
    [cache_creation_date] [datetime] NOT NULL, 
    [cache_data] [varbinary](max) NOT NULL 
) ON [PRIMARY] 

所有指数由SQL Server事件探查的创建,好像我需要manualy删除旧索引 指数1:

CREATE NONCLUSTERED INDEX [_dta_index_cache_6_366624349__K2_K3_K5_K4_7] ON [dbo]. [cache] 
(
    [cache_name] ASC, 
    [cache_event_id] ASC, 
    [cache_type_id] ASC, 
    [cache_encounter_id] ASC 
) 
INCLUDE ([cache_data]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

指数2://也许actualy不使用

CREATE NONCLUSTERED INDEX [_dta_index_cache_6_366624349__K5_1_2_3_4_6_7] ON [dbo].[cache] 
(
    [cache_type_id] ASC 
) 
INCLUDE ([cache_id], 
[cache_name], 
[cache_event_id], 
[cache_encounter_id], 
[cache_creation_date], 
[cache_data]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

索引3(I假定这个人是USEN删除)

CREATE NONCLUSTERED INDEX [_dta_index_cache_6_366624349__K3] ON [dbo].[cache] 
(
    [cache_event_id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

数据通过BulkCopy类插入到表

数据被取出来(这是最关键的部分)

SqlCommand cmd = new SqlCommand("GetPageCache", connection); 
cmd.CommandType = CommandType.StoredProcedure; 
cmd.Parameters.AddWithValue("@event_id", EventID); // int 
cmd.Parameters.AddWithValue("@encounter_id", EncounterID); // int 
cmd.Parameters.AddWithValue("@type_id", (int)CacheType); //int 
cmd.Parameters.AddWithValue("@cachename", CacheName); // Required in some cases, but 90% this is just a fallback 

回答

4

好消息是这样的:如果DELETE语句总是会删除大约3000行,那么随着表越来越大,情况可能不会变得更糟。

表格的结构可能会对您的DELETE操作需要多长时间以及由于锁定而直接影响用户有多大影响。

索引“帮助”可以很容易地识别~3000注定行的行定位符。但是,这些行必须位于“整个”表(并在表中的每个索引中),然后删除。造成这种情况的一个可能原因是这些3000行在单独的数据页面上遍布表(和索引)。

对于你来说没有一个通用的答案,但你应该仔细看看你的表的组织和索引。可能有一种方法可以更改组织和索引,以避免注定的行在较少的数据页上,并且DELETE的查询计划不会执行3000次单独的查找来访问它们。

如果您发布[cache]的CREATE TABLE和CREATE INDEX语句,我可能会有特定的建议而不是泛化。

补充说明:

这里有一些更多的心思。

您是否有PRIMARY KEY约束?如果没有,你没有聚集索引,这意味着你的表被存储为堆。这并不好,特别是对于经常进行大量活动的桌子。虽然我没有所有的细节,但我也同意Dems。它应该有助于让主键(应该被聚集)(cache_event_id,cache_id)。

另一个瓶颈可能是缓存数据本身。你有三个索引包含它,所以你将它存储在四个地方!我只是猜测,但似乎不太可能你有一次从多行返回cache_data列的查询。因此,您只能将cache_data存储在聚簇索引中(默认情况下聚簇索引包含所有列)。数据库调优顾问很适合给你提供想法,但并不总是一个好主意,完全按照它所说的去做。

典型的cache_data列有多大?如果它几乎总是很大(大小超过8K),它将导致LOB溢出页面的大量活动。当有大量的LOB活动时,我不是专家级的工作负载调优专家,但可能有一些很好的建议资源。有一点要考虑(直到你试图指数改进和实际观察一下内存使用,缓存命中率等)考虑修改,将允许更多的表行以适应页面:

  1. 考虑是否你对于cache_name,需要类型 nchar(128)。 (您 可能,但仔细想想,是不是 总是近128个字节的数据?是 使用统一的必要和 值得额外的空间吗?如果不是,也许 为nvarchar(128)或varchar(128)是 )

  2. 考虑是否有用 将' 行中的大值类型'选项设置为ON。默认为 OFF,这可能会导致你 平均每页数据只有一个表格行 页面,但 页面中没有减少需要LOB溢出页面。 查看sp_spaceused 或sys.dm_db_partition_stats的结果以尝试 来评估此操作。如果每页只有1个 或2行,则可能有助于 更改设置。

+0

使用所有索引和sql查询更新了原始邮件。 – EKS 2009-08-08 21:13:15

1

拥有终端用户等待一个删除调用执行缓存清理似乎不必要。这当然应该是一个后台工作/线程。

或者,您可以使用类似memcached这样的设计来处理缓存读取和过期。

+0

我已经做了mem缓存,sql缓存是一个项目不在内存中时的后备。因为页面生成需要1分钟。 – EKS 2009-08-08 20:22:59

1

清理这些数据应该是异步完成的(通过调度的sql作业,服务,填充数据的工作等)。如果您担心旧查询会在查询之前回到查询状态,然后您有机会删除这些查询,那么您可以实施某种只会返回最新项目的版本控制方案。

+0

版本是一个很酷的想法,每个cache_event_id都会有自己的版本。但一个好主意 – EKS 2009-08-08 20:38:19

2

如果有很多删除,它可能会写很多东西到日志文件中。如果涉及任何关系,则可能花费很长时间来确定是否允许删除记录。

我有一个类似的问题,(但在我的情况下,我需要确保旧记录不可见) 并最终添加了一个名为hidden的位域。因此,'删除'例程实际上只是一个更新语句,将隐藏设置为true,并将查找修改为忽略隐藏的记录。

然后我可以在不影响用户的情况下删除背景中的隐藏记录。

+0

谢谢你有关如何解决它的另一个好主意。 – EKS 2009-08-08 20:54:00

0

我认为这个问题的一部分是设计,但假设我们只想加快删除和改变没有别的?

只有“cache_event_id”的索引确实用于删除,但不是您可能会注意的方式。使用执行计划运行删除,您将在使用索引后看到它,然后使用主键(假设这是集群)。索引基本上就是快速找到需要删除的主键。主键(或任何聚集索引)可以让RDBMS在物理上知道记录的位置,因此可以删除它们。

另外,当记录被删除时,所有索引都需要更新。取决于你有多少指标,以及它们是如何设置的,这可以是labourios。

所以我的两个recomendations是:
1.确保主键或聚集索引“cache_event_id”作为第一场
2.理顺索引的数量,如果可能的话,这可能涉及重写某些查询