Welcome to MSDN Blogs Sign in | Join | Help

News

  • All posts on this blog are meant for illustration purposes only and not intended for use in production. The following disclaimer applied to all code used in this blog: Copyright (c) Microsoft Corporation. All rights reserved. THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. USE AND REDISTRIBUTION OF THIS CODE, WITH OR WITHOUT MODIFICATION, IS HEREBY PERMITTED.
Minor Changes in Database Configuration Checklist for Dynamics AX

We have made minor but important modifications to the Database Configuration settings, which are still posted under March, 2008 archive.  The archived posting includes the most current recommendations.  We've also posted a Microsoft Word version of the checklist below.

  • Changes specific to Microsoft Windows Server 2008 environments:
    • Max Server Memory for a SQL Server instance does not need to be managed with a custom setting, see KB918483 for details. (Configuration Part 1)
    • Partition alignment on SANs does not have to be performed manually on Windows Server 2008.  The default partition offset is 1024K which is suitable in most scenarios. (Configuration Part 2)
  • We have also modified our recommendations to set Auto Update Statistics Asynchronously to FALSE (from TRUE), which is the default setting.  If you modified this setting, please change it back to FALSE.  (Configuration Part 2)

 

Create RecID index on tables with Created/Modified DateTime fields

In Dynamics AX 2009, if you use Microsoft SQL Server 2005 as the database, you should create RecID index on a table if the table has CreatedDateTime field and/or ModifiedDateTime field.

When you Insert into a table with CreatedDateTime field on SQL 2005, the following SQL statement will be issued immediately following the Insert:

    SELECT CreatedDateTime From <table> WHERE RecID=<recid> AND DataAreaId=<dataareaid>

When you Update a table with ModifiedDateTime field on SQL 2005, the following SQL statement will be issued immediately  following the Update:

    SELECT ModifiedDateTime From <table> WHERE RecID=<recid> AND DataAreaId=<dataareaid>

If you don't have RecID index on the table, the SELECT will result in a table scan. If the table being inserted into or updated is big, a seemingly innocent Insert or Update operation can take a long time to return and put unnecesary load on the SQL server.

If your backend database is Microsoft SQL Server 2008 or Oracle, there's no issue since the second SELECT statement will not be issued. The kernel will use an OUTPUT clause on the INSERT/UPDATE statement to retrieve the CreatedDateTime/ModifiedDateTime values.

 

Event Trace Parser Tool for Microsoft Dynamics AX Released

We have released the RTM version of Event Trace Parser Tool for Microsoft Dynamics AX (aka TraceParser). You can download the tool from CustomerSource or PartnerSouorce.

The Event Trace Parser for Microsoft Dynamics® AX is a user interface and data analyzer built on top of the Event Tracing for Windows (ETW) framework.  The ETW framework allows an administrator to conduct tracing with an overhead of approximately 4%.  This low overhead allows administrators to diagnose performance problems in live environments as opposed to development environments.

The Trace Parser is built on top of Microsoft SQL Server.  It enables rapid analysis of traces to find the longest running code, longest running SQL query, highest call count and other metrics useful in debugging a performance problem.  In addition, it provides a call tree of the code that was executed, giving the developer insight into the code, and the power to quickly become familiar with the flow of an unfamiliar piece of code.  It also provides the ability to jump from the searching feature to the call tree, so that the person analyzing the trace can determine how the problematic code was called.

Event Trace Parser works with Microsoft Dynamics AX 4.0, Microsoft Dynamics AX 2009, Microsoft SQL Server 2005 and Microsoft SQL Server 2008.

Event Trace Parser for Microsoft Dynamics AX is being released as an unsupported tool. However, as alwasys we appreciate your feedback and suggestions very much!

 

 

Application Benchmark Toolkit for Microsoft Dynamics AX 2009
We have released the Benchmark Toolkit as an Open Source Project on CodePlex. You can use the Benchmark Toolkit to stress and performance test your AX application or custom classes. The Benchmark Toolkit comes with a predefined set of 12 scenarios such as Sales Order and Project Quotation. You are free to modify these to fit your scenario. The Benchmark Toolkit also comes with the Programming Model Proxy which will allow you to choose X++ classes, enums and tables from your AOT and automatically generate C# proxy code for calls into Microsoft Dynamics AX Business Connector. For more information visit our CodePlex website.
Trace Parser Release Candidate 2

Trace Parser has been updated with some new user-friendly UI changes as well as stability improvements. Please give it a try from either Partner Source or Customer Source. You will need an account to download.

Please post any issues or feedback and we can try to get it incorporated into the RTM release.

TraceParser Video Training Part II: Configuration, Collection and Importing
 

TraceParser - Collecting and Importing

This video will give you a step by step introduction to configuring Dynamics AX for tracing, collecting traces from both the TracingCockpit and Client Configuration Utility and importing these traces to the TraceParser database.

If you do not have TraceParser installed, make sure to review the first video on installing TraceParser at http://blogs.msdn.com/axperf/archive/2008/07/18/traceparser-video-training-part-i-installation.aspx


Video: Trace Parser Collection and Importing

Registry Keys

Registry Keys for Dynamics AX Client can be found under HKCU\Software\Microsoft\Dynamics\5.0\Configuration

Registry Keys for Dynamics AX Server can be found under

HKLM\System\CurrentControlSet\Services\Dynamics Server\5.0\01\Configuration

If there is more than one instance of Dynamics Server running, you will want to find the appropriate instance.

Under each of these, will want to find a key named Current and note the value of this key.  This is your active configuration for the Client.  You should find a corresponding subkey under Configuration which houses the registry keys for your current installation.  TraceParser specific keys are labeled with the trace prefix.  An explanation of each key and its meaning is below:

traceallowclient

Ignored for client, on server set to 0x1 to allow the client to turn on tracing

tracebuffersize

Size in MB of buffer space allocated for ETW

traceeventsenabled

A semicolon delimited list of event flags. These correspond to the trace events in the AX Client Configuration Utility

tracemaxbuffers

Maximum number of buffers to use for ETW. If you are getting any dropped event errors, you will want to increase this.

tracemaxfilesize

Maximum disk space usage in MB for the ETW tracefile. You will need to have enough free space to allocate a file of this size.

traceminbuffers

Minimum amount of buffers to allocate for ETW. If you are getting any dropped event errors, you will want to increase this.

tracestart

0x0 if tracing is currently stopped, or set to 0x1 when tracing is started

tracexppmethodcalldepth

Depth of the call stack to collect when tracing

 

Trace Events

Which events you specify will affect what information is collected when tracing. By default, if you install TraceParser using the client integration, it will automatically turn on most events necessary for tracing. If you would like to define your own events, here is an explanation.

RPC round trips to server

Remote Procedure Calls between Client and Server

X++ method calls

Method calls within the X++ language

Function Calls

X++ built in functions such as int2str

Number of nested calls

Depth of the X++ call stack when collecting traces

SQL Statements

Queries issues to the SQL Database

Bind variables

Parameters specified for each of the SQL queries

Row fetch

Number of rows returned by SQL

Row fetch summary

Same as row fetch, and includes timing information

Connect and disconnect

Connections and disconnections to SQL

Transactions: TTSBegin, TTSCommit, TTSAbort

Transaction statistics, not necessary if X++ method tracing is enabled

Using LOGMAN To Query Trace Providers

Open a command prompt and type "logman -ets query providers". This will show you a list of running providers. When AX Tracing is turned on, you should see an output similar to below:

C:\>logman -ets query providers

Session                    Id     File
-------------------------------------------------------------------------------
MSDTC_TRACE_SESSION        2      C:\WINDOWS\system32\MSDtc\trace\dtctrace.log
AX_TRACE_client_{CE6D980E-3F17-490A-951F-ECE4BE57A597} 3      c:\log\AOS_AXPTRACEDC_2712_20080731194343_client.trc
AX_TRACE_AOS_{CD8AE17D-33CA-4C75-8DC9-79DCA90387E7} 4      c:\log\AOS_AXPTRACEDC_2712_20080731194343.trc

The command completed successfully. 

Coming Soon - Part III

I will show you how to analyze the traces that we just collected.  This might be a 2 part series that starts with the basics and then covers the more advanced topics.

Future series:

  • Automation (using X++, C# and Batch Files)
  • Case Studies using TraceParser to identify issues
TraceParser Video Training Part I: Installation

Installing TraceParser

 This is the first part of a series of training videos on installing and using TraceParser.  I will walk you through the basics, and then show you how to identify performance issues within your AX application code.

Where to get TraceParser

TraceParser can be found on PartnerSource. You must be a registered partner with PartnerSource in order to access this tool.  Once registered, it can be found on PartnerSource             

Prerequisites

Windows Server 2003 or greater

SQL Server 2005 or greater

Dynamics AX 2009 or Dynamics AX 4.0 (Only supported on Windows Server 2003, x86)

 


Video: Trace Parser Installation

Notes

TraceParser (ETW) logs can be 1 or more gigabytes in size. You should install TraceParser on a system that has enough hard drive space for the logfile, including storing the same size logfile inside of the database.

Coming Soon - Part II

 I will be covering how to collect traces using 3 methods

  1. Using the Tracing Cockpit
  2. Using the Server Configuration Utililty
  3. Advanced usage by modifying the registry directly

 Further series will include

  • Analyzing Traces
  • Automation (using X++ and C#)
  • Case Studies where we have used TraceParser to identify issues

 

Debugging X++ Object Leaks

One of the most important aspects of writing managed code that interacts with AX through the Business Connector is cleaning up objects.  Each AxaptaObject and AxaptaTable must have dispose called on it before going out of scope, or we’ll leak the object on the server with no way to clean it up.  AxaptaObject and AxaptaTable do not implement Finalizers, so the onus is on the consumer of these APIs to do proper cleanup.  If a session logs off, all of its objects are cleaned up regardless of whether or not they were disposed of properly, but if we have a long running session that isn’t properly disposing objects, we can quickly run the server out of memory causing it to crash.  These problems can be notoriously hard to debug since there is no leaking happening on the Business Connector application, and there is little insight into what’s going on inside of the X++ runtime.  We’ve also seen problems occur similar to this from straight X++ code when users put things into the GlobalCache and never take them out or continuously add to the global cache.

 

X++ does have an API to enumerate all objects on the Heap, and a form to do so as well.  The form is called “SysHeapCheck” and can only be opened up from the AOT.  Clicking the “Update” button will populate the current tab.  This form, however, is only accessible from the Rich Client and does little to aggregate objects together, so it’s hard to visualize which, if any, objects are leaking.  The other big issue with this form is that it only shows the objects that are alive on the Client, not those that are alive on the server, so if we’ve leaked a bunch of objects on the server this form won’t help us.

Figure 1: SySHeapCheck form

  image

This information is retrieved from the API HeapCheck inside of the X++ Runtime.  We can use this API to provide a better abstraction of this information and make it accessible from inside of X++ code.  I’ve created a useful utility called Heap Dump.  You can find it attached to this post.

 

Let’s take a look at how we’ll use this code.  The Main method provides a sample of this API:

static void Main(Args _args)

{

    Map result;

    MapEnumerator enum;

    str objectName, objectType;

    boolean runningOnClient;

    int aliveCount;

    int totalObjects;

    int totalCursors;

    container _map;

    ;

    [totalObjects, totalCursors, _map] = HeapDump::DumpAllObjects();

    result = Map::create(_map);

    enum = result.getEnumerator();

    info(strfmt("Total Objects: %1", totalObjects));

    info(strfmt("Total Cursors: %1", totalCursors));

    while(enum.moveNext())

    {

        [objectName, runningOnClient, objectType] = enum.currentKey();

        aliveCount = enum.currentValue();

        info(strfmt("%1 %2 %3 %4", objectName, runningOnClient ? "Client" : "Server", objectType, aliveCount));

    }

}

The DumpAllObjects returns a Container in the format of [<Total number of unfreed Objects>, <Total number of unfreed Cursors>, <Unfreed Object Map>].  The third parameter, the Map of Unfreed Objects, is of type Map(Types::Container, Types::Integer).  The key to this map is a container in the format [<Type Name>, <1 if object is on Client, 0 if on the server>, <Cursor or Object>].  This implantation is a bit hairy, but it was done this way to maximize interoperability with .NET.  If we’re in X++ and we’re looking at a leak from the rich client, we can just use the main method here.

We can call this from managed code to get the same information, as seen below.

The best way to avoid these problems to begin with is to use the “using” statement.  You can find multiple examples of this in my code below.  Every time I call a method that returns an AxaptaObject, AxaptaContainer, or AxaptaCursor, it’s wrapped in a using statement.  By doing so, as soon as the object goes out of scope it gets cleaned up, and its memory freed.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Dynamics.BusinessConnectorNet;

 

namespace HeapDumpExample

{

    static class LambdaHelpers

    {

        /// <summary>

        /// Performs an action on each item in a list, used to shortcut a "foreach" loop

        /// </summary>

        /// <typeparam name="T">Type contained in List</typeparam>

        /// <param name="collection">List to enumerate over</param>

        /// <param name="action">Lambda Function to be performed on all elements in List</param>

        static void ForEach<T>(this IList<T> collection, Action<T> action)

        {

            for (int i = 0; i < collection.Count; i++)

            {

                action(collection[i]);

            }

        }

    }

    struct HeapDumpEntry

    {

        public string TypeName {get; set;}

        public bool IsRunningOnClient { get; set; }

        public string ObjectType { get; set; }

        public int AliveObjectCount { get; set; }

    }

 

    /// <summary>

    /// Helper class to allow us to perform LINQ queries over the X++ Map returned from the HeapDump call

    /// </summary>

    class EnumerableAxaptaMap : IEnumerable<HeapDumpEntry>

    {

        private AxaptaObject mMap;

        public AxaptaObject Map

        {

            get { return mMap; }

            set

            {

                if (startedEnumerating)

                {

                    throw new Exception("Can't change contianer once enumeration has started");

                }

                mMap = value;

            }

        }

        bool startedEnumerating = false;

 

        #region IEnumerable<HeapDumpEntry> Members

 

        public IEnumerator<HeapDumpEntry> GetEnumerator()

        {

            startedEnumerating = true;

            //Get an enumerator over the Map

            using (var MapEnumerator = Map.Call("getEnumerator") as AxaptaObject)

            {

                //Enumerate over the Map

                while ((Boolean)MapEnumerator.Call("moveNext"))

                {

                    using (var key = MapEnumerator.Call("currentKey") as AxaptaContainer)

                    {

                        var value = (Int32)MapEnumerator.Call("currentValue");

                        //The key contains a container with Type and location iformation, the value contains the number of objects alive

                        yield return new HeapDumpEntry

                        {

                            TypeName = key.get_Item(1).ToString(),

                            IsRunningOnClient = Convert.ToBoolean(key.get_Item(2)),

                            ObjectType = key.get_Item(3).ToString(),

                            AliveObjectCount = value

                        };

                    }

                }

                yield break;

            }

        }

 

        #endregion

 

        #region IEnumerable Members

 

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()

        {

            //Lazy default implementation

            foreach (var x in this.ToList()) { yield return x; }

            yield break;

        }

 

       

        #endregion

    }

 

    class Program

    {

        static void Main(string[] args)

        {

            int TotalNumObjects, TotalNumCursors;

            List<HeapDumpEntry> AliveObjects;

            var CreatedObjects = new List<AxaptaObject>();

            using (Axapta session = new Axapta())

            {

                //Log onto AX

                session.Logon("", "", "", "");

                //Dump all objects created on Logon

                Console.WriteLine("BaseLine");

                WriteAliveObjectsToConsole(session, out TotalNumObjects, out TotalNumCursors, out AliveObjects);

                //Create Lots of Objects and don't dispose them

                for (int i = 0; i < 100; i++)

                {

                    CreatedObjects.Add(session.CreateAxaptaObject("Activities"));

                }

                //Dump current list of objects

                Console.WriteLine("Before Disposing Objects");

                WriteAliveObjectsToConsole(session, out TotalNumObjects, out TotalNumCursors, out AliveObjects);

                //Dispose all of the objects

                CreatedObjects.ForEach(obj => obj.Dispose());

                Console.WriteLine("After Disposing Objects");

                //Dump current list of objects again

                WriteAliveObjectsToConsole(session, out TotalNumObjects, out TotalNumCursors, out AliveObjects);

            }

 

        }

 

        private static void WriteAliveObjectsToConsole(Axapta session, out int TotalNumObjects, out int TotalNumCursors, out List<HeapDumpEntry> AliveObjects)

        {

            DumpObjects(session, out TotalNumObjects, out TotalNumCursors, out AliveObjects);

            Console.WriteLine("Total Number of Live Class Objects: {0}", TotalNumObjects);

            Console.WriteLine("Total Number of Live Cursors: {0}", TotalNumCursors);

            AliveObjects.ForEach(entry => Console.WriteLine("\t{0} {1} {2} {3}", entry.TypeName

                                                                               , entry.IsRunningOnClient ? "Client" : "Server"

                                                                               , entry.ObjectType

                                                                               , entry.AliveObjectCount));

        }

 

        private static void DumpObjects(Axapta session, out int TotalNumObjects, out int TotalNumCursors, out List<HeapDumpEntry> AliveObjects)

        {

            TotalNumObjects = 0;

            TotalNumCursors = 0;

            AliveObjects = null;

            using (var objectsConatiner = session.CallStaticClassMethod("HeapDump", "DumpAllObjects") as AxaptaContainer)

            {

                if (objectsConatiner != null)

                {

                    TotalNumObjects = (Int32)objectsConatiner.get_Item(1);

                    TotalNumCursors = (Int32)objectsConatiner.get_Item(2);

                    using (AxaptaObject ObjectMap = session.CallStaticClassMethod("Map", "create", objectsConatiner.get_Item(3)) as AxaptaObject)

                    {

                        var MapEnumerable = new EnumerableAxaptaMap { Map = ObjectMap };

                        //We use an extension method to sort the list and put it to a list

                        AliveObjects = MapEnumerable.OrderByDescending(x => x.AliveObjectCount).ToList();

                    }

                }

            }

        }

    }

}

 

 

Ax Database Configuration Checklist Part 2

NOTE: This section has been updated to include special considerations for running SQL Server on Windows Server 2008. We have also modified our recommendations for setting Auto Update Statistics Asynchronously to FALSE from TRUE.

 

Tempdb database storage configuration

 

q  Determine total size of data and transaction log required for tempdb to avoid autogrow, and number of data files required based on # of processors (logical or physical).

 

How:

Determine the number of processors exposed to SQL Server.  Unless you are using an affinity mask (not covered here), this is the total number of processors you see in the Windows Task Manager Performance tab.

Why:

SQL Server creates one visible scheduler (for processing user requests) for each processor, and we generally want to maintain one tempdb data file per processor.  This recommendation is based on performance testing on the Dynamics AX OLTP workload.

 

q  Move tempdb primary data file and log file to high-speed storage (if available); at the same time, resize data file proportionally to total size, and resize log file to required size.

 

How:

Moving tempdb primary data and/or transaction log file must be performed in two steps: first, alter the database and move the files; second, restart SQL Server instance to complete the move.  The primary tempdb data file cannot be moved while SQL Server instance is running.  NOTE: tempdb data and transaction log files may reside on the same storage device.

See http://technet.microsoft.com/en-us/library/ms174269.aspx (Section G) for detailed instructions on how to move tempdb database files.

Why:

Isolating tempdb on its own storage can improve performance.  However, it is more critical to separate user database data and transaction log files (see section below).

 

q  Create additional data files depending on number of processor cores, of equal size, totaling the data size determined in previous step, so that the aggregate size of data files including the primary data file meets the total size requirement.  All data files must be identical in size.

 

How:

Additional database data files can be created from SQL Server Management Studio (SSMS) UI or by using the ALTER DATABASE command.

Why:

Creating multiple files for tempdb data, even if these files reside on the same storage device, can improve performance of tempdb operations, especially for databases (such as the AX user database, see below) that run in Read-Committed Snapshot Isolation (RCSI) mode.  RCSI stores row versions in tempdb.

References:

http://www.microsoft.com/technet/prodtechnol/sql/2005/workingwithtempdb.mspx contains a comprehensive set of Best Practices for managing the tempdb database.

 

q  If space is available on the drive where tempdb files are allocated, configure auto grow in MB (100-500) rather than percent for both data and log files.

 

How:

Auto grow configuration can be set from the SSMS UI or by using ALTER TABLE command. 

Why:

Auto grow should not be used for space management, but rather as a “relief valve” in case tempdb files exhaust their initially allocated space.  If files auto grow, the initial allocation should be adjusted to avoid auto grow in the future.  Configuring auto grow in MB rather than percent increments allow for more predictable space allocation, and helps avoid extremely small or large growth increments.

 

q  Review tempdb data and log files to ensure that they are all sized correctly and that data files remain of equal size.

 

How:

The easiest way to check is to use the database properties -> files UI in SQL Server Management Studio; scan the data files size to ensure that they are the same and at their initial values.  If not, it means more space was required and one or more files grew, and the initial files sizes may have to be adjusted accordingly to ensure that they files remain at the same size at all times.

 

AX User Database Configuration

 

q  Set compatibility level to 90 (SQL 2005) or 100 (SQL 2008).

 

q  Set Read-Committed Snapshot Isolation = true (cannot be performed through GUI).

 

How:

Execute the following command with no other active connections in the database:

ALTER DATABASE <ax database name>

    SET READ_COMMITTED_SNAPSHOT ON;

Query the row in sys.sysdatabases and ensure that the column is_read_committed_snapshot_on = 1.  Replace <ax database name>  with the appropriate name.

Why:

Testing with Dynamics AX ERP shows superior performance when using Read Committed Snapshot Isolation. 

References:

http://technet.microsoft.com/en-us/library/ms175095.aspx includes complete instruction on enabling RCSI.

http://technet.microsoft.com/en-us/library/ms188277.aspx includes a discussion contrasting Read Committed Snapshot Isolation and Read Committed Isolation.

 

 

q  Set Auto-Create Statistics, Auto Update Statistics= True; set Auto Update Statistics Asynchronously = FALSE (this is a change from our previous recommendation)

 

How:

These settings can be enabled from SQL Server Management Studio (SSMS) UI or by issusing the appropriate ALTER DATABASE statement.

Why:

Testing with Dynamics AX ERP shows superior performance when using these options.

 

q  Ensure Auto Shrink = False.

 

How:

These settings can be enabled from SQL Server Management Studio (SSMS) UI or by issusing the appropriate ALTER DATABASE statement.

Why:

Automatically shrinking database files can incur potentially severe performance problems.  If it’s necessary to shrink these files, it can be performed manually at a time designed by the DBA. 

References:

http://blogs.msdn.com/sqlserverstorageengine/archive/2007/03/28/turn-auto-shrink-off.aspx

 

q  If autogrow is configured, use MB (usually 100-500) rather than percent for both data and log files.

 

How:

Autogrow configuration can be set from SQL Server Management Studio (SSMS) UI or by issusing the appropriate ALTER or CREATE DATABASE statement.

Why:

When autogrow is specified in absolute increments rather than as a percent of the total size, the effects of autogrow are easier to anticipate and manage.  Percent-based autogrow setting can err on either extreme, of creating too many small file increments in rapid succession, or creating overly large increments. 

Autogrow should be considered a “relief valve” to allow database files to grow when absolutely necessary, rather than causing an error, but it should not be used as a long-term storage management solution.  When an autogrow event occurs, the DBA should respond by reviewing the size of all database files and adjusting accordingly, to minimize the probability of future autogrow events.  Space should be periodically reviewed and adjusted when necessary based, rather than having the system periodically expand files on its own.

 

Physical Storage Configuration

 

Note: Compliance is optional depending on storage resources available.  Some SAN vendors may have alternate recommendation that take precedence.  Recommendations are listed in priority order.

 

q  Perform sector alignment before allocating storage volumes to SQL Server on most SAN environments.

 

NOTE: The following recommendations for sector alignment do not apply to Windows Server 2008.  Partitions created under Windows 2008 are aligned to sector 1024 by default, which is an acceptable setting.

 

 

How:

Consult with your SAN vendor if possible for specific guidance on the storage product you are using.  In the absence of vendor-specific recommendations, volume alignment should be set on a 64K offset for SQL Server.  See the Predeployment I/O Best Practices paper referenced below for detailed instructions.  It is generally not necessary to perform volume alignment using Windows Server 2008, but check with your SAN vendor if in doubt.

Why:

Sector alignment (or volume alignment) ensures that logical disk sectors conform to physical sector boundaries on the disk geometry.  Windows 2003 does not align sectors optimally for SQL Server workloads by default, causing performance problems because each logical sector spans two physical sectors. 

References:

Predeployment I/O Best Practices

Physical Database Storage Design

 

q  AX User database data files and transaction log files are on separate physical stores.

 

q  Tempdb database files not on RAID 5 (RAID 1, 0+1, or 10 preferred).

 

q  AX transaction log file is not on RAID 5 (RAID 1, 0+1, or 10 preferred).

 

q  AX data files are not on RAID 5 (RAID 0+1, or 10 preferred).

 

q  Tempdb database files are on separate physical store from user database files.

 

q  Other database files (if any, such as databases for performance monitoring) are on separate physical store from AX and Tempdb database files.

Welcome -- Ax Database Configuration Checklist part 1

Welcome to the Dynamics Ax Performance Team's blog.  We're putting together a team introduction and hope to have it posted within the next week or so.  The first entries will discuss SQL Server 2005 configuration and Best Practices for Dynamics Ax, but we'll be covering a much wider range of topics over the coming months, such as Dynamics AOS configuration, X++ profiling, and leveraging new features of SQL Server 2008.

My name is David Reinhold and I specialize in SQL Server database and BI performance.  I spent nine years in Microsoft Consulting Services (MCS) practice before joining the Ax Performance Team in December, 2007.

Here is the Part 1 of the Database Configuration Checklist, covering configuration of: Operating System performance settings, SQL Server 2005/2008 Service configuration, and SQL Server 2005/2008 Instance configuration (updated to include special considerations for Windows Server 2008).

The format of the Checklist includes instructions on How to check and perform the configuration steps, Why we're making the recommendation, and References.

We've highlighted configurations that are specifically tuned for Ax databases, but there are also some general guidelines.  Rather than re-invent the wheel, we've inlcuded links to authoritative SQL Server Best Practices documentation available on the web, primarily on Microsoft sites.

Database Configuration Checklist Part 1

 Assumptions:

  • Dedicated SQL Server 2005 Server (does not run any other major applications besides SQL Server)
  • Single instance of SQL Server dedicated to running Dynamics Ax database.

OS

q  Set Advanced Performance Option->Processor scheduling: Best Performance of Background Services.

 

q  Set Advanced Performance Option->Memory usage: Best Performance of Programs (does not apply to Windows Server 2008).

  

Windows Server 2003:

My Computer->Properties->Advanced tab->Performance->Settings button->Advanced tab->Choose Processor Scheduling-Adjust for best performance of Background Services, Memory Usage-Adjust for best performance of Programs.

Windows Server 2008:

My Computer->Properties->Advanced System Settings->Advanced tab->Performance Settings->Advanced tab->Choose How to Allocate Processor Resources->Adjust for Best performance of background services.

 

q  Ensure boot.ini parameter /USEPMTIMER is present on AMD64 systems.

 

References:

http://blogs.technet.com/perfguru/default.aspx

http://support.microsoft.com/kb/895980

 

SQL Instance – Service Config

q  Run SQL Server service under a Domain Account with minimum necessary privileges.

 

References:

See http://download.microsoft.com/download/4/7/a/47a548b9-249e-484c-abd7-29f31282b04d/SQLSecurityOverviewforAdmins.doc for detailed prescriptive guidance on configuring SQL Server service accounts.

 

q  Grant SQL Server service account “lock pages in memory” privilege (Local Security Policy).

 

How:

See http://technet.microsoft.com/en-us/library/ms190730.aspx for detailed instructions.  This privilege is not automatically granted to local administrators.

References

http://blogs.msdn.com/psssql/archive/2007/10/18/do-i-have-to-assign-the-lock-privilege-for-local-system.aspx

http://support.microsoft.com/kb/918483

http://blogs.msdn.com/slavao/archive/2005/08/31/458545.aspx

 

q  Enable TCP/IP network protocol.

 

NOTE: This protocol may be automatically enabled when SQL Server is installed, depending on the edition.

How:

http://msdn2.microsoft.com/en-us/library/ms191294.aspx

 

q  Disable Hyperthreading

 

How:

This must be performed in the bios settings of the server.

Why:

Not all SQL Server workloads benefit from hyperthreading.  Dynamics AX ERP has not been extensively tested with hyperthreading, so the recommendation is to disable it.

References:

http://blogs.msdn.com/slavao/archive/2005/11/12/492119.aspx

 

SQL Instance – Configuration

 

Max Degree of Parallelism (MAXDOP instance-wide)

q  OLTP (normal operations0: Set Max Degree of Parallelism to 1

 

q  UPGRADE (when upgrading to a new release of Ax): Set Max Degree of Parallelism to lesser of {8, # of physical processor cores, # of physical processor cores per NUMA node}.

 

How:

Use sp_configure system stored procedure where n is the desired value:

EXEC sp_configure 'max degree of parallelism', 1;

RECONFIGURE;

GO

EXEC sp_configure;

Examine the output from the second sp_configure execution and confirm that the new value is in the run_value column for the max degree of parallelism setting.

Why:

Dynamics AX OLTP workloads generally perform better when intraquery parallelism is disabled, but the upgrade process benefits from parallelism.

References:

http://msdn2.microsoft.com/en-us/library/ms181007.aspx

http://support.microsoft.com/kb/329204

http://blogs.msdn.com/sqltips/archive/2005/09/14/466387.aspx

 

q  Memory: Adjust MAX Server Memory (MB) according to available RAM (32/64 bit server).

 

NOTE: The following recommendations for configuring SQL Server:max server memory do not apply to Windows Server 2008, only to earlier versions of the OS (Windows Server 2003).  Windows 2008 includes improvements to memory management that make overriding the default setting for maximum SQL Server memory unnecessary (assuming the host is a dedicated to running a single SQL Server instance); see http://support.microsoft.com/kb/918483.

Server RAM (MB)

SQLServer:max server memory (MB)

/3GB *

/PAE*

AWE*

2,000

1,600

No

No

No

4,000

3,000

Yes

No

No

6,000

4,500

Yes

Yes

Yes

8,000

6,500

Yes

Yes

Yes

12,000

10,000

No

Yes

Yes

16,000

14,000

No

Yes

Yes

24,000

22,000

No

Yes

Yes

32,000

30,000

No

Yes

Yes

>32,000 <= 128,000

(available RAM) – 2,000 to 4,000

No

Yes

Yes

>128,000

4,000

 No

 Yes

 Yes

             *Denotes 32-bit only setting

 

Set Maximum Memory

How:

Use sp_configure to set the value of ‘max server memory (MB)’ as follows:

EXEC sp_configure 'max server memory (MB)', 10240;

RECONFIGURE WITH OVERRIDE;

GO

EXEC sp_configure;

This example sets the max server memory to 10,240MB (10GB).  Replace the value 10240 with the desired value.  Examine the output from the second sp_configure execution and confirm that the new value is in the run_value column.

Why:

Under the default setting, which is to use all available memory, SQL Server may leave no more than 128MB physical memory free, and may not release memory fast enough if needed by the Operating System.  These settings generally assure that ample memory will be available. See note above – max server memory default setting can be used if SQL Server is running on Windows Server 2008 OS.

References:

http://blogs.msdn.com/slavao/archive/2006/11/13/q-a-does-sql-server-always-respond-to-memory-pressure.aspx

http://support.microsoft.com/kb/918483

 

Set AWE

NOTE: Only configure on 32-bit systems as specified.

How:

Use sp_configure to set the value of ‘awe enabled’ as follows:

Examine the output from the second sp_configure execution and confirm that the new value is in the run_value column.  If the ‘lock pages in memory privilege’ has not been set for the service account, an error is raised if you attempt to enable AWE (see section above).

Why:

If AWE is not enabled, SQL Server will be unable to address more than 2GB or 3GB of memory (depending on /3GB switch), irrespective of how much physical RAM is available.  When AWE is enabled, the additional memory over 2GB or 3GB is available to SQL Server for data cache buffers.  32-bit only, not applicable to native 64-bit SQL Server 2005 instances.

References:

http://msdn2.microsoft.com/en-us/library/ms190673.aspx

http://technet.microsoft.com/en-us/library/ms191481.aspx

http://technet.microsoft.com/en-us/library/ms187499.aspx

 

Set /PAE and /3GB switches in boot.ini

NOTE: Only configure on 32-bit systems as specified

How:

These options are set in the boot.ini file for the appropriate OS selection.  If hot-swappable memory is supported by the hardware, the functionality enabled by the /PAE switch is automatically enabled.

Why:

/3GB is required to address memory >2GB on 32-bit systems, and /PAE is required to address memory >4GB on 32-bit systems.  Enabling /3GB limits memory available to the OS to a maximum of 16GB, even if more physical RAM is installed..

References (see AWE section above)

 

q  Monitor available memory to ensure that SQL Memory setting is correct.

 

How:

Use the sysmon performance counter Memory:Available Mbytes to determine if available memory drops below 500MB, especially for sustained periods.  You may need to reduce SQL Server Max Memory if this happens regularly or for extended durations.

Why:

On a server with at least 4GB running a dedicated SQL Server workload, when available memory goes below 500MB, especially for sustained periods, the server may experience performance problems.  It is out of scope of this document to provide guidance applicable in every such situation, but if available memory exhibits this pattern, further investigation and remediation may be necessary.

 

_____________
David Reinhold

Microsoft Dynamics Ax Performance & Scalability Engineer - Database

Page view tracker