Insert, Update & Delete Performance Joe Chang
Insert, Update and Delete IUD Basics Multi-row Inserts Logical IO count IUD Operations and Indexes IUD Operations and Foreign Keys
Insert Plan – 1 Row Insert Table(…) Values(…) No indexes other than primary key No foreign keys
Insert – I/O Cost Note: difference in I/O cost Insert I/O cost depends On number of row in table! 0 & 1 row > 300 rows
Insert Plan I/O Cost versus Rows I/O cost
Insert – Clustered Index Clustered index more or less same as Table > 320 rows
Insert Plan Cost & Logical I/O Logical I/O count 1 st row Table 'MIC_01'. Scan count 0, logical reads 4, physical reads 0, read-ahead reads 0. 2 nd row Table 'MIC_01'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0. Row ~65,000 Table 'MIC_01'. Scan count 0, logical reads 3, physical reads 0, read-ahead reads 0. Insert Cost Formula I/O: to CPU: per row Total: to Plan cost independent of indexes at low row counts
Disk Setting Enable Write Caching and Enable Advanced Performance has large impact on log write performance 1)Write to disk, continue after confirmation 2)Write to disk, continue immediately Settings vary from Windows 2000 Server Windows XP Windows Server 2003
INSERT & Physical Disk Each standalone INSERT statement must be matched to 1 or more write I/Os to transaction log, may or may not result in write to data, SQL Server may consolidate transaction log entries from separate threads (Process ID or SPIDs) into a single I/O on the transaction log file Log writes for statements inside BEGIN/COMMIT TRANSACTION are consolidated?
BEGIN/COMMIT TRAN Which is faster and more efficient? < 100,000 BEGIN INSERT Table(…) VALUES …) + 1 END BEGIN TRANSACTION < 100,000 BEGIN INSERT Table(…) VALUES …) + 1 END COMMIT TRANSACTION A B
Update UPDATE N1N SET Value = 'ABC123456D‘ WHERE ID = 1 UPDATE MXN SET ID9 = 1 WHERE ID = 1 Non integer values No Compute Scalar for Updates to Clustered Index
Table Update – Index Seek Same as plain Index Seek
Table Update – CS & Top CPU: / row
Table Update
Clustered Index Update Single component, but numbers don’t add up
Update Plan Cost Same cost structure as Insert plus additional Index Seek cost (I/O costs depend on Table density and row count) Clustered Index I/O: CPU: per row Total: Table Index Seek: Compute Scalar: Top: Table Update I/O: CPU: Total: Index Seek cost implied?
Delete
Delete
Multi-row Inserts Compare two separate Insert statements: INSERT N1C(ID,Value) VALUES (321,'TYI539087J') INSERT N1C(ID,Value) VALUES (322,'TYI539087J') With statement below INSERT N1C(ID,Value) SELECT 321,'TYI539087J‘ UNION ALL SELECT 322,'TYI539087J'
Multi-row Inserts – Union All INSERT N1C(ID,Value) SELECT 321,'TYI539087J‘ UNION ALL SELECT 322,'TYI539087J'
Multi-row Inserts
2 rows CPU: 2X I/O: same
Multi-row Inserts
Multi-row Select = CASE ID THEN VALUE = CASE ID THEN VALUE END FROM M2C WHERE ID IN = VALUE FROM M2C WHERE ID = VALUE FROM M2C WHERE ID Plan Cost is lower than 2 separate selects, but actual performance is worse!
Multi-row Delete int = = DELETE MIC WHERE ID IN Has not been tested!
IUD with Additional Indexes IUD ops may need to modify indexes Insert & Delete – always Update – only if modified value is in index Plan costs for low row counts Not dependent on indexes Counter intuitive, but plan not impacted IUD w/larger row counts Plan depends on indexes
Inserts with indexes - I/O count Index depth: Clustered 2, Nonclustered 1 No indexes other than primary key Table 'MIC'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0. 1 Nonclustered index Table 'MIC'. Scan count 0, logical reads 3, physical reads 0, read-ahead reads 0. 2 Nonclustered indexes Table 'MIC'. Scan count 0, logical reads 4, physical reads 0, read-ahead reads 0. 2 I/O for Clustered Index (Index Depth 2) 1 I/O for each nonclustered index at Index Depth 1
Insert with Select Query Primary key – clustered, and 1 nonclustered index Up to ~500 rows INSERT MIC(…) SELECT … FROM M2C > ~505 rows SELECT INSERT
Multiple Indexes
Update w/IX, large row count 600 rows
Update multiple IX, large row count One for each index excluding PK
Spool & Sequence Spool I/O /page Spool CPU /row Sequence CPU /row
Delete w/Index large row count 505 rows 1 NC Index 2 NC Indexes
Foreign Keys ALTER TABLE [dbo].[M2C] ADD CONSTRAINT [FK_M2C_M2D] FOREIGN KEY ( [ID2] ) REFERENCES [dbo].[M2D] ( [ID] ) ON DELETE NO ACTION ON UPDATE NO ACTION
Insert w/Foreign Key Constraint INSERT M2C (…) VALUES (50001,…) Statistics IO: Table 'M2D'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0. Table 'M2C'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0. Index depth 2, both tables FK PK
Insert FK details
Delete w/FK Constraint DELETE M2D WHERE ID = Statistics IO: Table 'M2C'. Scan count 1, logical reads 507, physical reads 0, read-ahead reads 0. Table 'M2D'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads leaf level pages FK PK
Delete–FK & Table Scan compared FK PK
Delete – Reference Table Scan Unusually low cost Expected cost for 506 pages, 50,000 rows From Delete op FK Reference From normal Table scan
Index on Foreign Key CREATE INDEX IX_M2C_ID2 ON M2C(ID2) INSERT M2C (…) VALUES (50001,…) Statistics IO: Table 'M2D'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0. Table 'M2C'. Scan count 0, logical reads 4, physical reads 0, read-ahead reads 0. DELETE M2C WHERE ID = Statistics IO: Table 'M2C'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0.
Delete with Indexed Foreign Key DELETE M2D WHERE ID = Statistics IO: Table 'M2C'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0. Table 'M2D'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0. FK PK
Update with Foreign Key Update Primary Key table Update Foreign Key table FK PK FK PK
Query Cost Model Actual Query Costs in CPU-Cycles Stored Procedure Cost = RPC cost (once per procedure) + Type cost (once per procedure?) + Query cost (once per query) Query – one or more components Component Cost = + Component base cost + Additional row or page costs
INSERT Characteristics Single row INSERT Clustered index, no other indexes No Foreign Keys 2x2.4GHz server Net CPU-cycles cost – excludes RPC cost
Clustered, Heap, Non-Clust. Single row INSERT 1) Clustered index 2) Heap with no indexes 3) Heap with 1 non-clustered index Log write consolidation? Context switch reduction?
INSERT – Multiple Rows Multiple single row INSERT statements per stored proc 8 threads Multiple rows per INSERT statement (UNION ALL) 8 threads
IUD Cost Structure P4/Xeon*Notes RPC cost 240,000Higher for threads, owner m/m Type Cost 130,000once per procedure IUD Base 170,000once per IUD statement Single row IUD300,000Range: 200, ,000 Multi-row Insert Cost per row 90,000cost per additional row *Use Windows NT fibers on INSERT, UPDATE & DELETE cost structure very similar Multi-row UPDATE & DELETE not fully investigated
INSERT Cost Structure Index and Foreign Key not fully explored Early measurements: 50-70,000 per additional index 50-70,000 per foreign key
IUD Summary Consolidate IUD statements where possible Large impact on performance Verify impact of BEGIN/COMMIT TRAN REPEATABLE READ & SERIALIZABLE not tested Index & Foreign Key overhead Some cost on IUD for each index Most app 90% Read, 10% Write? Is FK required for data integrity?
Test Tables CREATE TABLE [dbo].[M2C] ( [ID] [int] NOT NULL, [ID2] [int] NOT NULL, [ID3] [int] NOT NULL, [ID4] [int] NOT NULL, [ID5] [int] NOT NULL, [ID6] [int] NOT NULL, [GroupID] [int] NOT NULL, [CodeID] [int] NOT NULL, [Value] [char] (10) NOT NULL, [randDecimal] [decimal](9, 4) NOT NULL, [randMoney] [money] NOT NULL, [randDate] [datetime] NOT NULL, [seqDate] [datetime] NOT NULL ) ON [PRIMARY] 50,000 rows Index depth 2 99 row per page 506 pages
Test Data int = = = = 10 = BEGIN INSERT M2C (ID, ID2, ID3, ID4, ID5, ID6, GroupID, CodeID, Value, randDecimal, randMoney, randDate, seqDate) , + 1, + 1, CHAR(65+26*rand())+CHAR(65+26*rand())+CHAR(65+26*rand()) +CONVERT(char(6), CONVERT(int,100000*(9.0*rand()+1.0)))+CHAR( *rand()), 10000*rand(), 10000*rand(), DATEADD(hour,120000*rand(),' '), ) END
Test Data Sequences WHILE loop 1,2,3,… FunctionSequence + 1increments every 10 rows distinct values repeating 1,2,3,4,5,6,7,9,10,1,2,3
Links SQL Server Quantitative Performance Analysis Server System Architecture Processor Performance Direct Connect Gigabit Networking Parallel Execution Plans Large Data Operations Transferring Statistics SQL Server Backup Performance with Imceda LiteSpeed