How Much is Offline During an Index Rebuild?

Kendra with her Not-A-SAN

Kendra with her Not-A-SAN

This question came up in the pre-show chat for our weekly webcast: when you rebuild a nonclustered index offline, does it impact only the nonclustered index? Or does it impact the entire table?

Showing is more fun than telling! Let’s take a look.

First I restore AdventureWorks to slow storage

Sometimes slower storage is handy. To demo any questions like this, I like to restore a copy of AdventureWorks2012 onto a handy external Seagate drive. I love having SSDs in my workstation, but when it comes to wanting to test out things like blocking on a reasonably small amount of data, it pays to have some poky storage around.

Now I run a super slow nonclustered index rebuild

Once AdventureWorks is restored, I set up an offline index rebuild in one session window— and to make it take longer I set up my index rebuild command in a BAD way. I set fillfactor to 1 percent, which means SQL Server is going to explode this table and leave 99% of each page empty.

In another session, I query the table’s clustered index

While the index rebuild is running, I open a second window and run a SELECT query. This select query is designed to specifically read from the clustered index of the table and not use the nonclustered index at all:

Now I check out the blocking

Now, from a third session I check and see– is my SELECT from the clustered index blocked by the rebuild on the nonclustered index? To get all the details, I run Adam Machanic’s sp_whoisactive with a parameter to get information on locking. (This can be a bit expensive, so only use this option when you need it.)

Sure enough, my SELECT query is blocked by my index rebuild.

Blocking-Index-Rebuild

Clicking on the ‘locks’ XML column for my blocked SELECT statement, I can see more detail:

This confirms that the read has requested an intent shared lock on the Production.TransactionHistory object– but it can’t get the lock.

Finding: offline rebuilds of a nonclustered index prevent querying the clustered index

If you need to rebuild indexes offline, this means that you can’t minimize the impact by only rebuilding the nonclustered index– rebuilding them places locks on the object itself. We saw this by seeing that queries who just wanted an intent shared lock on the table were blocked.

If our indexes were large and tables needed to be available constantly, this could be a huge problem!

Enterprise Edition Doesn’t Fix Everything

With SQL Server Enterprise Edition, you can specify that you’d like to do an “ONLINE” rebuild. In this case things are a little different– users can access the table during most of the index rebuild. However, at the end of the rebuild SQL Server still needs an exclusive “Schema Modification” lock (SCH-M) to finish the operation– and in highly concurrent systems, that can still be an issue.

If you’d like to reproduce that issue at home to demo for yourself, it’s easy! Just start up the SELECT statement first and change the rebuild to use the “ONLINE” option.

But Wait, There’s More

This logic also applies to creating indexes– if you’ve got Enterprise Edition, you want to remember to always create your nonclustered indexes with the “ONLINE” option if you need to avoid blocking on the table. Even then, that pesky SCH-M lock can be a killer on very busy systems.

Previous Post
Get a Report of Your Server’s Top 10 Worst Queries
Next Post
What Do Developers Need to Know About SQL Server?

13 Comments. Leave new

  • I love your method of using a slow drive for this type of stuff. So simple and elegant it’s genius.

    Reply
  • Nice post. Thanks Kendra.

    Reply
  • Kendra,

    Thanks for the post. One thing I noticed, if you do a DROP/CREATE instead of a REBUILD OFFLINE, you can run your SELECT.

    I tried this on a copy of a PRODUCTION database with 2M+ rows. REBUILD OFFLINE would take 3 minutes on one of the indexes, and my SELECT statement would hang until it was complete.

    When I DROP/CREATE the index, I’m able to run my query immediately.

    The application in question is in the healthcare space. Maintenance runs nightly/weekly, with index rebuilds only when fragmentation hits a threshold. We have 2 users online during maintenance vs 100’s during normal hours, so load is very low. Ultimately *we’re fine with the index being offline.* *I’m not fine with the DB being offline,* which is really the behavior you demonstrate…

    So, what’s the downside? Why are index rebuilds so intrusive?

    Reply
    • Jeremy – because you’re building a new copy of the object. If your server isn’t fast enough to do that, you’ll have performance degradation.

      Reply
      • Understood, but we have the same problem on REBUILD and not being able to query a table because of a lock to boot.

        * Do you have any understanding of the technical reason behind the lock on the table?
        * Are there any other issues?
        * Is a REBUILD somehow more efficient than a DROP/CREATE?
        * If not, should it be a best practice for any non SQL Enterprise install?

        Reply
        • Jeremy – unfortunately, troubleshooting blocks is kinda beyond what I can do in a blog post comment.

          Reply
          • Thanks Brent, I appreciate the response, but I’m not asking you to troubleshoot blocks.

            Kendra’s conclusion was:
            An offline REBUILD on an index in SQL Standard will block a SELECT query.

            I found:
            A DROP/CREATE of an index in SQL Standard will not block the same SELECT query.

            The Question:
            Why wouldn’t you always do the DROP/CREATE if you aren’t using SQL Enterprise?

          • Erik Darling
            April 14, 2016 8:54 am

            I have a better question: why do either one?

            If you’re a fragmentation zombie, just reorg when you hit 40%, and update stats. Update stats being the important part.

            Without looking at your test setup, it’s tough to say why. Perhaps you got lucky and none of the index pages were deallocated, and remained in memory when you did a drop and create? Maybe your table is really small? This is why Brent directed you to a more robust Q&A forum: you didn’t provide enough details for a good answer.

          • Anonymous Junky
            August 11, 2016 11:53 am

            May be you can also consider sort_in_tempdb if you have enough storage on the disk and your tempdb resides on a different disk which will improve the rebuild performance. Drop/create process puts a S lock thats why you can select from the table VS rebuilding the index which acquirs the SCH-M lock.

          • Errr, no, dropping an index will change schema. Try this:

            * Start a long-running select query
            * Drop an index on that table
            * Start another long-running select query

            And run sp_WhoIsActive. You’ll see that while #1 is running, #2 waits for a schema mod lock, and will block #3 (even if #3 uses a nolock hint.)

          • Thats correct. Conflict between Sch-m and Sch-s. Also long running query with nolock can block index rebuild online or offline.

  • In your other blog https://www.brentozar.com/archive/2013/04/five-things-about-fillfactor/, you wrote:
    “In other words, if you set a 90% fillfactor when you rebuild a clustered index, SQL Server will try to leave 10% of each leaf page empty.”

    Now you say ” I set fillfactor to 1 percent, which means SQL Server is going to explode this table and leave 99% of each page empty.”

    Which is correct? Thank you

    Reply
    • Dominic Lorenz
      December 14, 2016 2:43 pm

      Hey Generos1, the 1% was done as a test to perform large fragmentation of the index and to cause a slow rebuild. This was intentional I believe and only a one off exercise to show the impact of trying to query the table and index during a rebuild index operation. Stick to the 90% for normal use.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu
{"cart_token":"","hash":"","cart_data":""}