Before jumping into index fragmentation I’ll first explain what indexes are and how they are used by the SQL Server Engine. I tried to compare it to a real world way of searching data, to paint a clear picture in your head. I’m not going into the differences between clustered and non-clustered indexes as that would be a nice new blog.

How do indexes work?

Indexes are used by SQL Server to search for records in tables as we use indexes in books to get to certain chapters/words/authors etc. The way an index in SQL Server works is similar to the way we lookup a phone number in a (old fashioned and hard copy) phonebook.

  1. We decide we need a phonebook for this search not a cookbook.
  2. We pick up the phonebook of the state we need.
  3. We look at the index to find the pages that have the correct city.
  4. We go to the page that contains the first letter of the last name of whose phone number we need.
  5. We find the last name and see that there are in fact several.
  6. We look at the address and find the right number.
SQL Server will do the same thing, with the right index off course:
  1. It decides it needs a certain index.
  2. It will look at the 1st level of the index to find the right part (phonebook of state).
  3. It will look at the 2nd level of the index to find the right part again (correct city).
  4. It will look at the 3th level of the index to find the right part again (last name).
  5. It finds the page containing the correct last name.
  6. It traverses through the records for the one that contains the needed address.

What is index fragmentation?

Index fragmentation is what it says the fragmentation of an index.
So a page with empty spaces in between. Or pages that are out of order. Or even split up pages.

How does index fragmentation start?

Back to the phonebook:
I’m responsible for updating the phonebook manually. If someone would move to another city I would take my eraser and erase them from their old city. Leaving a blank record.
Now go to the new city and insert their phone number in an already full page. I don’t like to scribble another ‘Mr Smith’ in between the others.
So I erase all ‘Smith’ entries that go after my new entry and put those on a new page, leaving a partly filled page. The new page won’t fit in between so I stick it at the end of the phone book.
Finding ‘Mr Smith’ got a lot more difficult now.
  1. We decide we need a phonebook for this search not a cookbook.
  2. We pick up the phonebook of the state we need.
  3. We look at the index to find the pages that have the correct city.
  4. We go to the page that contains the first letter of the last name of whose phone number we need.
  5. We find the last name and see that there are in fact several.
  6. We get to the page our ‘Mr Smith’ should be on, only to find a reference half way the page telling us to look at a page in the end of the book.
  7. We go to the page at the end of the book
  8. We look at the address and find the right number.
And yes again SQL Server will do the same for us, and it’s called: page split. More detailed information on page splits can be found at Tony Rodgerson’s blog.
By doing so I and SQL Server added an entire page just for one entry and leaving a blank record on the old page. And there you have it: index fragmentation. Putting the new page in the back of the phone book also applies here, on the hard drive there is no room to put the page in between so it is stored where there is room. Making the search for the data even more difficult.
As you probably know a hard drive (HDD) has to rotate to get to the right spot, so data neatly ordered will be found much faster than randomly stored data. The rotation in HDD’s is what makes it a lot slower than SDD’s that don’t use moving parts.
This is only one person moving to another city. But think of the number of changes that have to be made to the phonebook of New York annually. People move, they change their name, they get married, they go live together, get their own place and phone number etc.
This index gets fragmented constantly, so it will always be slow… Not necessarily.

What happens with index fragmentation?

Well consider this:
If all the pages of your phonebook would only contain 1 phone number. How many pages should you read to get to the right ‘Mr. Smith in New York’? Exactly too much.
Again for SQL Server this would work in the same way. SQL Server indexes are divided in pages of 8Kbytes. If these pages only hold one phone number it needs to check a lot of pages in the end.
SQL Server also has another great feature called lock escalation; which makes it lock the pages that hold the records so other users cannot edit the records you’re currently working on. If the percentage to be locked pages get’s high enough SQL Server escalates to locking the entire index (table). This locking mechanism is your friend in most cases, it prevents users from updating the same record at the same time with all consequences of that. But with index fragmentation the lock escalation can kick in too fast making SQL Server lock a lot more then you want, and by doing so making other users wait for the records till they are no longer locked.

How to temporarily or partly prevent index fragmentation?

If we could leave some space open on the pages of a phonebook we could enter new entries there and only shuffle the rest. This would increase the number pages to begin with but prevents the introducing of a new page part.
In SQL Server you can set a property on an index called fill factor which indicates how full a page should be when the index is created.
That means that if you set this to 70 the pages will be filled for 70% leaving 30% for new or updated records. which leads to less index fragmentation, well till the 30% is also filled. (if the Pad Index setting wasn’t set to true SQL will only use fill factor for the leaf pages: the pages containing the actual data. If it was set the pages that are used by SQL to get to the leaf pages called intermediate pages are also filled according to fill factor).
I hear you think: oh then I’ll use fill factor and set it at 50% for all indexes in my Dynamics Ax environment. You could do that, but then you’ll make everything twice as slow as it is with a 100% fill factor. And page splits only occur to indexes that change a lot.
Example 1:
The SalesTable has an index on RecId. RecId’s are dispensed in a neatly order, and they cannot be updated. This index will only grow in the last page, creating new ones right at the end. As long as you prohibit deletion of SalesTable records, no gaps will be created in between. Why would you ever adjust the fill factor on this index? The answer is: You shouldn’t !
Example 2:
The AIFMessageLog table has an index on a GUID created in Ax and send to SQL. GUID’s created in Ax can be considered as random strings. They will trigger index fragmentation at every insert and/or update operation. So this index will get fragmented by every change you make. And the workflow items get created a lot, making this index fragmentation’s level up to 98% in no time at all. Setting this index’s fill factor to 70 and maintain it a lot will certainly help a lot. More information on GUID’s and index fragmentation can be found  as Kimberly L. Tripp’s blog.

TIP: Workflow and AIF are using GUID’s in indexes so set fill factor accordingly and maintain them.

Example 3:
The Parameter tables in Dynamics Ax mostly have one or a few records in them, with an index on RecId. They get no new records and are updated almost never. Why mess with the fill factor? Index fragmentation is no issue here.
What you also could do is once in a while rewrite the entire phonebook, making all pages full again and you end up with less pages.
In SQL Server we call this an index rebuild. When an index rebuild is done the old one is replaced by a new one that will use the fill factor setting discussed earlier. Index fragmentation will  be 0  after a rebuild.
Using standard maintenance jobs in SQL will rebuild all indexes on all tables if they are fragmented or not. Rebuilding an index can be done offline which means the index is removed and then rebuild, while rebuilding SQL Server cannot use the index and will use another one or a table lock. If you use Enterprise Edition of SQL you can rebuild indexes online, then SQL build a new index while the old one is still in play replacing it when done. I recommend to rebuild indexes in a maintenance window when there is minimum activity on your database.
There is a great alternative to using the maintenance jobs of SQL: scripts. No, I don’t want you to script this for yourself. Ola Hallengren provides scripts that calculate if rebuild is necessary for an index and it takes your version and edition of SQL Server in consideration. In my experience these scripts work very for Dynamics Ax databases they only need one adjustment. There is a parameter in them that only take indexes from a 1000 pages and up in consideration, set this to 100 for Dynamics Ax.

How to permanently prevent index fragmentation to be an issue?

There are several things you could do, and some are free of charge others are considered an investment. Do what you can and you’re performance will improve as index fragmentation goes down.
  1. Make sure the physical drives your data is on are not shared with other applications. Mainly for HDD storage as the other applications will use the HDD rotation as well.
  2. Use the scripts of Ola Hallengren to reduce index fragmentation without rebuilding all indexes. Set the parameter of minimum pages to 100 instead of 1000 for Dynamics Ax databases.
  3. Use SDD’s for storage of data instead of HDD’s. Because SDD’s don’t use moving parts.
  4. Cache your database by assigning enough memory to SQL Server to cache the entire database, or at least the records that are frequently used. Because cached data is way faster then any stored data on hard discs.

Is index fragmentation the root cause of performance issues?

 Simple answer: No, it isn’t but it is an easily treatable result of more complex underlying problems. Index fragmentation is caused by several things as:
  1. Poorly chosen indexes. (indexes on fields that are updated all the time)
  2. Changing of usage of the system. (Users are allowed to change id fields, which wasn’t in the original design)
  3. Poorly developed queries. (queries that cannot make full use of the indexes that are already there)
  4. and a thousand other reasons.
All code samples, walkthroughs and other instructions are provided as-is. Use any and all information provided in this blog at your own risk. It is never advised to import or write code in a production environment without rigorous testing in a test or development environment.
All opinions expressed in this blog are solely my own and do not necessarily reflect the opinions of my employer.
The names of actual companies and products mentioned in this blog may be the trademarks of their respective owners.
Microsoft and Dynamics AX are registered trademarks of Microsoft Corporation in the United States and other countries.

Leave a Comment!

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