Tuning an Azure SQL Database

Automatic Tuning

Azure SQL Database Automatic tuning utilizes artificial intelligence to continuously monitor and improve queries executed on an Azure SQL Database.

Automatic tuning observes the workload and applies recommendations to speed up performance. The recommendations are applied when the database activity is low so that there aren't any performance impacts when applying recommendations.

The following are the options which are available for automatic tuning.

Create Index

Automatically identify and implement missing indexes to improve workload performance. It also verifies whether or not the indexes created have any improvement on the performance.

Drop Indexes

Automatically identifies and removes duplicate, redundant, and unused indexes.

Force Last Good Plan

Automatically identifies the queries using the execution plan which are slower than the previous good plan and forces the last known good plan to improve the query's performance.

Automatic tuning has to be manually switched ON and is set to OFF by default. Also, it gives you an option to either automatically or manually apply the recommendations.

To enable automatic tuning, follow the following instructions:

  1. Open a browser and log in to Azure Portal (https://portal.azure.com) with your Microsoft Azure credentials.
  2. Open the toystore database and select the Automatic Tuning option from the Support + Troubleshooting section:

    In the Automatic tuning blade:

    • Under Inherit from, select Don't inherit.
    • Under "Configure the automatic tuning option", Switch ON Force Plan and the Create Index and Drop Index options.
    • Click Apply to save the automatic tuning settings.
      ALTER DATABASE current SET AUTOMATIC_TUNING = CUSTOM
      ALTER DATABASE current SET AUTOMATIC_TUNING (FORCE_LAST_GOOD_PLAN = ON, CREATE_INDEX = ON, DROP_INDEX = ON)

    Alternatively, you can also enable automatic tuning by executing the following query in the toystore database:

    Force Last Good Plan

In-Memory Technologies

In-memory technologies were first introduced in SQL Server 2012, and are built into the SQL Server database engine. They can improve performance significantly for workloads such as data ingestion, data load, and analytical queries.

In Azure SQL Database, in-memory technologies are only available in the Premium service tier.

Azure SQL Database has the following in-memory technologies: In-Memory OLTP and Columnstore Indexes. Let's talk about these briefly.

In-Memory OLTP

As the name suggests, In-Memory OLTP improves performance for transaction processing scenarios where a major portion of the workload consists of inserts, updates, and deletes.

In-Memory OLTP is achieved by using one of the following objects.

Memory-Optimized Tables

Memory-optimized tables are used to store data in memory. All of the data in a memory-optimized tables resides in memory. Memory-optimized tables and disk-based tables can reside within the same database simultaneously.

A table is defined as being a memory-optimized table at the time of its creation. A memory-optimized table creation script is shown in the following code snippet:

CREATE TABLE dbo.Orders
(  
    OrderId   int not null IDENTITY  
        PRIMARY KEY NONCLUSTERED,  
    CustomerId int not null,  
    OrderDate  datetime not null,
    Quantity int not null  
)  
    WITH  
        (MEMORY_OPTIMIZED = ON,  
        DURABILITY = SCHEMA_AND_DATA);

The Memory_Optimized keyword specifies whether or not the table is a memory-optimized table. The durability refers to retaining only Schema or Schema and Data for the memory-optimized table. As the table is in memory, the data will go out of memory if a machine is restarted. However, if the durability is set to Schema_And_Data, SQL Server makes sure that the data isn't lost.

There are two types of indexes allowed on an in-memory table, and these are hash and non-clustered indexes. The indexes don't contain data rows. Instead, they contain memory pointers to the data rows. The indexes are also in memory.

Hash indexes are used to optimize point lookups and aren't suitable for range lookups. Non-clustered indexes are best suited for range lookups.

Memory-optimized tables can be accessed through the regular DDL and DML commands.

Natively Compiled Procedures

A regular or InterOP stored procedure is compiled and the plan is cached within the SQL server. However, a natively compiled procedure is compiled into a DLL and is loaded in memory. This further improves DML command performance on memory-optimized tables.

Note

An InterOP stored procedure here refers to the regularly stored procedure which is compiled, and the generated plan is cached into the SQL Server plan cache.

This is unlike natively compiled procedures where the procedure is compiled into DLL and loaded into memory.

A natively compiled procedure syntax is given in the following figure:

Natively Compiled Procedures

Figure 9.1: Syntax of a Natively Compiled Procedure. This image has been sourced from https://www.microsoft.com/en-in/download/confirmation.aspx?id=41704

A natively compiled procedure contains the regular T-SQL code as the InterOP or regular procedures; however, it's defined differently at the time of creation.

The term Native_Compilation defines that the procedure is a natively compiled procedure and is to be compiled into DLL.

A natively compiled procedure should be schema-bounded and should have the execution context.

A natively compiled procedure is always executed in a snapshot transaction isolation level.

Memory-optimized tables and natively compiled procedures, which together can be used to speed up an OLTP workload and make it up to 20 times faster.

Columnstore Indexes

Columnstore indexes, introduced in SQL Server 2012 (non-clustered), use columnar storage instead of regular row-based storage to store data. A row-store has rows with multiple columns arranged sequentially on a page; however, in column-store, values of a single column (from different rows) are stored contiguously.

Note

Clustered column store indexes were added in SQL Server 2014, whereas non-clustered column store indexes were introduced in SQL Server 2012.

In a row-store, this is how data is stored on disk:

  • Abel, Doctor, WA
  • Abha, Engineer, UT
  • Adrian, Doctor, HA

However, in a column-store, the same information is stored as:

  • Abel, Abha, Adrian
  • Doctor, Engineer, Doctor
  • WA, UT, HA

This allows faster response times and less storage for data warehouse scenarios.

Column-store has better compression compared to row-store, as values of the same data type compress better than the values of different data types (row-store contains columns with different data types while column-store has values from the same column).

This improves query performance, as only those pages which contain the selected column values are scanned or fetched, thereby decreasing the reads.

For example, consider the following query:

SELECT Name, profession FROM Employees

The preceding query will only touch pages with the Name and Profession columns if run against a column-store. However, against a row-store, the query will run through all the pages. This significantly improves the performance in data warehouse scenarios with huge tables.

There are two types of Columnstore indexes: clustered and non-clustered.

Clustered Columnstore Index

Clustered Columnstore indexes store the entire table data as column stores. They reduce the storage footprint by up to 10 times of its original size. They can be used on fact tables in a data warehouse to speed up the queries and fit more data into the available storage.

The syntax for creating a clustered column store index is as follows:

CREATE CLUSTERED COLUMNSTORE INDEX CCS_Orders ON [Sales].[Orders]

Non-Clustered Columnstore Index

Non-clustered columnstore indexes are created on sets of tables columns and can co-exist. When introduced in SQL Server 2012, non-clustered column indexes weren't updatable; in other words, if you had a non-clustered column index on a table, you were not allowed to update the data in that table using the DML statements.

However, starting from SQL Server 2016, they are now updateable and can be used to gain real time operational insights on your transactional data. You can query the operational data directly instead of spending time doing ETL and loading the data into a data warehouse. You can do all of this without any operation impacts.

The syntax for creating a non-clustered column store index is as follows:

CREATE NONCLUSTERED COLUMNSTORE INDEX nccsix_CustomerID
ON [Sales].[Orders]  
(CustomerID,ContactPersonID,OrderDate);  

The preceding query creates a non-clustered column store index on customerid, contactpersonid, and orderdate. The column store structure is stored separately from the table structure.

Activity: Explore the In-Memory OLTP Feature

In this activity, we’ll compare the performance of a disk-based table with a memory-optimized table for an OLTP workload for our toystore database. Let’s consider a case where Mike wants to compare the new In-Memory OLTP feature using the memory optimized tables. But before he does that, to check if it is truly profitable, he wants to compare the performance of disk-based tables and memory-optimized tables. This can be done via the following steps:

  1. Run the following command in a PowerShell console to change the service tier of the toystore database to Premium tier. The in-memory technologies are only available in Premium service tiers:
    PowerShell.exe "C:CodeLesson02ScaleUpAzureSQLDB.ps1" -resourcegroupname toystore -azuresqlservername toyfactory -databasename toystore -newservicetier Premium -servicetierperfomancelevel P1 -AzureProfileFilePath "C:CodeMyAzureProfile.json"
  2. Navigate to C:CodeLesson09InMemoryOLTP and open the CreateObjects.sql file in a new SQL Server Management Studio.

    This query creates the following objects:

    • uspInsertOrders: A traditional disk-based store procedure that inserts new orders, as specified by the numberoforderstoinsert parameter. If the @numberoforderstoinsert is set to 10, then it'll insert 10 new orders into the Sales.Orders table.
    • Orders_Inmem: The memory-optimized version of the Sales.Order table. The schema is the same as that of the Sales.Orders table, however, it has Memory_Optimized set to ON.
    • Customers_Inmem: The memory-optimized version of the Sales.Customers table. The schema is the same as that of the Sales.Customers table, however, it has Memory_Optimized set to ON. All of the existing customers in the Sales.Customers table are inserted into the Sales.Customers_Inmem table.
    • uspInsertOrders_Inmem: This is a natively compiled version of the uspInsertOrders procedure. It inserts a number of orders, as specified by the @numberoforderstoinsert parameter, into the Sales.Orders_Inmem table.
      ALTER DATABASE CURRENT SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT = ON

    The query also runs the following query to automatically map all the lower isolation levels to the snapshot isolation level for memory-optimized tables:

    This changes the databases context to toystore. Press F5 or hit execute to execute the query.

  3. Execute the following command in a PowerShell console. This will insert 10,000 orders into the Sales.Orders table using the ostress utility described earlier in the chapter:
    PowerShell.exe "C:CodeLesson09InMemoryOLTPStart-Workload.ps1 -sqlserver toyfactory -database toystore -sqluser sqladmin -sqlpassword Packt@pub2 -ostresspath '"C:Program FilesMicrosoft CorporationRMLUtilsostress.exe"' -workloadtype disk"

    The workloadtype parameter specifies which procedure is executed. If the value is disk, the InterOP procedure is executed (uspInsertOrders), which inserts a value into the Sales.Orders (disk-based) table.

    Otherwise, if the workloadtype parameter is set to inmem, the natively compiled procedure is executed (uspInsertOrders_inmem), which inserts a value into the Sales.Orders_inmem (memory-optimized) table.

    You should get the following output. The elapsed time may be different in your case:

    Activity: Explore the In-Memory OLTP Feature

    It took 163 seconds to insert 10,000 orders into the disk-based table. You can execute the following query to count the number of orders which have been inserted:

    SELECT COUNT(*) FROM sales.orders WHERE orderdate=CONVERT(date, getdate())
  4. Execute the following command in a PowerShell console. This will insert 10,000 orders into the Sales.Orders_inmem table using the ostress utility described earlier in the chapter:
    Activity: Explore the In-Memory OLTP Feature

    It took only 31 seconds to insert 10,000 records into the memory-optimized table using the natively compiled stored procedure.

    You can execute the following query to count the number of orders inserted into the Sales.Orders_inmem table:

    SELECT COUNT(*) FROM sales.orders_inmem WHERE orderdate=CONVERT(date, getdate())
  5. Navigate to C:codeLesson09InMemoryOLTP and open the Cleanup.sql file in SQL Server Management Studio:
    -- Clean up 
    DROP PROCEDURE IF EXISTS uspInsertOrders_Inmem
    GO
    DROP PROCEDURE IF EXISTS uspInsertOrders
    GO
    DROP TABLE IF EXISTS [Sales].Orders_Inmem
    GO
    DROP TABLE IF EXISTS [Sales].Customers_Inmem
    GO
    -- delete inserted data from the orders table.
    DELETE FROM sales.orders WHERE orderdate=CONVERT(date, getdate())
    GO
    -- Change the database edition to basic
    ALTER DATABASE toystore 
        MODIFY (EDITION = 'basic');

    The script drops the memory-optimized objects, deletes the rows inserted into the Sales.Order table as a part of the activity, and changes the database edition to Basic from Premium.

    This completes the activity.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.131.10