【问题标题】:Performance issue of stored procedure with several select and insert operations in SQL Server 2008 R2SQL Server 2008 R2 中具有多个选择和插入操作的存储过程的性能问题
【发布时间】:2014-05-11 16:15:00
【问题描述】:

我的应用程序和数据库设计可以简化为一个简单的报价系统。

以下是详细说明:

数据库中的三个表:

表格:项目:

  • itemId(唯一,主键,自动增加)
  • 名称 varchar 不为空
  • 类别 varchar 不为空
  • 说明 varchar 不为空

表格:收件人:

  • recipientId(唯一,主键,自动增加)
  • 名字 varchar 不为空
  • 姓氏varchar不为空
  • 地址 varchar 不为空
  • 电子邮件 varchar 不为空

表格:顺序:

  • id(唯一,主键,自动增加)
  • 价格双倍不为空
  • 日期时间戳
  • itemId(非外键)
  • recipientId(非外键)

我有以下存储过程来处理选择/业务逻辑:

CREATE procedere spInsert
   --list of parameters
   set @tmpItemId = (select itemId from item 
                     where name = @name 
                       and category = @category 
                       and description = @description)

   if @tmpItemId is NULL
   begin 
       insert to item values(@name, @category, @description)
       set @tmpItemId = @@IDENTITY
   end

   set @tmpRecipentId = (select recipentId from recipient 
                         where firstname = @firstname 
                           and lastname = @lastname 
                           and address = @address and email = @email )

   if @tmpRecipentId is NULL
   begin 
       insert to recipient values(@firstname, @lastname, @address, @email)
       set @tmpRecipentId = @@IDENTITY
   end

   insert into order values(@price, @date, @tmpItemId, @tmpRecipientId )

所以基本上逻辑是检查ItemRecipient的存在,然后插入。

在真实数据库中,ItemRecipient的列号分别为20,20。我正在尝试通过读取文本文件中的大量原始信息(将商品信息、收件人信息和订单信息混合在一起)来进行压力测试,然后执行spInsert

JdbcTemplate jt = getJdbcTemplate();
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jt);
simpleJdbcCall.withProcedureName("spInsert").execute(paraMap);

但是速度却不尽如人意。粗略计算,每100行原始信息插入需要3-5秒。

我想知道是否有任何改进的余地?包括改变数据库的设计?

PS,我正在考虑将 ItemRecipient 的自动增加索引 ID 更改为 UUID,以便让程序知道新 ID ItemRecipient 并避免 @ 987654333@ 在存储过程中。是否可行?


这是执行计划:

【问题讨论】:

  • 请执行计划。您希望我们重建您的数据库并为您制定执行计划吗?很可能你错过了提高查询效率的方法,但我们至少应该知道执行计划。而且您错过了像这样的 SP 可能需要避免参数嗅探问题的“重新编译”选项。
  • 执行计划请参考更新
  • 他并没有因为参数嗅探而得到糟糕的执行计划,而是因为根本没有索引,甚至在他创建了必要的索引之后也可能不会。顺便说一句,避免 WITH RECOMPILE,如果你真的必须处理糟糕的缓存计划问题,请在语句级别使用 OPTION (RECOMPILE),这样开销要少得多。

标签: sql sql-server database stored-procedures database-design


【解决方案1】:

我怀疑您需要以下索引来提高性能:

item(name, category, description)
recipient(firstname, lastname, address, email)

你可能也应该做这些唯一索引,这样你就可以避免“子查询返回太多行”的问题。

【讨论】:

  • 如前所述,我说的是现实中的 20 列。将 20 列组合的唯一索引设为一个好主意吗?
【解决方案2】:

这只是一个简化版本,您实际上是在比较大约 20 列以查找在每次插入 Item 表之前是否有匹配项,对于 Recipient 表也是如此?是否可以重新考虑设计?也许将 Recipients 和 Orders 分别插入到 Items 中?

要检查实际发生的情况,请检查执行计划和 SET STATISTICS IO ON 的输出。我的猜测是搜索部分是问题所在。

我相信这些表都聚集在标识列上。在一个或两个最具选择性的列上创建非聚集索引以加快搜索速度。不要让索引太宽,因为你会减慢它的速度。您无法使其独一无二,但可以使用 SELECT TOP 1 保护子查询。

永远不要使用@@IDENTITY,而是使用scope_identity()。

【讨论】:

  • 列数很大,只好一一比较。我想重新设计,但不知道。您的意思是为ItemRecipientOrder 创建单独的存储过程吗?请参考更新后的执行计划。
  • 执行计划如预期,表(聚集索引)扫描,没有有用的索引。选择最有选择性的列或组合并在其上创建 NC 索引,这实际上可能已经足够改进了。
  • 我尝试在Item 表中创建聚集索引进行测试,但它说表中已经有一个聚集索引,即itemId。我应该将itemId 更改为非聚集索引并将2-4 列放入聚集索引吗?
  • 不,您当前的聚集索引(在标识列上)没问题,保持原样;否则,您将显着减慢插入速度并引入碎片。而是在相关的高选择性列(尽可能少)上创建非聚集索引。
  • 用UUID代替自动增加id怎么样?这样我就可以处理服务器端的表关系,存储过程只负责插入
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-26
  • 1970-01-01
  • 2017-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多