SQL Server High CPU Use / Database Consistency Issue

Posted on July 31, 2009

I run all or most of my development environments in virtual machines. Every so often, I will forget this and suspend my main machine or just unplug the external drive running my VM and completely forget to shutdown my VM beforehand.

For the most part, this doesn’t seem to cause too many issues. SQL on the other hand seems to get hit by this pretty hard. I believe that there there will be open transactions or running jobs on the server that never fully commit when this happens and as such, there is some corruption on the DB.

I know something is going on when I fire-up the VM again and the CPU spikes up to 100% after the SQL agent starts up or I fire up BizTalk server. All of a sudden I will get a ton of messages in the event viewer complaining that there are logical consistency issues with some DB. Generally, it will be the MSDB.

 

Screenshot Stonis Development Server VMware Workstation e.x.p build 169612 2 SQL Server High CPU Use / Database Consistency Issue

Event Viewer Log Entry for SQL Server Logical Consistency Issue

This was a pretty hard one to track down a fix for because the MSDB can’t be easily detached and recreated. I tried quite a few things, but nothing seemed to work properly, until I came across this post at sqlteam.com. Basically, what you want to do is set the database to single user mode, run a CHECKDB to repair and then change it back to multi-user mode. Took about 5 minutes to run for me, but after that, everything was golden.



Screenshot Stonis Development Server VMware Workstation e.x.p build 169612 SQL Server High CPU Use / Database Consistency Issue

SQL Server Command To Repair MSDB

 

  SET NOCOUNT ON;
-- User Preferences

DECLARE @prefix varchar(5);
SET @prefix = 'usp_';

-- End User Preferences

-- Create CRUD procedures
DECLARE DBtables CURSOR FOR SELECT name FROM sysobjects WHERE xtype = 'u' AND name != 'sysdiagrams'
DECLARE @tblName varchar(max);
OPEN DBtables
FETCH NEXT FROM DBtables INTO @tblName 
WHILE @@FETCH_STATUS = 0 
BEGIN
	BEGIN
		-- Holds column attributes for each column of the table
		DECLARE @tblColumns TABLE
		( 
			Id BIGINT IDENTITY(1,1),
			Name VARCHAR(max),
			DataType VARCHAR(max),
			MaxLength BIGINT,
			IsNullable BIT,
			IsAnsiPadded BIT
		)
		-- Holds the primary key attributes
		DECLARE @PKey TABLE
		(
			TABLE_QUALIFIER sysname,
			TABLE_OWNER sysname,
			TABLE_NAME sysname,
			COLUMN_NAME sysname,
			KEY_SEQ smallint,
			PK_NAME sysname
		)			
		-- If the table variables currently have rows, delete them
		IF ((SELECT COUNT(*) FROM @tblColumns) > 0)
		BEGIN
			DELETE FROM @tblColumns;
		END
		IF ((SELECT COUNT(*) FROM @PKey) > 0)
		BEGIN
			DELETE FROM @PKey;
		END
		
		-- Insert the table attributes into table variable
		INSERT INTO @tblColumns (Name, DataType, MaxLength, IsNullable, IsAnsiPadded)
		SELECT c.name , t.name, c.max_length, c.is_nullable, c.is_ansi_padded
		FROM sys.columns AS c 
			 INNER JOIN sys.types AS t ON t.system_type_id = c.system_type_id
		WHERE (c.object_id = object_id(@tblName)) 
			  AND t.system_type_id = t.user_type_id
		ORDER BY c.column_id
		
		-- Insert the primary key attributes into table variables
		INSERT INTO @PKey
		exec sp_pkeys @tblName;
				
		DECLARE @sql NVARCHAR(MAX);
		
		DECLARE @max BIGINT;
		DECLARE @min BIGINT;
		DECLARE @pkeyName varchar(100);				
		DECLARE @ColumnName varchar(max);						
		DECLARE @AnsiPadded BIT;
		SET @AnsiPadded = 0;
		-- Check whether the current table has a primary key
		IF((SELECT COUNT(*) FROM @PKey) = 0)
		BEGIN
			SET @pkeyName = '';
		END
		ELSE
		BEGIN
			DECLARE @schema nvarchar(128);
			
			SELECT @schema = CAST(TABLE_SCHEMA AS NVARCHAR) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_Name = ''+ @tblName + ''
			-- Get the primary key column
			SELECT @pkeyName = 	CAST(COLUMN_NAME AS NVARCHAR) FROM @PKey WHERE TABLE_Name = CAST(@tblName as sysname)	
		END						
		DECLARE @value varchar(100);		
		SELECT @value = CAST(DataType AS NVARCHAR) FROM @tblColumns WHERE Name = @pkeyName		
		-- Get the min Id for table variable
		SELECT @min = MIN(Id) FROM @tblColumns;
		-- Get the max Id for table variable
		SELECT @max = MAX(Id) FROM @tblColumns;		
		IF(LEN(@pkeyName) > 0)
		BEGIN
			PRINT 'Creating Procedure: [' + @schema + '].['+ @prefix + @tblName+'Select]'
			-- Check the the select procedure currently exists, if so delete
			SET @sql = 'IF OBJECT_ID(''[' + @schema + '].[' + @prefix + +@tblName+'Select]'') IS NOT NULL' + CHAR(10)
					 +	'BEGIN' + CHAR(10) + CHAR(9)
					 +		'DROP PROC [' + @schema + '].[' + @prefix + @tblName+'Select]' + CHAR(10)
					 +	'END' + CHAR(10)
					
			exec sp_executesql @sql;
			-- Create select procedure
			SET @sql = 'CREATE PROC [' + @schema + '].[' + @prefix +@tblName+'Select]' + CHAR(10) + CHAR(9)
					+ '@'+@pkeyName + ' ' + @value + CHAR(10) + 'AS ' + CHAR(10) + CHAR(9) + 'SET NOCOUNT ON;' + CHAR(10) + CHAR(9) + 'SET XACT_ABORT ON;'
					+ CHAR(10) + CHAR(10) + CHAR(9) + 'BEGIN TRAN' + CHAR(10) + CHAR(10) + CHAR(9) + 'SELECT '
			 --Loop through table columns
			WHILE(@min <= @max)
			BEGIN
				-- Get column name in table
				SELECT @ColumnName = Name FROM @tblColumns WHERE Id = @min;
				SET @sql += '[' + @ColumnName +']';
				SET @min += 1;
				IF(@min <= @max)
				BEGIN
					-- Column names must be separated by a comma
					SET @sql += ', ';
				END
			END
			-- Set the table name and where conditions
			SET @sql += CHAR(10) + CHAR(9) + '  FROM [' + @schema + '].[' + @tblName + ']' + CHAR(10) + CHAR(9)
			SET @sql += ' WHERE  (['+ @pkeyName + '] = @' + @pkeyName + ' OR @' + @pkeyName + ' IS NULL)'
					 + CHAR(10) + CHAR(10) + CHAR(9) + 'COMMIT;' + CHAR(10) + CHAR(10) --+ 'GO'
			
			exec sp_executesql @sql;
			-- Create Insert procedure
			PRINT 'Creating Procedure: [' + @schema + '].[' + @prefix +@tblName+'Insert]'
			-- Check the the insert prcedure currently exists, if so delete
			SET @sql = 'IF OBJECT_ID(''[' + @schema + '].[' + @prefix +@tblName+'Insert]'') IS NOT NULL' + CHAR(10)
					 +	'BEGIN' + CHAR(10) + CHAR(9)
					 +		'DROP PROC [' + @schema + '].[' + @prefix + @tblName+'Insert]' + CHAR(10)
					 +	'END' + CHAR(10)					 
			
			exec sp_executesql @sql;
					
			SET @sql = 'CREATE PROC [' + @schema + '].[' + @prefix +@tblName+'Insert]' + CHAR(10) + CHAR(9)
			-- Get the min Id for table variable
			SELECT @min = MIN(Id) FROM @tblColumns;
			-- Get the max Id for table variable
			SELECT @max = MAX(Id) FROM @tblColumns;
			-- Loop through table columns
			WHILE(@min <= @max)
			BEGIN						
				-- Get column name in table
				SELECT @ColumnName = Name FROM @tblColumns WHERE Id = @min;
				-- The identity column does not belong in the procedure parameters
				IF(@ColumnName != @pkeyName)
				BEGIN
					-- Gets the value data type				
					SELECT @value = CAST(DataType AS NVARCHAR) FROM @tblColumns WHERE Name = @ColumnName;			
					SET @sql += '@' + @ColumnName + ' ' + @value;
					-- Determines whether the datatype is ansi padded
					SELECT @AnsiPadded = IsAnsiPadded FROM @tblColumns WHERE Id = @min;
					IF(@AnsiPadded = 1)
					BEGIN
						-- Gets the ansi padded length						
						SELECT @value = CASE WHEN LEFT(@value, 1) = 'n' THEN (MaxLength / 2) ELSE MaxLength END FROM @tblColumns WHERE Id = @min;						
						SET @sql = @sql + CASE WHEN @value <> -1 THEN '(' + @value + ')' ELSE '(MAX)' END;

					END
					IF(@min < @max)
					BEGIN
						-- Parameters must be separated by a comma
						SET @sql += ',' + CHAR(10) + CHAR(9)
					END
				END		
				SET @min += 1;
			END
			SET @sql += CHAR(10) + 'AS ' + CHAR(10) + CHAR(9) + 'SET NOCOUNT ON;' + CHAR(10) + CHAR(9) + 'SET XACT_ABORT ON;'
					+ CHAR(10) + CHAR(10) + CHAR(9) + 'BEGIN TRAN' + CHAR(10) + CHAR(10) + CHAR(9)
				    + 'INSERT INTO [' + @schema + '].['+@tblName+'] (' 
		    SELECT @min = MIN(Id) FROM @tblColumns;
			-- Get the max Id for table variable
			SELECT @max = MAX(Id) FROM @tblColumns;
			-- Loop through table columns
			WHILE(@min <= @max)
			BEGIN						
				-- Get column name in table
				SELECT @ColumnName = Name FROM @tblColumns WHERE Id = @min;
				IF(@ColumnName != @pkeyName)
				BEGIN	
					-- Get the column data type			
					SELECT @value = CAST(DataType AS NVARCHAR) FROM @tblColumns WHERE Name = @ColumnName;
					-- timestamps cannot be updated or inserted
					IF(@value <> 'timestamp')
					BEGIN
						-- Set the insert column list
						SET @sql += @ColumnName;
						IF(@min < @max)
						BEGIN
							SET @sql += ', '
						END
					END
				END		
				SET @min += 1;
			END
			SET @sql += ')' + CHAR(10) + CHAR(9) + 'VALUES ('
			SELECT @min = MIN(Id) FROM @tblColumns;
			-- Get the max Id for table variable
			SELECT @max = MAX(Id) FROM @tblColumns;
			-- Loop through table columns
			WHILE(@min <= @max)
			BEGIN						
				-- Get column name in table
				SELECT @ColumnName = Name FROM @tblColumns WHERE Id = @min;
				-- Set the insert values
				IF(@ColumnName != @pkeyName)
				BEGIN				
					SELECT @value = CAST(DataType AS NVARCHAR) FROM @tblColumns WHERE Name = @ColumnName;
					IF(@value <> 'timestamp')
						BEGIN
						SET @sql += '@' + @ColumnName 					
						IF(@min < @max)
						BEGIN
							SET @sql += ', ' 
						END
					END
				END		
				SET @min += 1;
			END
			-- Return the current identity
			SET @sql += ')' + CHAR(10) + CHAR(10) + + CHAR(9) + 'SELECT [' + @pkeyName +'] ' + CHAR(10) + CHAR(9) 
				     + 'FROM [' + @schema + '].['+ @tblName + ']' + CHAR(10) + CHAR(9) + 'WHERE [' + @pkeyName + '] = SCOPE_IDENTITY()'
					 + CHAR(10) + CHAR(10) + CHAR(9) + 'COMMIT;' + CHAR(10) --+ 'GO'			
			exec sp_executesql @sql;
			
			
			-- Create Update procedure
			PRINT 'Creating Procedure: [' + @schema + '].[' + @prefix +@tblName+'Update]'
			-- Drop the procedure if it currently exists
			SET @sql = 'IF OBJECT_ID(''[' + @schema + '].[' + @prefix +@tblName+'Update]'') IS NOT NULL' + CHAR(10)
					 + 'BEGIN' + CHAR(10) + CHAR(9)
					 +	'DROP PROC [' + @schema + '].[' + @prefix + @tblName+'Update]' + CHAR(10)
					 + 'END' + CHAR(10)
					 --+ 'GO'
			exec sp_executesql @sql;
			-- Create update procedure
			SET @sql = 'CREATE PROC [' + @schema + '].[' + @prefix +@tblName+'Update]' + CHAR(10) + CHAR(9)		
		    SELECT @min = MIN(Id) FROM @tblColumns;
			-- Get the max Id for table variable
			SELECT @max = MAX(Id) FROM @tblColumns;
			-- Set the parameter list
			WHILE(@min <= @max)
			BEGIN
				-- Get column name in table
				SELECT @ColumnName = Name FROM @tblColumns WHERE Id = @min;						
				SELECT @value = CAST(DataType AS NVARCHAR) FROM @tblColumns WHERE Name = @ColumnName;
				-- Cannot modify timestamp fields so we skip them
				IF(@value <> 'timestamp')
				BEGIN
					-- Set parameter list and datatypes
					SET @sql += '@' + @ColumnName + ' ' + @value;
					SELECT @AnsiPadded = IsAnsiPadded FROM @tblColumns WHERE Id = @min;
					IF(@AnsiPadded = 1)
					BEGIN
						SELECT @value = CASE WHEN LEFT(@value, 1) = 'n' THEN (MaxLength / 2) ELSE MaxLength END FROM @tblColumns WHERE Id = @min;
						SET @sql += CASE WHEN @value <> -1 THEN '(' + @value + ')' ELSE '(MAX)' END;
					END
					IF(@min < @max)
					BEGIN
						SET @sql += ',' + CHAR(10) + CHAR(9)
					END
				END
				SET @min += 1;
			END
			SET @sql += CHAR(10) + 'AS ' + CHAR(10) + CHAR(9) + 'SET NOCOUNT ON;' + CHAR(10) + CHAR(9) + 'SET XACT_ABORT ON;'
					+ CHAR(10) + CHAR(10) + CHAR(9) + 'BEGIN TRAN' + CHAR(10) + CHAR(10) + CHAR(9)
				    + 'UPDATE [' + @schema + '].['+@tblName+']' + CHAR(10) + CHAR(9) + '   SET' 
			SELECT @min = MIN(Id) FROM @tblColumns;
			-- Get the max Id for table variable
			SELECT @max = MAX(Id) FROM @tblColumns;
			-- Set update conditions
			WHILE(@min <= @max)
			BEGIN						
				-- Get column name in table
				SELECT @ColumnName = Name FROM @tblColumns WHERE Id = @min;
				IF(@ColumnName != @pkeyName)
				BEGIN				
					SELECT @value = CAST(DataType AS NVARCHAR) FROM @tblColumns WHERE Name = @ColumnName;
					IF(@value <> 'timestamp')
					BEGIN
						SET @sql += ' ['+@ColumnName+'] = @' + @ColumnName 					
						IF(@min < @max)
						BEGIN
							SET @sql += ',' + CHAR(10) + CHAR(9) + CHAR(9) + '  '
						END
					END
				END
				SET @min += 1;
			END
			-- Set where conditions
			SET @sql += CHAR(10) + CHAR(9) + ' WHERE [' + @pkeyName + '] = @' + @pkeyName
				      + CHAR(10) + +CHAR(10) + CHAR(9) + ' SELECT @' + @pkeyName + ' AS [' + @pkeyName + ']'
			          + CHAR(10) + CHAR(10) + CHAR(9) + 'COMMIT;' + CHAR(10)
			exec sp_executesql @sql;
			
			PRINT 'Creating Procedure: [' + @schema + '].[' + @prefix  + @tblName + 'Delete]'
			-- Drop Delete procedure if it currently exists
			SET @sql = 'IF OBJECT_ID(''[' + @schema + '].[' + @prefix +@tblName+'Delete]'') IS NOT NULL' + CHAR(10)
					 + 'BEGIN' + CHAR(10) + CHAR(9)
					 + 'DROP PROC [' + @schema + '].[' + @prefix + @tblName+'Delete]' + CHAR(10)
					 + 'END' + CHAR(10)
		exec sp_executesql @sql;
		-- Get delete parameter		
		SELECT @value = CAST(DataType AS NVARCHAR) FROM @tblColumns WHERE Name = @pkeyName		
		-- Set procedure syntax
		SET @sql = 'CREATE PROC [' + @schema + '].[' + @prefix +@tblName+'Delete]' + CHAR(10) + CHAR(9)
				 + '@'+@pkeyName + ' ' + @value + CHAR(10) + 'AS ' + CHAR(10) + CHAR(9) + 'SET NOCOUNT ON;' + CHAR(10) + CHAR(9) + 'SET XACT_ABORT ON;'
				 + CHAR(10) + CHAR(10) + CHAR(9) + 'BEGIN TRAN' + CHAR(10) + CHAR(10) + CHAR(9) + 'DELETE FROM [' + @schema + '].[' 
				 + @tblName + ']' + CHAR(10) + CHAR(9) + 'WHERE [' + @pkeyName + '] = @' + @pkeyName + CHAR(10) + CHAR(10) + CHAR(9) + 'COMMIT;' 
				 + CHAR(10) + CHAR(10) 
		exec sp_executesql @sql;
		END
		ELSE
		BEGIN
			PRINT CHAR(10) + 'Table ' + @tblName + ' does not have a primary key column and therefore CRUD statements could not be generated' + CHAR(10) 
		END		
	END
-- Get the next table to alter
FETCH NEXT FROM DBtables INTO @tblName 
END
-- Close cursor
CLOSE DBtables
DEALLOCATE DBtables
PRINT 'FINISHED'	

« Back to Blog main page