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.
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.
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:
toystore
database and select the Automatic Tuning option from the Support + Troubleshooting section:In the Automatic tuning blade:
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:
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.
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 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.
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.
A natively compiled procedure syntax is given in the following figure:
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, 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.
In a row-store, this is how data is stored on disk:
However, in a column-store, the same information is stored as:
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 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 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);
To learn more about Column Store indexes, refer to: https://docs.microsoft.com/en-us/sql/t-sql/statements/create-columnstore-index-transact-sql.
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.
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:
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"
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.
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:
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())
Sales.Orders_inmem
table using the ostress utility described earlier in the chapter: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())
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.
18.119.131.10