代码之家  ›  专栏  ›  技术社区  ›  EKS

删除表中选定的行(如何提高速度)

  •  2
  • EKS  · 技术社区  · 16 年前

    back-story我有一个存储缓存时间的表,目前有大约100万行。当我用缓存项的新版本更新表时,我需要删除旧的缓存项(大约3K项)。这些项目立即被禁止并不重要,但我更喜欢它,因为当客户机取出缓存的项目时,我希望他们得到最新的版本。

    但是删除过程仍然“到”缓慢,需要几秒钟的时间让最终用户等待,有没有办法让这一过程更快?ATM IM执行简单的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事件探查器创建的,似乎我需要手动删除旧索引。 索引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://可能实际未使用

    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(我假设这个用于删除)

    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
    
    5 回复  |  直到 16 年前
        1
  •  4
  •   Steve Kass    16 年前

    好消息是:如果DELETE语句总是删除大约3000行,则情况可能不会随着表的增大而变得更糟。

    表的结构可能会对删除操作所用的时间以及由于锁而直接影响用户的程度产生很大影响。

    索引“有助于”识别约3000行的行定位器。但是,这些行必须位于“整个”表中(以及表中的每个索引中),然后删除。这个速度慢的一个可能原因是这3000行分布在表(和索引)的不同数据页上。

    没有一个适合所有人的答案,但是您应该仔细查看表的组织和索引。可能有一种方法可以改变组织和索引,使注定要删除的行在更少的数据页上,并且删除的查询计划不会执行3000个单独的查找来访问它们。

    如果您发布create table并为[cache]创建index语句,我可能会有特定的建议,而不是泛化。

    附加说明:

    这里还有一些想法。

    您有主键约束吗? 如果没有,则没有聚集索引,这意味着表存储为堆。这不太好,尤其是对于一个有很多活动的桌子来说。虽然我没有所有的细节,但我也同意下面的DEM。它应该有助于将主键(应该是集群的)打开(cache_event_id,cache_id)。

    另一个瓶颈可能是缓存数据本身。您已经将它包含在三个索引中,所以您将它存储在四个位置!我只是在猜测,但似乎您不太可能有同时从多行返回cache_数据列的查询。因此,您可以只在聚集索引中存储缓存数据(默认情况下,聚集索引包括所有列)。数据库调优顾问很好地为您提供了一些想法,但它并不总是一个好主意去做它所说的。

    典型的缓存数据列有多大? 如果它几乎总是大的(超过8K的大小),那么它将导致大量带有LOB溢出页的活动。当有很多LOB活动时,我不是工作负载调优专家,但可能有一些很好的建议资源。要考虑的一件事是(在尝试改进索引并实际查看内存使用情况、缓存命中率等之前)考虑允许更多表行适合页面的更改:

    1. 重新考虑是否需要类型 NChar(128)用于缓存名称。(你 可能,但想想看。它是 总是接近128字节的数据?是 必须使用Unicode,并且 值得额外的空间吗?如果不是,也许 nvarchar(128)或varchar(128)是 好的。

    2. 考虑它是否有用 将“大值类型”设置为 Row'选项打开。默认值为 关闭,这可能会导致您 每个数据只有一个表行 平均页数,但不减少 需要LOB溢出页。 查看使用的sp_space的结果 或sys.dm_db_partition_stats to try 评估这一点。如果你只有1个 或者每页2行,这可能有助于 更改设置。

        2
  •  2
  •   sgmoore    16 年前

    如果有大量的删除,可能会向日志文件写入大量内容。如果涉及到任何关系,可能要花很长时间来确定是否允许删除记录。

    我也有类似的问题,(但在我的案例中,我需要确保旧记录不可见) 最后添加了一个名为hidden的位字段。因此,“删除”例程实际上只是一个将hidden设置为true的更新语句,并且对查找进行了修改以忽略隐藏的记录。

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

        3
  •  1
  •   wsorenson    16 年前

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

    或者,您可以使用 memcached 用于处理缓存读取和过期。

        4
  •  1
  •   Jim Petkus    16 年前

    清理这些数据绝对应该异步进行(通过一个预定的SQL作业、一个服务、填充数据的作业等)。如果您担心查询中的旧项在有机会删除它们之前会返回,那么您可以实现某种类型的版本控制方案,该方案只返回最新的项。

        5
  •  0
  •   MatBailie    16 年前

    我认为问题的一部分是设计,但假设我们只想加快删除速度而不更改其他内容?

    只有“cache_event_id”的索引确实用于删除,但不是您可以预期的方式。使用执行计划运行删除,使用索引后,您将看到它还使用主键(假设是集群的)。索引基本上只是作为快捷键来查找需要删除的主键。主键(或任何聚集索引)允许RDBMS物理上知道记录在哪里,因此可以删除它们。

    此外,删除记录时,所有索引都需要更新。根据您有多少个索引,以及它们的设置方式,这可以是labourios。

    所以我的两个建议是:
    1。确保主键或聚集索引的第一个字段为“缓存事件ID”
    2。合理化索引的数量,如果可能,这可能涉及重写一些查询。