
The first release of
SQL Server 2016 took place on June
1 , which brought to the familiar development a large number of innovations, including the long-announced
Stretch Database technology that allows you to dynamically transfer hot and cold data from
SQL Server to
Azure .
From a marketing point of view, the
Stretch Database is very well advertised. They cited rational arguments that, as historical data is accumulated in the database, the complexity and cost of its operation increases. And they offered a reasonable solution - automatic transfer of archived data as they become obsolete in the “cloud”. Honestly, I liked the idea.
')
Beginning with
SQL Server 2016 RC0, I started testing
Stretch Database technology on two projects that I help develop. The first is the working time tracker for which the
OLTP load is characteristic, the second is the internal project with a
DW load.
On a simple example, consider how everything works. The first thing we need to do is to allow the use of the
Stretch Database , since by default this functionality is disabled on the server:
EXEC sp_configure 'remote data archive' , '1' GO RECONFIGURE GO
There is no need to restart the server. Next, create a test database:
USE [master] GO IF DB_ID('StretchDB') IS NOT NULL BEGIN ALTER DATABASE StretchDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE DROP DATABASE StretchDB END GO CREATE DATABASE StretchDB /* COLLATE Cyrillic_General_CI_AS */ GO USE StretchDB GO CREATE TABLE dbo.ErrorLog ( LogID INT IDENTITY PRIMARY KEY , PostTime DATETIME NOT NULL DEFAULT GETDATE() , UserName NVARCHAR(100) NOT NULL , ErrorMessage NVARCHAR(MAX) COLLATE Ukrainian_BIN NOT NULL ) GO INSERT INTO dbo.ErrorLog (UserName, ErrorMessage) VALUES (N'sergeys', N'Azure row')
And we call the
Stretch Database wizard from
SSMS 2016 :

We are warned in advance to successfully set up
Stretch Database you need to have:
- admin privileges on the server (everything was done with sysadmin rights)
- active Azure subscription
- allow access for SQL Server to the Internet

In the next step of the wizard, the first disappointment awaits us:

We cannot transfer data from our table to
Azure due to “some”
limitations in the
Stretch Database technology. We list the main ones (
in bold, I highlighted the restrictions that are not taken into account in the certificate):
Placing data in
Azure is not supported for tables that:
- contain more than 1,023 columns or more than 998 indices
- contain FILESTREAM data
- participate in replication or use Change Tracking or Change Data Capture
- optimized for memory placement ( in-memory tables)
- have always encrypted columns
In addition, the table can not be used:
- all user data types ( CLR , user-defined types )
- TEXT , NTEXT , IMAGE
- TIMESTAMP
- SQL_VARIANT
- XML
- GEOMETRY , GEOGRAPHY , HIERARCHYID
- SYSNAME
- COLUMN_SET
- COMPUTED columns
- SEQUENCE column
- DEFAULT and CHECK collections
- Full-Text Search , XML and Spatial Indexes
- there should be no index views on the table
- foreign keys that refer to the table (for example, for the OrderDetail child table, you can enable Stretch , but for the parent, you cannot already order)
As it turned out, our problem was at the
DEFAULT constraint. Re-create the table and try again:
DROP TABLE IF EXISTS dbo.ErrorLog CREATE TABLE dbo.ErrorLog ( LogID INT IDENTITY PRIMARY KEY , PostTime DATETIME NOT NULL , UserName NVARCHAR(100) NOT NULL , ErrorMessage NVARCHAR(MAX) COLLATE Ukrainian_BIN NOT NULL ) GO INSERT INTO dbo.ErrorLog (PostTime, UserName, ErrorMessage) VALUES (GETDATE(), N'sergeys', N'Azure row')
Now the wizard allows to select a table:

But warns that:
Uniqueness is not enforced for UNIQUE constraints and PRIMARY KEY constraints . For this reason, it is desirable for
Stretch tables to create a
PRIMARY KEY using the
IDENTITY field, or, at worst, use
UNIQUEIDENTIFIER .
Next, we are asked to authorize and automatically create a
SQL Server in
Azure :

Let's manually create a
SQL Server on the
Azure Portal :

And select the created server in the wizard:

Let's set a master password, which we still need in the future:

We indicate the exceptions to be added to the firewall:

And here we are at the finish line:

But when setting up, we are protected by another restriction about which, alas, no one wrote in the help:

The log was the following error:
Configure Stretch on the Database StretchDB Status : 'Failed' Details : Task failed due to following error: Alter failed for Database 'StretchDB': 'Cyrillic_General_CI_AS' is not a supported collation. ALTER DATABASE statement failed.
When creating the database, I clearly did not indicate a coluscheon and it turned out that
Cyrillic_General_CI_AS is not supported, however, like many others. As a result of testing, it turned out that
Stretch Database will not work if
collation is used in database properties:
- any Windows collations ( Latin1_General_100_CI_AS , ...)
- any AI and BIN collations
- there are selective problems with CS collations
For everything to work stably, it is advisable to use only
SQL Server collations . I did not check them all, but when using
SQL_Latin1_General_CP1_CI_AS I did not have any problems:
ALTER DATABASE StretchDB COLLATE SQL_Latin1_General_CP1_CI_AS
After changing the
COLLATE for the base, re-launch the wizard and a miracle:

Now let's try to track how data migrates to
Azure via a monitor:

or a separate system view
sys.dm_db_rda_migration_status :
SELECT * FROM sys.dm_db_rda_migration_status WHERE table_id = OBJECT_ID('dbo.ErrorLog') AND database_id = DB_ID()
In
RC3 there was a bug and if the
COLLATE columns did not match the
COLLATE database, then the data was not transferred to
Azure ,
sys.dm_db_rda_migration_status was constantly growing and was not cleared.
In our example, one of the columns has a
COLLATE , which is not the same as what is set in the database properties. Because of this, the data transfer periodically will fall by mistake:
migrated_rows start_time_utc end_time_utc error_number error_severity error_state
But after several such unsuccessful attempts will be successful:
migrated_rows start_time_utc end_time_utc error_number error_severity error_state
From this we can conclude that the bug in
RTM was not fully fixed, so it is highly desirable to set the same
COLLATE for all columns as for the base.
At this stage, we tried one of the ways to create a
Stretch table. By the way of the script, all this is done much easier and faster:
USE StretchDB GO CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'VasyaPupkin12' GO CREATE DATABASE SCOPED CREDENTIAL azure WITH IDENTITY = N'server_name', SECRET = N'VasyaPupkin12' GO ALTER DATABASE StretchDB SET REMOTE_DATA_ARCHIVE = ON ( SERVER = 'server_name.database.windows.net', CREDENTIAL = azure ) GO ALTER TABLE dbo.ErrorLog SET (REMOTE_DATA_ARCHIVE = ON (MIGRATION_STATE = OUTBOUND))
The main thing is to pre-set permissions for our
Azure server:

Now let's see what changes have occurred with the table ...
Whenever the data gets into the table, it is physically located on the local server for some time, and then automatically transferred to
Azure . This can be easily seen using
Live Query Statistics features implemented in
SSMS 2016 :

After 5-10 seconds:

If you look at the execution plan, then all the functionality of the
Stretch Database is no longer a secret: a linked server and a separate component are created that feeds data into
Azure .
What restrictions do we get:
- you can’t be able to make it up.
- Stretch-enabled table on a linked server
It turns out that we can only insert data into the table:
INSERT INTO dbo.ErrorLog (PostTime, UserName, ErrorMessage)
VALUES (GETDATE (), N'sergeys ', N'Local row')
Because any DELETE, UPDATE, TRUNCATE operation will fail with an error:
Msg 14893, Level 16, State 1, Line 6
It can be used.Let's try to make
Stretch a table with a filter that determines what data should be on the local machine and which can be moved to
Azure .
To do this, return all the data from Azure:
ALTER TABLE dbo.ErrorLog SET (REMOTE_DATA_ARCHIVE (MIGRATION_STATE = INBOUND))
Create a function and a column that will store a sign that this data can be migrated to the cloud:
CREATE FUNCTION dbo.fn_stretchpredicate(@IsOld BIT) RETURNS TABLE WITH SCHEMABINDING AS RETURN SELECT 1 AS is_eligible WHERE @IsOld = 1 GO ALTER TABLE dbo.ErrorLog ADD IsOld BIT GO
When re-placing the table in the cloud, specify the filter:
ALTER TABLE dbo.ErrorLog SET ( REMOTE_DATA_ARCHIVE = ON ( FILTER_PREDICATE = dbo.fn_stretchpredicate(IsOld), MIGRATION_STATE = OUTBOUND ) )
After the team worked try to see what has changed. Firstly, the conditions of migration are not set down yet, you can do anything with local data. Delete or update:
UPDATE dbo.ErrorLog SET IsOld = 0
and when we need to selectively transfer to
Azure :
UPDATE TOP(1) dbo.ErrorLog SET IsOld = 1
The main thing to remember: what is already in the cloud can no longer be returned by simple
UPDATE :
Msg 14875, Level 16, State 1, Line 14 DML operation failed because it would have affected one or more migrated (or migration-eligible) rows.
Let's see how much space our data takes:
EXEC sys.sp_spaceused 'dbo.ErrorLog', @mode = 'LOCAL_ONLY' EXEC sys.sp_spaceused 'dbo.ErrorLog', @mode = 'REMOTE_ONLY'
name rows reserved data index_size unused
Now we will try to use the filter in our queries to make accessing
Stretch tables more efficient:
SELECT * FROM dbo.ErrorLog WHERE IsOld = 0 SELECT * FROM dbo.ErrorLog WHERE IsOld = 1
If you look at the execution plan, in the first case you do not need to make a connection to the linked server, which should work an order of magnitude faster:

Have we really found an option when using
Stretch Database will be justified? In practice, not everything is so good, because both requests are processed equally slowly due to a bug (
upd 07/26/2016: Update # 1 is necessary to fix
KB3174812 ):
Table 'ErrorLog'. Scan count 1, logical reads 2, physical reads 0, .... SQL Server Execution Times: CPU time = 0 ms, elapsed time = 1225 ms. SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. Table 'ErrorLog'. Scan count 1, logical reads 2, physical reads 0, .... SQL Server Execution Times: CPU time = 0 ms, elapsed time = 1104 ms. SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms.
For any access to the
Stretch table, you are connected to
Azure :


Because of this behavior, any queries involving
Stretch will slow down or fall on a timeout. The limitations I left in reserve will seem ridiculous against the background of the performance problem described above:
- Stretch-enabled tables
- indexes are not on the remote table
Small resultsAs already said above, before that on two projects I tried to use the
Stretch Database . In the tracker lived a large table with segments of working time. Appeal to it is quite active, so
Stretch Database is not elementary due to its poor performance. They
struggled with the size of this table very simply: they deleted all foreign keys and re-created the table with the cluster
COLUMNSTORE index (as a result, they squeezed the table 12 times).
On an internal project, a feint with section switching came in handy, which I liked to use. The meaning is as follows ... two tables are created (
A and
B ). We work actively with table
A and then switch the section to
B , which is a
Stretch table:
DROP TABLE IF EXISTS A GO CREATE TABLE A (val INT PRIMARY KEY) GO INSERT INTO A SELECT 1 GO DROP TABLE IF EXISTS B GO CREATE TABLE B (val INT PRIMARY KEY) GO ALTER TABLE B SET (REMOTE_DATA_ARCHIVE = ON (MIGRATION_STATE = OUTBOUND)) GO ALTER TABLE A SWITCH TO B GO SELECT * FROM A SELECT * FROM B
In fact, we are distinguished between historical and operational data, and it turns out that we are getting around the performance bug (although there are still disadvantages to this solution - switching the section to a non-empty table will not work).
A big post came out and it's hard to read, but here all the problems I encountered in two or three months working with the
Stretch Database are collected. For this reason, I hope that these works will be useful to someone in the future. If he has a desire to supplement him with something, I will be glad to comment.
Everything was tested on
Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (X64) .
If you want to share this article with an English-speaking audience:
SQL Server 2016 Stretch Database