As you all may know, many vendors tend to force you to put the MAXDOP of your SQL Server to 1 (SAP, SHAREPOINT, AX). For these applications, this is by design because another MAXDOP setting might interfere with the otherwise performant workload of the application. Today I would like to demonstrate you the actual impact on your server of setting your MAXDOP to 1.
To start off I would like to give you a small introduction on what MAXDOP actually does. The MAXDOP setting limits the amount of CPUs used by one query in your SQL server instance. For example: if you put your MD to 2, then the query receives the option to create a parallel plan with 2 cores to execute a query. Fortunately, in SQL Server 2016 you can do this on database level. The MD setting works in parallel with another setting on instance level: Cost Threshold for Parallelism (default 5). The CTFP is the value you set for your instance to start evaluating the possibility to use a parallel plan if you have MD 2/4/8/…. For example, if your CTFP is set to 60 and your query plan only has a cost of 10, your query will not be evaluated to run as parallel. If then, however, you have another query with a cost of 80, the query optimizer will check the possibility to run this query in parallel (depending on your MD setting).
To show you what the impact to your system is, I am going to create an OLTP workload and OLAP workload to my virtual machine running on my laptop. The OLTP workload will do update inserts and deletes, the OLAP workload will do some read intensive queries onto the system. The OLTP workload will be small queries with a low cost, the OLAP workload will have more impact and calculations which will make them have a higher cost.The virtual machine will be configured with 4 cores. To run the workload, I will be using SQLQueryStress which can be found here. One thread will be used and will do 100 iterations for the two workloads. I will be running all my test against in-memory tables to reduce any disk overhead, and purely to look at the CPU usage. I will be running 5 tests with following setup.
I have altered my CTFP setting to 6 because only then my OLAP queries will be eligible for a parallel plan since they have a plan cost of 7. Here are the results:
When looking at the full elapsed time, we can see that following configurations were the fastest.
When Looking at the CPU user for every iteration, we can see that following configurations used the least amount of CPU.
When looking at the amount of seconds used by the client for every iteration we see the following test to take the least amount of time
When looking at the full elapsed time following configuration were the slowest
When looking at the CPU usage for every iteration we can see that following configuration uses the most of the CPU
When looking at the amount of seconds used by every client for every iteration we see that following test were the slowest.
As we can see in the results, the MAXDOP 1 setting is the slowest in comparison to all other tests in full. This means that, in most cases, you will limit the throughput of your SQL Server if you set your MAXDOP to one. The fastest configuration for my laptop has been proven to be MAXDOP 2 with COST THRESHOLD FOR PARALLELISM 6 for this workload with this hardware configuration. This is, however, not an exact science; this has to be tested for every system. The MAXDOP 1 was the slowest because we are limiting our read intensive workload to use one core for every query it executes. This increases the time for every read query in total, this makes that the workload was the slowest.
The fastest configuration is when we allow the read intensive workload to use 2 cores and limit the write/update/delete workload to 1 core. Because we are running both workloads together with 1 thread, we are letting our read intensive workload use 2 cores and leaving the 2 other cores available for our OLTP workload. This means that we are getting an ideal throughput for our workload as seen to our hardware.
Our datawarehouse will primarily have a read intensive aka OLAP workload, with one or more daily loads (INSERT/UPDATES/DELETES) happening. This means that we should get the most throughput on our read queries. I have been to some clients who have set their MAXDOP to 1 for their datawarehouse. This will have a negative effect on the speed at which queries are executed. I demonstrate this by testing the OLAP workload without the OLTP workload interfering For this I will do 3 tests.
1. 100 Iterations 1 thread OLAP workload MAXDOP 1
2. 100 Iterations 1 thread OLAP workload MAXDOP 2
3. 100 Iterations 1 thread OLAP workload MAXDOP 4
These are the results on my laptop:
As you can see in the table above the fastest way of doing read queries is by giving the workload as much cores as possible to increase the amount of reads which can be done in the least amount of time. MAXDOP 1 is the slowest run, with MAXDOP 2 being 17 SECONDS faster. There is not a lot of difference between the run of MAXDOP 4 and MAXDOP 2 because of hardware limitations.
Because of the read intended workload of a datawarehouse, we should not put our MAXDOP to 1. As seen in the test above, the more cores you have, the more read throughput you will have. This will make your queries in your DWH go faster. In these test we were 60% faster when using multiple cores for our workload instead of one core.
I would also like to know what MAXDOP setting is best when just writing data(UPDATE INSERT DELETE). To test this, I created 3 test scenarios:
1. 100 Iterations 1 thread OLTP workload MAXDOP 1
2. 100 Iterations 1 thread OLTP workload MAXDOP 2
3. 100 Iterations 1 thread OLTP workload MAXDOP 4
These are the results on my laptop:
When inserting/updating/deleting data, the fastest option is running queries in a serial way. When running these processes in parallel, we will increase the amount of CPU needed for the query and this will increase the time elapsed. When writing to in-memory tables as we are doing in this test, we see that writing data in amount of CPU time is 50% faster on MAXDOP 1. This means that going in a serial way will be faster than doing this in a parallel way.
We should use caution wwith concern to our MAXDOP setting. We could see that for the two different workloads, we have different needs. The read intensive workload will, in most cases, benefit from a parallel execution, while the write intensive workload will benefit of a serial execution. Because most systems are not READ or WRITE only, I generally think the way to go is try to filter out those read-intensive high cost plans and allow these plans to make use of parallel execution. Thus limiting the amount of queries to be executed in parallel, but not removing the option by default. This way we will not hold back our OLTP queries and we will be able to speed up those queries that can really benefit of the parallel execution. This means you should always use the MAXDOP setting in combination with your COST THRESHOLD FOR PARALLELISM SETTING. There is, however, no golden bullet for these settings and they will always need to be tested out to cope with the workload of your system.
Thank you for reading and stay tuned!
© 2023 Kohera
Crafted by
© 2022 Kohera
Crafted by
Cookie | Duration | Description |
---|---|---|
ARRAffinity | session | ARRAffinity cookie is set by Azure app service, and allows the service to choose the right instance established by a user to deliver subsequent requests made by that user. |
ARRAffinitySameSite | session | This cookie is set by Windows Azure cloud, and is used for load balancing to make sure the visitor page requests are routed to the same server in any browsing session. |
cookielawinfo-checkbox-advertisement | 1 year | Set by the GDPR Cookie Consent plugin, this cookie records the user consent for the cookies in the "Advertisement" category. |
cookielawinfo-checkbox-analytics | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics". |
cookielawinfo-checkbox-functional | 11 months | The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional". |
cookielawinfo-checkbox-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-others | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other. |
cookielawinfo-checkbox-performance | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance". |
CookieLawInfoConsent | 1 year | CookieYes sets this cookie to record the default button state of the corresponding category and the status of CCPA. It works only in coordination with the primary cookie. |
elementor | never | The website's WordPress theme uses this cookie. It allows the website owner to implement or change the website's content in real-time. |
viewed_cookie_policy | 11 months | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |
Cookie | Duration | Description |
---|---|---|
__cf_bm | 30 minutes | Cloudflare set the cookie to support Cloudflare Bot Management. |
pll_language | 1 year | Polylang sets this cookie to remember the language the user selects when returning to the website and get the language information when unavailable in another way. |
Cookie | Duration | Description |
---|---|---|
_ga | 1 year 1 month 4 days | Google Analytics sets this cookie to calculate visitor, session and campaign data and track site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognise unique visitors. |
_ga_* | 1 year 1 month 4 days | Google Analytics sets this cookie to store and count page views. |
_gat_gtag_UA_* | 1 minute | Google Analytics sets this cookie to store a unique user ID. |
_gid | 1 day | Google Analytics sets this cookie to store information on how visitors use a website while also creating an analytics report of the website's performance. Some of the collected data includes the number of visitors, their source, and the pages they visit anonymously. |
ai_session | 30 minutes | This is a unique anonymous session identifier cookie set by Microsoft Application Insights software to gather statistical usage and telemetry data for apps built on the Azure cloud platform. |
CONSENT | 2 years | YouTube sets this cookie via embedded YouTube videos and registers anonymous statistical data. |
vuid | 1 year 1 month 4 days | Vimeo installs this cookie to collect tracking information by setting a unique ID to embed videos on the website. |
Cookie | Duration | Description |
---|---|---|
ai_user | 1 year | Microsoft Azure sets this cookie as a unique user identifier cookie, enabling counting of the number of users accessing the application over time. |
VISITOR_INFO1_LIVE | 5 months 27 days | YouTube sets this cookie to measure bandwidth, determining whether the user gets the new or old player interface. |
YSC | session | Youtube sets this cookie to track the views of embedded videos on Youtube pages. |
yt-remote-connected-devices | never | YouTube sets this cookie to store the user's video preferences using embedded YouTube videos. |
yt-remote-device-id | never | YouTube sets this cookie to store the user's video preferences using embedded YouTube videos. |
yt.innertube::nextId | never | YouTube sets this cookie to register a unique ID to store data on what videos from YouTube the user has seen. |
yt.innertube::requests | never | YouTube sets this cookie to register a unique ID to store data on what videos from YouTube the user has seen. |
Cookie | Duration | Description |
---|---|---|
WFESessionId | session | No description available. |