简体   繁体   中英

SQL Server 2012 : SELECT and UPDATE in one query slow performance

I am running SQL Server 2012 and this one query is killing my database performance.

My text message provider does not support scheduled text messages so I have a text message engine that picks up messages from the database and sends them at the scheduled time. I put this query together that gets the messages from the database and also changes their status so that they do not get picked up again.

The query works fine, it is just causing wait times on the CPU especially since it runs every other second. I installed a database performance software and it said this query accounts for 92% of instance execution time. The software also said that every single execution is doing 347,267 Logical Reads.

Any ideas on how to make this perform better?

Should I maybe select into a temporary table and update those results before returning them?

Here is the current query:

    UPDATE TOP (30) dbo.Outgoing
    SET Status = 2
    OUTPUT INSERTED.OutgoingID, INSERTED.[Message], n.PhoneNumber, c.OptInStatus
    FROM dbo.Outgoing o
    JOIN Numbers n on n.NumberID = o.NumberID
    LEFT JOIN Contacts c on c.ContactID = o.ContactID
    WHERE Scheduled <= GETUTCDATE() AND SmsId IS NULL AND Status = 1

Here is the execution plan 执行计划

There are three tables involved in this query: Outgoing, Numbers, & Contacts

Outgoing is the main table that this query deals with. There are only two indexes right now, a clustered primary key index on OutgoingID [PK, bigint, not null] and a non-clustered, non-unique index on SmsId [varchar(255), null] which is an identifier sent back from our text message provider once the messages are successfully received in their system. The Status column is just an integer column that relates to a few different statuses (Scheduled, Queued, Sent, Failed, Etc)

Numbers is just a simple table where we store unique cell phone numbers, some different formats of that number, and some basic information identifying the customer such as First name, carrier, etc. It just has a clustered primary key index on NumberID [bigint]. The PhoneNumber column is just a varchar(15).

The Contacts table just connects the individual person (phone number) to one of our merchants and keeps up with the number's opt in status, and other information related to the customer/merchant relationship. The only columns related to this query are OptInStatus [bit, not null] and ContactID [PK, bigint, not null]

--UPDATE--

Added a non-clustered index on the the Outgoing table with columns (Scheduled, SmsId, Status) and that seems to have brought down the execution time from 2+ second to milliseconds. I will check in with my performance monitoring software tomorrow to see how it has improved. Thank you everyone for the help so far!

As several commenters have already pointed out you need a new index on the dbo.Outgoing table. The server is struggling with finding the rows to update/output. This is most probably where the problem is:

WHERE Scheduled <= GETUTCDATE() AND SmsId IS NULL AND Status = 1

To improve performance you should create an index on dbo.Outgoing where you include these columns. This will make is easier for Sql Server to find the correct rows. It will on the other hand create some more work for the actual update though since there will be a new index that needs attention when updating.

While you're working on this, it would likely be a good idea to shorten the SmsId column unless you actually need it to be 255 chars long. Preferably before you create the index.

As an alternate solution you might think about having separate tables for the messages that are outgoing and those that are outgone . Then you can:

  • insert all records from Outgoing to Outgone
  • delete all records from Outgoing , with output clause like you are currently doing.

Make sure though that the insert and the delete operations are done in one transaction or you will soon have weird inconsistencies in the database.

it is just causing wait times on the CPU especially since it runs every other second.

Get rid of the TOP 30 and run it much less often than once every other second... maybe every two or three minutes.

您可以启用sql服务器的最大并行度,以加快处理速度

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM