How to deploy schema changes without scheduled downtime
- Introduction
- Blue-Green Deployment
- Blue-Green Deployment (Details)
- Keep Changes OLTP-Friendly
- Stage and Switch
- Careful Batching
Using the Blue-Green deployment method, database changes are decoupled from applications changes. That leaves us with one last challenge to tackle. The schema changes have to be performed while the application is online. It’s true that you can’t always write an online script for every kind of schema change you want.
The challenge of writing online schema changes is essentially a concurrency problem and the guiding principle I follow is: Do whatever you need to do, but avoid excessive blocking.
Locks Are Hot Potatoes
You can’t hold them for long. This applies to schema changes too. Logically if you don’t hold a lock long, you can’t block activity. One exception might be the SCH-M lock which can participate in blocking chains:
SCH-M locks
There are two main kinds of SQL queries. SELECT/INSERT/UPDATE/DELETE statements are examples of Data Manipulation Language (DML). CREATE/ALTER/DROP statements are examples of Data Definition Language (DDL).
With schema changes – DDL – we have the added complexity of the SCH-M lock. It’s a kind of lock you don’t see with DML statements. DML statements take and hold schema stability locks (SCH-S) on the tables they need. This can cause interesting blocking chains between the two types where new queries can’t start until the schema change succeeds:
Some suggestions:
- Don’t rebuild indexes while changing schema
- Rely on the OLTP workload which has many short queries. In an OLTP workload, the lead blocker shouldn’t be a lead blocker for long. Contrast that with an OLAP workload with long-running and overlapping queries. OLAP workloads can’t tolerate changing tables without delays or interruptions.
- When using Enterprise Edition, use ONLINE=ON for indexes. It takes and holds a SCH-M lock only briefly.
Changes to Big Tables
Scripts that change schema are one-time scripts. If the size of the table is less than 50,000 rows, I write a simple script and then move on.
If the table is larger, look for metadata-only changes. For example, these changes are metadata-only changes:
- New columns that are nullable
- New columns with a default value
- Some column alterations with Enterprise Edition (see Altering Text Columns: Only a Metadata Change)
If a table change is not a meta-data change, then it’s a size-of-data change. Then it’s time to get creative. Look for my other post in this series for an example of batching and an example of a column switcheroo.
Pragmatism Example
If you think “good enough” is neither, you may want to skip this section. There are some schema changes that are still very difficult or impossible to write online. With some creativity, we’ve always been able to mitigate these issues with shortcuts and I want to give an example which I think is pretty illustrative.
When a colleague asked for a rowversion
column on a humongous table. We avoided that requirement by instead creating a datetime
column called LastModifiedDate. Since 2012, new columns with constant default values are online. So we added the column with a constant default, and then changed the default value to something more dynamic:
alter table dbo.MYTABLE add LastModifiedDate DATETIME NOT NULL CONSTRAINT DF_TABLE_LastModifiedDate DEFAULT '20000101' alter table dbo.MYTABLE drop CONSTRAINT DF_TABLE_LastModifiedDate; alter table dbo.MYTABLE add CONSTRAINT DF_TABLE_LastModifiedDate DEFAULT GETUTCDATE() for LastModifiedDate; |
It’s a cool situation because it seems like the column has two defaults, one constant default for rows with missing values. And another definition to be used for new rows:
select pc.default_value, d.definition as [default definition] from sys.system_internals_partitions p join sys.system_internals_partition_columns pc on p.partition_id = pc.partition_id join sys.default_constraints d on d.parent_object_id = p.object_id and d.parent_column_id = pc.partition_column_id where p.object_id = object_id('MYTABLE') and pc.partition_column_id = 2 /* Gives default_value default definition ------------- ------------------ 2000-01-01 (getutcdate()) */ |
So be creative and pragmatic. Successful 100% online schema changes involve creativity and close collaboration between everyone involved.
[…] Read MoreĀ (Community […]
Pingback by 100 Percent Online Deployments: Keep Changes OLTP-Friendly - SSWUG.ORG — February 26, 2018 @ 2:00 am