Rudi Bruchez
#sqlsat
What I have seen so far in my
performance tuning audits
About me Rudi Bruchez
[email protected] www.babaluga.com
Video Training
Agenda
What I have seen for far, in my performance audits
• A museum of
horrors
Why this session
I’d like to pinpoint the
main problem
We should take
RDBMS more seriously
Using the diagnostic queries
https://github.com/rudi-bruchez/tsql-scripts
Hardware, or not?
Hardware
The DBA says the
problem is the hardware
The Infrastructure team
says the problem is the
database
Where is the problem?
Hardware
One common disk problem
Everything is on C:
WaitType Wait_S Resource_S Signal_S WaitCount Percentage
PAGEIOLATCH_SH 149404,67 149007,7 396,97 70850679 23,78
IO_COMPLETION 122404,08 122137,52 266,56 33420851 19,48
Look at virtual_io_file_stats
No supervision
Come on! Centreon, Zabbix… are free!
CPU
Disk
Buffer cache hit ratio + page life expectancy
Plan cache objects
No supervision
SQL Server Configuration
and maintenance
Server Configuration
Optimize for adhoc workloads – Nobody knows it
Parallelism – Change that, please!
Perform maintenance tasks – Do it !
Do you need to set max memory?
Compress the backups!
Perform Maintenance Task Permission
Server Security
❖All applications connect through sa
❖Or, everybody is sysadmin
❖Nobody cares about backups
Database Configuration
❖Yes, some people auto shrink
❖Yes, some people auto close
❖What is Update stats asynchronously?
Auto Close and Auto Shrink
SELECT COUNT(*)
FROM sys.databases d
WHERE d.is_auto_close_on = 1;
SELECT COUNT(*)
FROM sys.databases d
WHERE d.is_auto_shrink_on = 1;
Auto Close and Auto Shrink
https://support.microsoft.com/en-
us/help/315512/considerations-for-the-autogrow-
and-autoshrink-settings-in-sql-server
https://www.sqlskills.com/blogs/paul/why-you-
should-not-shrink-your-data-files/
Autogrowth
We love default values
Recovery Model and Backups
Recovery Model, what does it mean?
Why should I take transaction log backups?
Why is my log file growing?
Recovery Model and Backups
CHECKPOINT
SIMPLE MODE FULL MODE
Recovery Model and Backups
CHECKPOINT
FULL MODE
They told me to create filegroups
File Name Physical Name file_id Filegroup Name
Account_FO G:\DATA\Account_FO.mdf 1 PRIMARY
Account_FO_log G:\LOG\Account_FO_Log.ldf 2
Account_FO_Tables G:\DATA\Account_FO_tables.ndf 3 TABLES
Account_FO_Index G:\INDEX\Account_FO_Index.ndf 4 INDEX
Database schema
Choice of data types
is your apartment too big?
Love of NVARCHAR
Still a lot of TEXT and IMAGE columns
How many columns can I add?
This one had 628 columns
ADDRE ADDRE ADRESS ZIP1 CITY1 COUNT ADDRE ADDRE ADDRE ZIP2 CITY2 COUNT
SS11 SS12 13 RY1 SS21 SS22 SS23 RY2
How many columns can I add?
In memoriam
Another customer, 2018
TABLE_NAME NB_COLUMNS
GBRESCR2 485
CFGASE 479
AMLTR 462
AFOLDER 389
BSUBPROG 382
GTADOCC 340
ABIFALGA 329
SOBARD 311
BENCHTTT It goes on …
303
What « relationships » means?
In memoriam
The infamous State column
STATE
ACTIVE
ACTIVE
WAITING
ACTIVE
WAITING
WAITING
NVARCHAR
WAITING
BLOCKED BY MATILDA
BLOCKED
Of course
WAITING
ACTIVE
ACTIVE
ACTIVE
Love of NVARCHAR
CREATE TABLE [dbo].[log_events_20161222](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[date] [datetime] NULL,
[id_evenement] [nvarchar](50) NOT NULL,
[id_produit] [nvarchar](50) NOT NULL,
[reference] [nvarchar](50) NOT NULL,
[id_categorie] [nvarchar](50) NOT NULL,
[libelle] [nvarchar](1000) NOT NULL,
[origine] [nvarchar](50) NULL,
[reference_piece] [nvarchar](1000) NULL,
CONSTRAINT [log_events_20161222] PRIMARY KEY CLUSTERED ([id]) ON [TABLES]
) ON [TABLES]
CHAR still exists
Table Name # Records Reserved (KB) Data (KB) Indexes (KB)
dbo.MDOC 55 203 565 53 258 704 26 288 872 26 968 824
Column CHAPR2 is CHAR(25) …
SELECT LEN(CHAPR2) as length,
COUNT_BIG(*) as nb
FROM MDOC WITH (READUNCOMMITTED)
GROUP BY LEN(CHAPR2)
ORDER BY LEN(CHAPR2);
CHAR still exists
Length Row Count
NULL 2 796 701
0 19 726 562
1 985 622
2 495 207
3 9 708 965
4 4 786 845
5 706 131
6 5 023 981
7 4 029 525
8 1 675 090
9 555 746
10 462 201
11 3 106 950
…
Let’s show it to the customer
-- waste analysis
;WITH cte AS (
SELECT LEN(CHAPR2) as length,
COUNT_BIG(*) as nb
FROM MDOC WITH (READUNCOMMITTED)
GROUP BY LEN(CHAPR2)
)
SELECT
SUM((25-length) *nb) / 1024.0 / 1024 as lost_mb,
Lost_mb Row_count
SUM(nb) as row_count
FROM cte ; 1070.42 55 204 335
Are we Doomed?
You have
ROW
compression!
ROW Compression
CHAR(50) INT
Value Salve 1
Uncompressed 50 4
Compressed 5 + 4 bits 1 + 4 bits
demos-nossms\data compression gain.xlsx
Junk tables
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME
XXX_Prod dbo ___following_purge
XXX_Prod dbo __MigrationHistory
XXX_Prod dbo _to_delete_bo_MultisBlocs_Blocs_s
XXX_Prod dbo _to_delete_bo_MultisBlocs_s
XXX_Prod dbo _CleaningListeTableOperation
XXX_Prod dbo _CleaningListeTableOperation2
XXX_Prod dbo _user_login
XXX_Prod dbo _user_rights
XXX_Prod dbo A_exclusions_to_update
XXX_Prod dbo to_delete_TARIF_05092016
XXX_Prod dbo clients_modification_mdp999999
Why is my database so big?
No, it is not Big Data, it is Oblivion
Oblivion #2 – no archiving
Very often, no archiving is done. Too time consuming.
Damned foreign keys !
SELECT
YEAR(creation_date) as Annee,
COUNT(*) as nb
FROM mytable WITH (READUNCOMMITTED)
GROUP BY YEAR(creation_date)
ORDER BY YEAR(creation_date);
Archiving…
Since 2016 sp1, partitioning is in all editions
Backup by filegroup
Manually partitioned views
Manual Archiving How To
SELECT 1
WHILE @@ROWCOUNT > 0
BEGIN
DELETE TOP (1000) FROM Invoice
OUTPUT DELETED.* INTO InvoiceArchive
WHERE InvoiceDate < DATEADD(YEAR, -1, CURRENT_TIMESTAMP);
END;
GO
Partitioned View How To
CREATE VIEW all_data
AS
SELECT * FROM dbo.table
UNION ALL
SELECT * FROM
base_archive.dbo.archive
It was slow, I created a lot of indexes
Let’s check index usage…
tbl idx seeks scans updates last_upd size_kb
dbo.PIEC PIEC_PICPRT 0 0 598 097 06/09/2018 415 924,22
I don’t know how to use Missing Index
Should you trust Missing Indexes?
/*
USE [PachaDataFormation]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [Inscription].[Inscription] ([ContactId])
INCLUDE
([SessionId],[DecideurInscriptionId],[DateAnnulation],[Remise],[Present],[DateCre
ation],[ReferenceCommande],[ConventionEnvoyee],[ConvocationEnvoyee],[ListeAttente
],[FeuilleEmargement])
GO
*/
Maintenance
We hate the transaction log
Whe are rebuilding indexes everyday
We love shrinking!
Let’s put everything in a maintenance plan
We love Sweden. Look at the parameters!
We hate the transaction log
Index Maintenance
Analyze fragmentation first
• < 5% do nothing
• 5 – 30% reorganize
• > 30% rebuild
Beware of heap fragmentation
/diagnostics/database-health/forwarded-records.sql
Index Maintenance Done Right
Ola Hallengren
EXECUTE dbo.IndexOptimize
@Databases = 'USER_DATABASES',
Look at the parameters!
EXECUTE dbo.IndexOptimize
@Databases = 'USER_DATABASES, -archive’,
@FragmentationLow = NULL,
@FragmentationMedium =
'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE’,
@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE’,
@Indexes = 'ALL_INDEXES, -%.Archive.%’,
@LogToTable = 'Y’,
@TimeLimit = 3600, -- seconds = 1h
@SortInTempdb = 'Y’,
@MaxDOP = 0,
@DatabaseOrder = 'DATABASE_SIZE_DESC’,
@AvailabilityGroups = '%AG%’,
@MinNumberOfPages = 50
We are rebuilding indexes everyday
After a few days, performances
drop
We restart SQL Server
We tried to defragment indexes,
it helps
Let’s do it as often as possible
We are rebuilding indexes everyday
Recompile queries
Rebuilds statistics Invalidates cached plans
Which Stats Were Used?
SQL Server 2017 and SQL Server 2016 SP2.
SQL Server 2017 CU3, fix for queries with LIKE predicates not
showing loaded statistics
IF CardinalityEstimationModelVersion > "70"
https://github.com/rudi-bruchez/tsql-
scripts/blob/master/diagnostics/database-
health/statistics.sql
How to upgrade statistics?
UPDATE STATISTICS
sp_updatestats
Ola Hallengren again
Update Statistics with Ola
EXECUTE dbo.IndexOptimize
@Databases = 'USER_DATABASES’,
@FragmentationLow = NULL,
@FragmentationMedium = NULL,
@FragmentationHigh = NULL,
@UpdateStatistics = 'ALL’,
@OnlyModifiedStatistics = 'Y’,
@StatisticsModificationLevel = 5 -- %
We love Shrinking
That’s All, Folks!