SQL Server concurrent processing, update solution discussion, SQL Server

Source: Internet
Author: User

SQL Server concurrent processing, update solution discussion, SQL Server

Preface

In this section, we will talk about the most common situation of concurrency, that is, update. If no row record exists in concurrency, insert it. At this time, it is very easy to insert duplicate keys, this article describes the seven solutions for updating Row Records in concurrency and the most suitable solutions for comprehensive analysis.

Seven solutions are updated if they exist

First, create a test table.

IF OBJECT_ID('Test') IS NOT NULL DROP TABLE TestCREATE TABLE Test( Id int, Name nchar(100), [Counter] int,primary key (Id), unique (Name));GO

Solution 1(Start transaction)

We use SQLQueryStress to test the concurrency of the stored procedure. Let's look at the first scenario.

IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100))  BEGIN TRANSACTION IF EXISTS ( SELECT 1    FROM Test    WHERE Id = @Id )  UPDATE Test  SET  [Counter] = [Counter] + 1  WHERE Id = @Id; ELSE  INSERT Test    ( Id, Name, [Counter] )  VALUES ( @Id, @Name, 1 ); COMMITGO

When 100 and 200 threads are enabled at the same time, the chance of inserting duplicate keys is relatively small.

Solution 2(Reduce the isolation level to the lowest isolation level UNCOMMITED)

IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100))  SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED BEGIN TRANSACTION IF EXISTS ( SELECT 1    FROM Test    WHERE Id = @Id )  UPDATE Test  SET  [Counter] = [Counter] + 1  WHERE Id = @Id; ELSE  INSERT Test    ( Id, Name, [Counter] )  VALUES ( @Id, @name, 1 ); COMMITGO

At this time, the problem remains the same as the solution (if the lower level is the lowest isolation level, if the row record is empty, if the previous transaction is not committed, the current transaction can also read the row record as null, if the current transaction is inserted and committed, and the previous transaction is committed again, the insertion of duplicate keys will occur)

Solution 3(Improve the isolation level to the highest level SERIALIZABLE)

IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100))  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE BEGIN TRANSACTION IF EXISTS ( SELECT 1    FROM dbo.Test    WHERE Id = @Id )  UPDATE dbo.Test  SET  [Counter] = [Counter] + 1  WHERE Id = @Id; ELSE  INSERT dbo.Test    ( Id, Name, [Counter] )  VALUES ( @Id, @Name, 1 ); COMMITGO

In this case, it is worse, and the deadlock is caused directly.

Increasing the isolation level to the highest isolation level will solve the problem of inserting duplicate keys, but it is not submitted for updating to obtain the exclusive lock, at this time, another process queries and obtains the shared lock, which will cause mutual congestion between processes and lead to deadlocks. Therefore, the highest isolation level can sometimes solve the concurrency problem but also lead to deadlocks.

Solution 4(Improve isolation level + Good lock)

In this case, we will add an update Lock Based on the highest isolation level, as shown below:

IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100))  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE BEGIN TRANSACTION IF EXISTS ( SELECT 1    FROM dbo.Test WITH(UPDLOCK)    WHERE Id = @Id )  UPDATE dbo.Test  SET  [Counter] = [Counter] + 1  WHERE Id = @Id; ELSE  INSERT dbo.Test    ( Id, Name, [Counter] )  VALUES ( @Id, @Name, 1 ); COMMITGO

No exception is found during multiple operations. Update locks instead of shared locks are used to query data. In this way, data can be read but other transactions are not blocked, second, it also ensures that the data has not been changed since the last read, which solves the deadlock problem. It seems that this solution is feasible. If it is high concurrency, I wonder if it is feasible.

Solution 5(Upgrade the isolation level to line version control SNAPSHOT)

ALTER DATABASE UpsertTestDatabaseSET ALLOW_SNAPSHOT_ISOLATION ON ALTER DATABASE UpsertTestDatabaseSET READ_COMMITTED_SNAPSHOT ONGO IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100))  BEGIN TRANSACTION IF EXISTS ( SELECT 1    FROM dbo.Test    WHERE Id = @Id )  UPDATE dbo.Test  SET  [Counter] = [Counter] + 1  WHERE Id = @Id; ELSE  INSERT dbo.Test    ( Id, Name, [Counter] )  VALUES ( @Id, @Name, 1 ); COMMITGO

The preceding solution also causes the insertion of duplicate keys.

Solution 6(Improve isolation level + Table variable)

IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) DECLARE @updated TABLE ( i INT );  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION UPDATE Test SET  [Counter] = [Counter] + 1 OUTPUT DELETED.Id   INTO @updated WHERE Id = @Id;  IF NOT EXISTS ( SELECT i     FROM @updated )  INSERT INTO Test    ( Id, Name, counter )  VALUES ( @Id, @Name, 1 ); COMMITGO

It is also a zero error after multiple authentication, and it seems feasible to implement it through the form of table variables.

Solution 7(Enhanced isolation + Merge)

The Merge key is used to implement the existence, that is, update, or insert. At the same time, we should pay attention to setting the isolation level to SERIALIZABLE. Otherwise, the duplicate key insertion problem may occur. The Code is as follows:

IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) SET TRAN ISOLATION LEVEL SERIALIZABLE  BEGIN TRANSACTION MERGE Test AS [target] USING  ( SELECT @Id AS Id  ) AS source ON source.Id = [target].Id WHEN MATCHED THEN  UPDATE SET    [Counter] = [target].[Counter] + 1 WHEN NOT MATCHED THEN  INSERT ( Id, Name, [Counter] )  VALUES ( @Id, @Name, 1 ); COMMITGO

No exception information is displayed for multiple authentication tasks, including 100 concurrent threads and 200 concurrent threads.

Summary

This section describes in detail how to handle the problem of existence, update, or insert in concurrency. Currently, the above three solutions are feasible.

Solution 1(Maximum isolation level + update lock)

IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100))  BEGIN TRANSACTION;  UPDATE dbo.Test WITH ( UPDLOCK, HOLDLOCK ) SET  [Counter] = [Counter] + 1 WHERE Id = @Id;  IF ( @@ROWCOUNT = 0 )  BEGIN   INSERT dbo.Test     ( Id, Name, [Counter] )   VALUES ( @Id, @Name, 1 );  END  COMMITGO

For the time being, I can only think of these three solutions. I personally recommend solution 1 and solution 3. What are your comments? If your comments are feasible, I will add them later.

Solution 2(Maximum isolation level + Table variable)

IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) DECLARE @updated TABLE ( i INT );  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION UPDATE Test SET  [Counter] = [Counter] + 1 OUTPUT DELETED.id   INTO @updated WHERE id = @id;  IF NOT EXISTS ( SELECT i     FROM @updated )  INSERT INTO Test    ( Id, Name, counter )  VALUES ( @Id, @Name, 1 ); COMMITGO

Solution 3(Maximum isolation level + Merge)

IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro;GO CREATE PROCEDURE TestPro ( @Id INT )AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) SET TRAN ISOLATION LEVEL SERIALIZABLE  BEGIN TRANSACTION MERGE Test AS [target] USING  ( SELECT @Id AS Id  ) AS source ON source.Id = [target].Id WHEN MATCHED THEN  UPDATE SET    [Counter] = [target].[Counter] + 1 WHEN NOT MATCHED THEN  INSERT ( Id, Name, [Counter] )  VALUES ( @Id, @Name, 1 ); COMMITGO

For the time being, I can only think of these three solutions. I personally recommend solution 1 and solution 3. What are your comments? If your comments are feasible, I will add them later.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.